test: SSH-Testziele und Mock-OIDC fuer Integrationstests (#011)

Bootstrap, Host-Key-Pruefung und spaeteres Ausrollen brauchen echte
sshd-Gegenstellen. Zwei Debian-13-Container (ziel-a, ziel-b) in einem
gemeinsamen Netz bilden frische Hosts vor dem Bootstrap nach: root- und
Passwort-Login sind absichtlich offen, Host-Keys entstehen erst beim
Start, und `hostkeys-tauschen` erlaubt den Test "geaenderter Host-Key
wird abgelehnt" ohne Neuerstellen des Containers.

Die Testhilfen lesen dieselben Adress-Variablen wie die Compose-Datei
und lassen Tests mit Hinweis auf den Startbefehl fehlschlagen, statt
sie still zu ueberspringen - ein uebersprungener Integrationstest sieht
sonst wie ein bestandener aus.

Der Mock-OIDC-Anbieter ersetzt authentik in der Entwicklung (PLAN.md,
Entscheidung 7). Bei interaktivem Login uebernimmt er nur Claims aus dem
Formular, daher belegt eine eigene Login-Seite email, name und groups
vor und laesst die Gruppen frei waehlen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-12 09:13:56 +02:00
co-authored by Claude Opus 5.5
parent 224386106b
commit e89112a761
11 changed files with 614 additions and 6 deletions
+97
View File
@@ -0,0 +1,97 @@
# Nur fuer Tests und Entwicklung (#011): zwei SSH-Testziele und ein Mock-OIDC-Anbieter.
# Diese Datei niemals im Betrieb verwenden – die Ziele erlauben root-Login per Passwort.
#
# pnpm test:ziele:up # bauen, starten, auf healthy warten
# pnpm test:integration
# pnpm test:ziele:down
#
# Alle Ports nur auf 127.0.0.1. Die Adressen kommen aus denselben Variablen, die auch
# die Testhilfen lesen (tests/integration/hilfen/ziele.ts), damit beide nie auseinanderlaufen.
name: sshkv-test
x-ssh-ziel: &ssh-ziel
# Kein gemeinsames `image:` – sonst bauen beide Dienste parallel unter demselben Tag
# und BuildKit bricht mit "already exists" ab. Der Build-Cache wird trotzdem geteilt.
build:
context: tests/ssh-ziel
restart: unless-stopped
networks: [ziel-netz]
environment:
SSHKV_ROOT_PASSWORT: ${SSHKV_TEST_ROOT_PASSWORT:-sshkv-test}
SSHKV_TEST_PASSWORT: ${SSHKV_TEST_TEST_PASSWORT:-sshkv-test}
services:
ziel-a:
<<: *ssh-ziel
hostname: ziel-a
ports:
- "${SSHKV_TEST_ZIEL_A:-127.0.0.1:2221}:22"
ziel-b:
<<: *ssh-ziel
hostname: ziel-b
ports:
- "${SSHKV_TEST_ZIEL_B:-127.0.0.1:2222}:22"
# Ersatz fuer authentik in der Entwicklung (PLAN.md, Entscheidung 7).
# Issuer: http://127.0.0.1:8090/sshkv – der Mock leitet den Issuer aus dem Host-Header
# ab, "localhost:8090" ergaebe also einen anderen Issuer. Client-ID und -Secret prueft
# er nicht; jeder Wert wird angenommen.
mock-oidc:
image: ghcr.io/navikt/mock-oauth2-server:6.0.5
restart: unless-stopped
ports:
- "${SSHKV_TEST_OIDC:-127.0.0.1:8090}:8080"
volumes:
- ./tests/mock-oidc/login.html:/mock/login.html:ro
environment:
SERVER_PORT: "8080"
# Bei interactiveLogin uebernimmt der Mock nur die Claims aus dem Login-Formular.
# Die eigene Login-Seite belegt sie deshalb vor (email, name, groups …) und
# erlaubt freie Gruppenwahl. tokenCallbacks gelten fuer nicht-interaktive Grants
# (z. B. client_credentials in Tests).
JSON_CONFIG: >-
{
"interactiveLogin": true,
"httpServer": "NettyWrapper",
"loginPagePath": "/mock/login.html",
"tokenCallbacks": [
{
"issuerId": "sshkv",
"tokenExpiry": 3600,
"requestMappings": [
{
"requestParam": "scope",
"match": "*",
"claims": {
"sub": "admin",
"email": "admin@sshkv.test",
"email_verified": true,
"name": "Admin (Mock)",
"preferred_username": "admin",
"groups": ["sshkv-admins"]
}
}
]
}
]
}
healthcheck:
test:
[
"CMD",
"wget",
"-q",
"-O",
"/dev/null",
"http://127.0.0.1:8080/sshkv/.well-known/openid-configuration",
]
interval: 3s
timeout: 3s
start_period: 10s
retries: 20
networks:
# Gemeinsames Netz: ziel-a erreicht ziel-b per Dienstname (Host-zu-Host-Tests).
ziel-netz: