ssh2.utils.generateKeyPairSync liefert in etwa 4 von 1000 Fällen einen privaten Schlüssel, an dem der eigene Parser scheitert. Ein so erzeugter Server-Schlüssel wäre dauerhaft unbrauchbar gewesen; in den Tests war es die Ursache des gelegentlich roten SSH-Tests. Refs #014 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
104 lines
4.0 KiB
TypeScript
104 lines
4.0 KiB
TypeScript
import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||
import { POST as hostSchluesselPost } from "@/app/api/v1/server/[name]/ssh/hostschluessel/route";
|
||
import { POST as testPost } from "@/app/api/v1/server/[name]/test/route";
|
||
import { verschluesseln } from "@/lib/geheimnisse";
|
||
import { MOCK_TOKEN_GEHEIMNIS, MOCK_TOKEN_ID, ProxmoxMock } from "./proxmox-mock/mock";
|
||
import { szenarioEinrichten } from "./proxmox-mock/szenario";
|
||
import { SshMock, testPaar } from "./ssh-mock";
|
||
import { datenbankLeeren, datenbankVerfuegbar, testPrisma } from "./helpers/db";
|
||
import { angemeldet, anfrage, kontext } from "./helpers/anfragen";
|
||
|
||
const mock = new ProxmoxMock();
|
||
const paar = testPaar();
|
||
let ssh: SshMock;
|
||
|
||
describe.runIf(datenbankVerfuegbar)("Verbindungstest über die API", () => {
|
||
beforeAll(async () => {
|
||
await mock.starten();
|
||
szenarioEinrichten(mock, { version: "8.3.5", importStorage: true });
|
||
ssh = await new SshMock(paar.public).starten();
|
||
});
|
||
afterAll(async () => {
|
||
await mock.stoppen();
|
||
await ssh.stoppen();
|
||
});
|
||
|
||
let admin: string;
|
||
let operator: string;
|
||
beforeEach(async () => {
|
||
await datenbankLeeren();
|
||
admin = (await angemeldet("ADMIN")).cookie;
|
||
operator = (await angemeldet("OPERATOR")).cookie;
|
||
await testPrisma.server.create({
|
||
data: {
|
||
name: "rz1",
|
||
titel: "RZ 1",
|
||
url: mock.url,
|
||
tokenId: MOCK_TOKEN_ID,
|
||
tokenGeheimnis: verschluesseln(MOCK_TOKEN_GEHEIMNIS),
|
||
fingerabdruck: mock.fingerabdruck,
|
||
sshAktiv: true,
|
||
sshPort: ssh.port,
|
||
sshPrivat: verschluesseln(paar.private),
|
||
sshOeffentlich: paar.public,
|
||
},
|
||
});
|
||
});
|
||
|
||
const testen = async (cookie: string) =>
|
||
await testPost(anfrage("POST", "/api/v1/server/rz1/test", cookie), kontext({ name: "rz1" }));
|
||
|
||
it("speichert Bericht, Version und Erreichbarkeit; Operator darf testen", async () => {
|
||
const antwort = await testen(operator);
|
||
expect(antwort.status).toBe(200);
|
||
const ergebnis = await antwort.json();
|
||
expect(ergebnis.ok).toBe(true);
|
||
expect(ergebnis.bericht.ssh).toBe("nicht_bestaetigt");
|
||
const server = await testPrisma.server.findUniqueOrThrow({ where: { name: "rz1" } });
|
||
expect(server).toMatchObject({ erreichbar: true, pveVersion: "8.3.5" });
|
||
expect(await testPrisma.protokoll.count({ where: { aktion: "Verbindung getestet" } })).toBe(1);
|
||
});
|
||
|
||
it("Host-Key bestätigen: nur der gesehene, nur durch Admins – danach geht SSH", async () => {
|
||
const erst = await (await testen(admin)).json();
|
||
const gesehen = erst.bericht.knoten[0].ssh.hostSchluessel as string;
|
||
const pfad = "/api/v1/server/rz1/ssh/hostschluessel";
|
||
|
||
expect(
|
||
(
|
||
await hostSchluesselPost(
|
||
anfrage("POST", pfad, operator, { knoten: "pve01", hostSchluessel: gesehen }),
|
||
kontext({ name: "rz1" }),
|
||
)
|
||
).status,
|
||
).toBe(403);
|
||
|
||
const untergeschoben = "SHA256:" + "A".repeat(43);
|
||
const abgelehnt = await hostSchluesselPost(
|
||
anfrage("POST", pfad, admin, { knoten: "pve01", hostSchluessel: untergeschoben }),
|
||
kontext({ name: "rz1" }),
|
||
);
|
||
expect(abgelehnt.status).toBe(409);
|
||
|
||
const ok = await hostSchluesselPost(
|
||
anfrage("POST", pfad, admin, { knoten: "pve01", hostSchluessel: gesehen }),
|
||
kontext({ name: "rz1" }),
|
||
);
|
||
expect(ok.status).toBe(200);
|
||
expect((await ok.json()).sshHostSchluessel).toEqual({ pve01: gesehen });
|
||
|
||
const dann = await (await testen(admin)).json();
|
||
expect(dann.bericht.ssh).toBe("ok");
|
||
expect(dann.bericht.faehigkeiten.templateBau.weg).toBe("ssh");
|
||
});
|
||
|
||
it("ein nicht erreichbarer Server ist ein Ergebnis, kein Fehler", async () => {
|
||
await testPrisma.server.update({ where: { name: "rz1" }, data: { url: "https://127.0.0.1:9" } });
|
||
const antwort = await testen(admin);
|
||
expect(antwort.status).toBe(200);
|
||
const ergebnis = await antwort.json();
|
||
expect(ergebnis).toMatchObject({ ok: false });
|
||
expect((await testPrisma.server.findUniqueOrThrow({ where: { name: "rz1" } })).erreichbar).toBe(false);
|
||
});
|
||
});
|