Auf A als Zielbenutzer ssh mit BatchMode und StrictHostKeyChecking: keine Rueckfrage, keine stille Annahme eines Host-Keys. Der Test beweist damit genau das, was ein Cronjob auf A spaeter braucht. Die letzte Meldung von ssh landet in Vorgang und Protokoll; ein Erfolg setzt die letzte Verwendung des Schluessels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
61 lines
2.3 KiB
TypeScript
61 lines
2.3 KiB
TypeScript
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
import { randomBytes } from "node:crypto";
|
|
import { tmpdir } from "node:os";
|
|
import path from "node:path";
|
|
import { prisma } from "@/lib/db";
|
|
import { verschluesseln } from "@/lib/geheimnisse";
|
|
import { vorgangAusfuehren } from "@/worker/ausfuehren";
|
|
import { masterKeyVergessen } from "@/worker/geheimnisse";
|
|
import { HANDLER } from "@/worker/handler";
|
|
import { rootPasswort, type Ziel } from "./ziele";
|
|
|
|
/** Gemeinsame Vorbereitung fuer Integrationstests: Master-Key, Host mit Bootstrap (#016). */
|
|
|
|
export function masterKeyAnlegen(): () => void {
|
|
const 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();
|
|
return () => rmSync(verzeichnis, { recursive: true, force: true });
|
|
}
|
|
|
|
export async function vorgangLaufen(
|
|
art: "HOSTKEY_ABRUFEN" | "BOOTSTRAP" | "TROCKENLAUF" | "AUSROLLEN" | "VERBINDUNGSTEST",
|
|
daten: {
|
|
hostId?: string;
|
|
eingabe?: object;
|
|
geheim?: string;
|
|
angefordertVonId?: string;
|
|
},
|
|
) {
|
|
const vorgang = await prisma.vorgang.create({
|
|
data: {
|
|
art,
|
|
hostId: daten.hostId ?? null,
|
|
eingabe: daten.eingabe ?? {},
|
|
angefordertVonId: daten.angefordertVonId ?? null,
|
|
},
|
|
});
|
|
await vorgangAusfuehren(vorgang.id, HANDLER, daten.geheim);
|
|
return prisma.vorgang.findUniqueOrThrow({ where: { id: vorgang.id } });
|
|
}
|
|
|
|
/** Host anlegen, Host-Key vertrauen, Bootstrap per root-Passwort. */
|
|
export async function eingerichteterHost(
|
|
ziel: Ziel,
|
|
name: string,
|
|
anmeldeart: "DIENSTBENUTZER" | "ROOT" = "DIENSTBENUTZER",
|
|
) {
|
|
const host = await prisma.host.create({ data: { name, adresse: ziel.host, port: ziel.port, anmeldeart } });
|
|
await vorgangLaufen("HOSTKEY_ABRUFEN", { hostId: host.id });
|
|
await prisma.hostSchluessel.updateMany({ where: { hostId: host.id }, data: { status: "VERTRAUT" } });
|
|
const bootstrap = await vorgangLaufen("BOOTSTRAP", {
|
|
hostId: host.id,
|
|
eingabe: { modus: "passwort", benutzer: "root" },
|
|
geheim: verschluesseln(rootPasswort()),
|
|
});
|
|
if (bootstrap.status !== "ERLEDIGT") throw new Error(`Bootstrap fehlgeschlagen: ${bootstrap.fehler}`);
|
|
return prisma.host.findUniqueOrThrow({ where: { id: host.id } });
|
|
}
|