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>
64 lines
2.2 KiB
TypeScript
64 lines
2.2 KiB
TypeScript
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");
|
|
});
|
|
});
|