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:
@@ -0,0 +1,63 @@
|
||||
import { beforeAll, describe, expect, it } from "vitest";
|
||||
import {
|
||||
befehlAlsRoot,
|
||||
hostKeyFingerprint,
|
||||
hostKeysTauschen,
|
||||
testPasswort,
|
||||
zielA,
|
||||
zielB,
|
||||
zielBereit,
|
||||
} from "./hilfen/ziele";
|
||||
|
||||
// Rauchtest fuer die Testumgebung selbst (#011): Spaetere Integrationstests (Bootstrap,
|
||||
// Host-Key-Pruefung, Ausrollen) verlassen sich darauf, dass diese Annahmen gelten.
|
||||
describe("SSH-Testziele", () => {
|
||||
beforeAll(async () => {
|
||||
await Promise.all([zielBereit(zielA), zielBereit(zielB)]);
|
||||
});
|
||||
|
||||
it.each([zielA, zielB])("$dienst: Passwort-Login als root, whoami = root", async (ziel) => {
|
||||
const ergebnis = await befehlAlsRoot(ziel, "whoami");
|
||||
expect(ergebnis).toEqual({ stdout: "root\n", stderr: "", code: 0 });
|
||||
});
|
||||
|
||||
it("hat sudo installiert", async () => {
|
||||
const ergebnis = await befehlAlsRoot(zielA, "sudo -V");
|
||||
expect(ergebnis.code).toBe(0);
|
||||
expect(ergebnis.stdout).toMatch(/^Sudo version/m);
|
||||
});
|
||||
|
||||
it("erlaubt den Benutzer test per Passwort, mit sudo-Gruppe", async () => {
|
||||
const ergebnis = await befehlAlsRoot(zielB, "id -un; id -Gn", {
|
||||
benutzer: "test",
|
||||
passwort: testPasswort(),
|
||||
});
|
||||
expect(ergebnis.code).toBe(0);
|
||||
const [name, gruppen] = ergebnis.stdout.trim().split("\n");
|
||||
expect(name).toBe("test");
|
||||
expect(gruppen?.split(" ")).toContain("sudo");
|
||||
});
|
||||
|
||||
it("lehnt ein falsches Passwort ab", async () => {
|
||||
await expect(befehlAlsRoot(zielA, "true", { passwort: "falsch" })).rejects.toThrow(/authentication/i);
|
||||
});
|
||||
|
||||
it("Ziel A erreicht Ziel B über das Docker-Netz", async () => {
|
||||
const ergebnis = await befehlAlsRoot(zielA, "ssh-keyscan -T 5 -t ed25519 ziel-b");
|
||||
expect(ergebnis.code).toBe(0);
|
||||
expect(ergebnis.stdout).toMatch(/^ziel-b ssh-ed25519 AAAA\S+$/m);
|
||||
});
|
||||
|
||||
it("Host-Key-Tausch ändert den Fingerprint", async () => {
|
||||
const vorher = await hostKeyFingerprint(zielB);
|
||||
expect(vorher).toMatch(/^SHA256:[A-Za-z0-9+/]{43}$/);
|
||||
|
||||
await hostKeysTauschen(zielB);
|
||||
|
||||
const nachher = await hostKeyFingerprint(zielB);
|
||||
expect(nachher).toMatch(/^SHA256:[A-Za-z0-9+/]{43}$/);
|
||||
expect(nachher).not.toBe(vorher);
|
||||
// Der Container bleibt nach dem Tausch benutzbar.
|
||||
expect((await befehlAlsRoot(zielB, "whoami")).stdout).toBe("root\n");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user