Files
proxdeploy/tests/verbindungstest.test.ts
T
florian.wildeandClaude Opus 5.5 8dca30e254 fix(ssh): nur ed25519-Schlüssel speichern, die ssh2 lesen kann
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>
2026-10-12 16:39:54 +02:00

104 lines
4.0 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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);
});
});