Admins koennen einen gespeicherten Private Key nach frischer Bestaetigung einmalig anzeigen oder als Datei <slug> herunterladen und ihn endgueltig aus SSHKeyVault loeschen. Warum so: - Der Web-Prozess hat keinen Master-Key. Er bittet den Worker per Vorgang, den Key zu oeffnen; der legt ihn mit SSHKV_APP_SECRET verschluesselt in Vorgang.ergebnis ab. Das Web holt ihn genau einmal ab: ein einziges UPDATE unter Zeilensperre liest und ueberschreibt. - Nicht abgeholte Keys verfallen nach zwei Minuten: Worker-Takt alle 30 s, Aufraeumen vor jeder Abholung, und das Web gibt aeltere nicht mehr heraus. Die Status-Schnittstelle zeigt nur "bereit". - Download nur per POST (ein GET-Link oder Vorabruf wuerde die Abholung verbrauchen), no-store, Herkunft per Sec-Fetch-Site, weil no-referrer bei Formular-POSTs Origin: null sendet. - Loeschen laeuft als Worker-Vorgang, damit nur geheimnisse.ts die Chiffrat-Felder anfasst; eigenes Recht privateKeyLoeschen mit Bestaetigung. Deployment-Keys gibt sshkv weder heraus noch loescht es sie. - Protokoll: angefordert, angezeigt, heruntergeladen, geloescht - mit Fingerprint, nie mit Key. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SSHKeyVault (sshkv)
Selbst gehostete Weboberfläche zur zentralen Verwaltung von SSH-Schlüsseln: Schlüsselpaare
erzeugen und verwalten, Public Keys in authorized_keys verteilen, Private Keys gezielt auf
andere Hosts ausrollen, Ist- und Soll-Zustand abgleichen – alles revisionssicher protokolliert.
Stand 0.1.0 (Phase 1): Grundgerüst, Anmeldung (OIDC + Notfall-Admin mit TOTP), Hosts und Hostgruppen, Host-Key-Prüfung (TOFU) und Bootstrap neuer Hosts. Schlüsselverwaltung und Ausrollen folgen in Phase 2 und 3 – siehe
docs/PLAN.md.
Sicherheit vorab
SSHKeyVault hat root-Zugriff auf alle verwalteten Hosts. Deshalb:
- nur im internen Netz bzw. VPN betreiben, mit Reverse Proxy und TLS davor;
- den Master-Key (
SSHKV_MASTER_KEY_FILE) getrennt von der Datenbank sichern – ohne ihn ist ein Datenbank-Backup wertlos, und mit ihm zusammen gibt es alle Private Keys preis; - Private Keys liegen nur verschlüsselt in der Datenbank (AES-256-GCM, Envelope Encryption); entschlüsseln kann ausschließlich der Worker, der Webprozess hat den Master-Key nicht.
Mehr dazu in docs/PLAN.md, Abschnitt 3.
Installation (Docker Compose)
cp .env.example .env # Werte eintragen, siehe Kommentare
openssl rand -base64 48 # → SSHKV_APP_SECRET
mkdir -p geheim && head -c 32 /dev/urandom > geheim/master.key
sudo chown 1001:1001 geheim/master.key && sudo chmod 400 geheim/master.key
docker compose up -d
Die Oberfläche lauscht auf 127.0.0.1:3400 (SSHKV_BIND, SSHKV_PORT). Beim ersten Start legt
der Dienst migrate das Schema, den Notfall-Admin (SSHKV_BREAKGLASS_*) und die eingebauten
Vorlagen an; der Worker erzeugt den Deployment-Key der Instanz.
Anmeldung: über authentik (SSHKV_OIDC_*, Rollen über die Gruppen sshkv-admins,
sshkv-operators, sshkv-readers). Der Notfall-Admin richtet bei der ersten Anmeldung TOTP ein.
Einen Host aufnehmen
- Hosts → + Host anlegen (Name, Adresse, Port; Anmeldung als Dienstbenutzer
sshkvmit sudo – empfohlen – oder direkt als root). - Host-Key abrufen, den Fingerprint am Host vergleichen
(
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub) und vertrauen. - Bootstrap: einmalig per Passwort (wird nicht gespeichert) oder den angezeigten Befehl auf dem Host einfügen und „Einrichtung prüfen“.
Entwicklung
Node 24, pnpm 12, Docker. Konventionen und Befehle: CLAUDE.md.
pnpm install
pnpm db:up && pnpm db:migrate && pnpm db:seed
pnpm test:ziele:up # SSH-Testziele + Mock-OIDC
pnpm build && pnpm start # http://localhost:3400
pnpm worker # zweites Terminal
pnpm test && pnpm test:integration && pnpm test:e2e