import { execFileSync } from "node:child_process"; import { mkdtempSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import path from "node:path"; import { randomBytes } from "node:crypto"; import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest"; import { prisma } from "@/lib/db"; import { privateKeyLesen } from "@/lib/ssh/openssh"; import { deploymentKeySicherstellen, masterKeyVergessen, privateKeyLaden, privateKeyOeffnen, } from "@/worker/geheimnisse"; import { datenbankLeeren } from "./hilfen/db"; /** Verschluesselte Ablage im Worker (#006). */ let verzeichnis: string; beforeAll(() => { verzeichnis = mkdtempSync(path.join(tmpdir(), "sshkv-master-")); const datei = path.join(verzeichnis, "master.key"); writeFileSync(datei, randomBytes(32), { mode: 0o600 }); process.env["SSHKV_MASTER_KEY_FILE"] = datei; masterKeyVergessen(); }); afterAll(() => { rmSync(verzeichnis, { recursive: true, force: true }); }); beforeEach(async () => { await datenbankLeeren(); }); describe("Deployment-Key", () => { it("wird einmal erzeugt und danach wiederverwendet", async () => { const [a, b] = await Promise.all([deploymentKeySicherstellen(), deploymentKeySicherstellen()]); expect(a).toBe(b); expect(await prisma.schluessel.count({ where: { kategorie: "DEPLOYMENT" } })).toBe(1); expect(await deploymentKeySicherstellen()).toBe(a); }); it("laesst sich oeffnen und passt zum Public Key", async () => { const id = await deploymentKeySicherstellen(); const schluessel = await prisma.schluessel.findUniqueOrThrow({ where: { id } }); const klartext = await privateKeyLaden(id); const info = privateKeyLesen(klartext.toString("utf8")); klartext.fill(0); expect(info.fingerprint).toBe(schluessel.fingerprint); }); it("laesst sich nicht unter fremder ID oeffnen (Zusatzdaten)", async () => { const id = await deploymentKeySicherstellen(); const schluessel = await prisma.schluessel.findUniqueOrThrow({ where: { id } }); await expect(privateKeyOeffnen({ ...schluessel, id: "andere-id" })).rejects.toThrow(); }); it("steht in keinem Datenbank-Dump im Klartext", async () => { const id = await deploymentKeySicherstellen(); const klartext = (await privateKeyLaden(id)).toString("utf8"); // Der Teil zwischen Kopf und Fuss ist das eigentliche Geheimnis. const kern = klartext.split("\n").slice(1, -2).join(""); const url = new URL(process.env["DATABASE_URL"] ?? ""); const dump = execFileSync( "docker", ["exec", "sshkv-dev-db", "pg_dump", "-U", url.username, "--data-only", url.pathname.slice(1)], { encoding: "utf8", maxBuffer: 64 * 1024 * 1024 }, ); expect(dump).toContain("Deployment-Key der Instanz"); expect(dump).not.toContain("PRIVATE KEY"); expect(dump).not.toContain(kern.slice(0, 40)); }); });