Files
proxdeploy/src/server/services/bereitstellung.ts
T
florian.wildeandClaude Opus 5.5 d25ec54ef9 feat(lxc): LXC-Container ausrollen, mit oder ohne Node-SSH
Container aus dem Proxmox-Template (neueste passende Version aus
aplinfo), unprivilegiert, nesting automatisch für docker. Weil Proxmox
keine Exec-API für Container hat, läuft die Provisionierung per SSH:
bevorzugt pct exec über den Node, sonst direkt in den Container mit
einem Bereitstellungsschlüssel, der am Ende wieder entfernt wird.

Auf pve-demo beide Wege geprüft: Debian 13 mit docker per pct exec
(hello-world läuft mit nesting allein) und Ubuntu 24.04 per SSH in den
Container. Dabei gefunden: chrony scheitert ohne CAP_SYS_TIME (jetzt
if: vm), sshd -t auf Ubuntu 24.04 ohne /run/sshd.

Closes #009

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 13:19:13 +02:00

31 lines
1.5 KiB
TypeScript

import ssh2 from "ssh2";
import type { Bereitstellungsschluessel } from "@/engine/ablaeufe/lxc-ausrollen";
import { prisma } from "@/lib/db";
import { entschluesseln, verschluesseln } from "@/lib/geheimnisse";
/**
* Der Bereitstellungsschlüssel: damit meldet sich ProxDeploy als root in frisch angelegten
* Containern an, wenn es keinen SSH-Zugang zum Node hat. Er steht nur während der
* Provisionierung in /root/.ssh/authorized_keys und wird am Ende des Skripts entfernt (am
* Kommentar `proxdeploy-bereitstellung` erkennbar). Einmal erzeugt, privat nur verschlüsselt.
*/
const SCHLUESSEL = "bereitstellungsschluessel";
export const KOMMENTAR = "proxdeploy-bereitstellung";
export async function bereitstellungsschluessel(): Promise<Bereitstellungsschluessel> {
const vorhanden = await prisma.einstellung.findUnique({ where: { schluessel: SCHLUESSEL } });
if (vorhanden) {
const wert = vorhanden.wert as { privat: string; oeffentlich: string };
return { privat: entschluesseln(wert.privat), oeffentlich: wert.oeffentlich };
}
const paar = ssh2.utils.generateKeyPairSync("ed25519", { comment: KOMMENTAR });
const neu = { privat: verschluesseln(paar.private), oeffentlich: paar.public.trim() };
// Zwei gleichzeitige Aufträge: der erste gewinnt, der zweite liest dessen Schlüssel.
await prisma.einstellung.upsert({
where: { schluessel: SCHLUESSEL },
create: { schluessel: SCHLUESSEL, wert: neu },
update: {},
});
return await bereitstellungsschluessel();
}