Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
f164887
test(e2e): add Kerberos/SPNEGO end-to-end test harness
Jun 2, 2026
ee5fdeb
fix(e2e): use --legacy-peer-deps for node-libcurl build
Jun 2, 2026
229c7e8
fix(kerberos-e2e): rewrite repeater Dockerfile for Linux vcpkg build
aborovsky Jun 2, 2026
1a72685
fix(kerberos-e2e): extract node inline scripts to helper files
aborovsky Jun 2, 2026
5384826
fix(kerberos-e2e): remove sspi feature from curl on Linux (Windows-only)
aborovsky Jun 2, 2026
3084775
fix(kerberos-e2e): drop ldap and gsasl curl features on Linux
aborovsky Jun 2, 2026
611f7c8
fix(kerberos-e2e): add autoconf/automake/libtool for vcpkg ports
aborovsky Jun 2, 2026
0b0d164
fix(kerberos-e2e): drop http3 curl feature — ngtcp2 fails to build in…
aborovsky Jun 2, 2026
e14084e
fix(kerberos-e2e): add bison and flex — required by krb5 vcpkg port
aborovsky Jun 2, 2026
db992a9
fix(kerberos-e2e): compile TS dist and fix check-gssapi require path
aborovsky Jun 2, 2026
5f49fd1
fix(kerberos-e2e): use pkg-config --static to link all GSSAPI/krb5 tr…
aborovsky Jun 2, 2026
7c1710e
fix(kerberos-e2e): detect mit-krb5/SPNEGO in check-gssapi, not litera…
aborovsky Jun 2, 2026
685a522
fix(kerberos-e2e): handle Curl.getVersion() returning a plain string
aborovsky Jun 2, 2026
afbfbac
fix(kerberos-e2e): install node-libcurl tarball --ignore-scripts and …
aborovsky Jun 2, 2026
7a8e0da
fix(kerberos-e2e): remove unsupported GssapiAllowedMech directive (Ub…
aborovsky Jun 2, 2026
c7de2de
fix(kerberos-e2e): remove static IP from repeater so docker compose r…
aborovsky Jun 2, 2026
fe66c8b
fix(kerberos-e2e): handle plain string from Curl.getVersion() in test…
aborovsky Jun 2, 2026
331bc26
fix(kerberos-e2e): set --workdir /app for steps 5 & 6 to reach bright…
aborovsky Jun 2, 2026
154ceb2
fix(kerberos-e2e): read dist/index.js (webpack bundle) not per-file d…
aborovsky Jun 2, 2026
4f641b0
feat(kerberos-e2e): add step 6 — full SPNEGO flow through HttpRequest…
aborovsky Jun 3, 2026
0e63f6c
fix(ci): exclude kerberos-e2e from root tsconfig and jest scope
aborovsky Jun 3, 2026
df3099a
fix(ci): exclude kerberos-e2e from tsconfig.build.json (used by webpack)
aborovsky Jun 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
354 changes: 354 additions & 0 deletions kerberos-e2e/README.md

Large diffs are not rendered by default.

85 changes: 85 additions & 0 deletions kerberos-e2e/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
version: "3.9"

# ─────────────────────────────────────────────────────────────────────────────
# Kerberos/SPNEGO E2E test stack for bright-cli repeater
#
# Services
# kdc — MIT Kerberos KDC: creates realm, principals, exports keytab
# httpd — Apache httpd with mod_auth_gssapi: SPNEGO-protected endpoint
# repeater — bright-cli built from feat/kerberos-auth + node-libcurl gssapi
#
# Usage
# BRIGHT_TOKEN=<token> REPEATER_ID=<id> docker compose up --build
# See README.md for the full step-by-step guide.
# ─────────────────────────────────────────────────────────────────────────────

networks:
kerbnet:
driver: bridge
ipam:
config:
- subnet: 172.30.0.0/24

services:
# ── MIT Kerberos KDC ───────────────────────────────────────────────────────
kdc:
build: ./kdc
hostname: kdc.EXAMPLE.COM
domainname: EXAMPLE.COM
networks:
kerbnet:
ipv4_address: 172.30.0.2
volumes:
- keytab_vol:/keytabs # KDC writes HTTP.keytab here; httpd reads it
- /dev/urandom:/dev/random # prevent entropy starvation during realm init
healthcheck:
test: ["CMD", "sh", "-c", "nc -z 127.0.0.1 88"]
interval: 3s
timeout: 5s
retries: 20

# ── Apache httpd with mod_auth_gssapi ──────────────────────────────────────
httpd:
build: ./httpd
hostname: www.EXAMPLE.COM
domainname: EXAMPLE.COM
networks:
kerbnet:
ipv4_address: 172.30.0.3
volumes:
- keytab_vol:/keytabs:ro # receives the keytab produced by kdc
depends_on:
kdc:
condition: service_healthy
ports:
- "8880:80" # expose on host for direct curl smoke-tests
healthcheck:
test: ["CMD", "curl", "-sf", "http://localhost/health"]
interval: 5s
timeout: 5s
retries: 15

# ── bright-cli repeater ────────────────────────────────────────────────────
# Built from feat/kerberos-auth with @brightsec/node-libcurl overridden to
# the unmerged feat/gssapi-support branch that adds GSSAPI to the curl build.
repeater:
build: ./repeater
hostname: repeater.EXAMPLE.COM
domainname: EXAMPLE.COM
networks:
kerbnet:
depends_on:
kdc:
condition: service_healthy
httpd:
condition: service_healthy
environment:
- REPEATER_TOKEN=${BRIGHT_TOKEN}
- REPEATER_ID=${REPEATER_ID}
- REPEATER_KERBEROS=true
- REPEATER_KERBEROS_CREDENTIALS=scanner@EXAMPLE.COM:ScannerPass1
- REPEATER_KERBEROS_DOMAINS=www.EXAMPLE.COM
restart: on-failure

volumes:
keytab_vol:
35 changes: 35 additions & 0 deletions kerberos-e2e/httpd/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
FROM ubuntu:22.04

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install -y --no-install-recommends \
apache2 \
libapache2-mod-auth-gssapi \
krb5-user \
curl \
&& a2enmod auth_gssapi headers \
&& rm -rf /var/lib/apt/lists/*

COPY krb5.conf /etc/krb5.conf
COPY spnego.conf /etc/apache2/sites-enabled/000-default.conf

# Unauthenticated health-check page used by Docker healthcheck
RUN mkdir -p /var/www/html && echo "OK" > /var/www/html/health

# Protected content served after successful Kerberos authentication
RUN echo "Hello, authenticated user!" > /var/www/html/protected/index.html 2>/dev/null || \
(mkdir -p /var/www/html/protected && echo "Hello, authenticated user!" > /var/www/html/protected/index.html)

EXPOSE 80

# Wait for the keytab to be written by the KDC container, then start Apache.
# The KDC writes to the shared volume; we poll until the file appears.
CMD ["bash", "-c", \
"echo '[httpd] Waiting for keytab from KDC...'; \
until [ -f /keytabs/HTTP.keytab ]; do sleep 2; done; \
echo '[httpd] Keytab found — installing'; \
cp /keytabs/HTTP.keytab /etc/krb5.keytab && \
chmod 640 /etc/krb5.keytab && \
chown root:www-data /etc/krb5.keytab && \
echo '[httpd] Starting Apache'; \
apachectl -D FOREGROUND"]
17 changes: 17 additions & 0 deletions kerberos-e2e/httpd/krb5.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[libdefaults]
default_realm = EXAMPLE.COM
dns_lookup_realm = false
dns_lookup_kdc = false
ticket_lifetime = 24h
renew_lifetime = 7d
forwardable = true

[realms]
EXAMPLE.COM = {
kdc = kdc.EXAMPLE.COM:88
admin_server = kdc.EXAMPLE.COM:749
}

[domain_realm]
.EXAMPLE.COM = EXAMPLE.COM
EXAMPLE.COM = EXAMPLE.COM
30 changes: 30 additions & 0 deletions kerberos-e2e/httpd/spnego.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
ServerName www.EXAMPLE.COM

<VirtualHost *:80>
ServerName www.EXAMPLE.COM
DocumentRoot /var/www/html

# ── Unauthenticated health-check (used by Docker healthcheck) ──────────
<Location /health>
Require all granted
</Location>

# ── SPNEGO-protected resource ──────────────────────────────────────────
# Requires a valid Kerberos ticket for HTTP/www.EXAMPLE.COM@EXAMPLE.COM
<Location /protected>
AuthType GSSAPI
AuthName "Kerberos Login"
GssapiCredStore keytab:/etc/krb5.keytab
GssapiSSLonly Off
Require valid-user

# Echo the authenticated principal back in a response header so the
# test script can verify which identity was used.
Header always set X-Authenticated-User "%{REMOTE_USER}s"
</Location>

# ── Everything else is open (for test convenience) ─────────────────────
<Location />
Require all granted
</Location>
</VirtualHost>
20 changes: 20 additions & 0 deletions kerberos-e2e/kdc/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
FROM ubuntu:22.04

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install -y --no-install-recommends \
krb5-kdc \
krb5-admin-server \
krb5-user \
netcat-openbsd \
&& rm -rf /var/lib/apt/lists/*

COPY krb5.conf /etc/krb5.conf
COPY kdc.conf /etc/krb5kdc/kdc.conf
COPY kadm5.acl /etc/krb5kdc/kadm5.acl
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

EXPOSE 88 749

ENTRYPOINT ["/entrypoint.sh"]
44 changes: 44 additions & 0 deletions kerberos-e2e/kdc/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/bin/bash
# KDC container entrypoint
# - Initialises the Kerberos realm database on first boot
# - Creates the principals used by the E2E tests
# - Exports the HTTP service keytab to the shared volume
set -euo pipefail

REALM=EXAMPLE.COM
MASTER_PW=MasterKey1
ADMIN_PW=AdminPass1
SCANNER_PW=ScannerPass1
HTTPD_HOST=www.EXAMPLE.COM
KEYTAB=/keytabs/HTTP.keytab

echo "==> [KDC] Initialising Kerberos realm: ${REALM}"
# krb5_newrealm reads the master key from stdin (twice: set + confirm)
printf '%s\n%s\n' "${MASTER_PW}" "${MASTER_PW}" | krb5_newrealm

echo "==> [KDC] Starting krb5kdc"
krb5kdc

echo "==> [KDC] Starting kadmind"
kadmind

# Give kadmind time to bind its socket before we call kadmin.local
sleep 2

echo "==> [KDC] Creating principals"
kadmin.local -q "addprinc -pw ${ADMIN_PW} kadmin/admin@${REALM}"
kadmin.local -q "addprinc -pw ${SCANNER_PW} scanner@${REALM}"
kadmin.local -q "addprinc -randkey HTTP/${HTTPD_HOST}@${REALM}"

echo "==> [KDC] Exporting service keytab to ${KEYTAB}"
mkdir -p "$(dirname "${KEYTAB}")"
kadmin.local -q "ktadd -k ${KEYTAB} HTTP/${HTTPD_HOST}@${REALM}"
# World-readable so that the httpd container (different uid) can copy it
chmod 644 "${KEYTAB}"

echo "==> [KDC] Realm ready. Principals:"
kadmin.local -q "listprincs"

echo "==> [KDC] Tailing KDC log (Ctrl-C to stop stack)"
# Keep the container running; healthcheck polls port 88
tail -f /var/log/krb5kdc.log 2>/dev/null || tail -f /dev/null
1 change: 1 addition & 0 deletions kerberos-e2e/kdc/kadm5.acl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
kadmin/admin@EXAMPLE.COM *
16 changes: 16 additions & 0 deletions kerberos-e2e/kdc/kdc.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[kdcdefaults]
kdc_ports = 88,750

[realms]
EXAMPLE.COM = {
database_module = DB2
acl_file = /etc/krb5kdc/kadm5.acl
max_renewable_life = 7d
supported_enctypes = aes256-cts-hmac-sha1-96:normal aes128-cts-hmac-sha1-96:normal
default_principal_flags = +preauth
}

[dbmodules]
DB2 = {
db_library = db2
}
17 changes: 17 additions & 0 deletions kerberos-e2e/kdc/krb5.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[libdefaults]
default_realm = EXAMPLE.COM
dns_lookup_realm = false
dns_lookup_kdc = false
ticket_lifetime = 24h
renew_lifetime = 7d
forwardable = true

[realms]
EXAMPLE.COM = {
kdc = kdc.EXAMPLE.COM:88
admin_server = kdc.EXAMPLE.COM:749
}

[domain_realm]
.EXAMPLE.COM = EXAMPLE.COM
EXAMPLE.COM = EXAMPLE.COM
Loading
Loading