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>
3.1 KiB
3.1 KiB
#014 Private Key anzeigen und herunterladen
Status: erledigt Meilenstein: v0.2.0 Labels: sicherheit Geschätzt: 2 h Tatsächlich: 0 h 10 min
Ziel
Nur Admin, nur nach Re-Auth, immer protokolliert; Entschlüsselung im Worker.
Aufgaben
- Einmal-Abholung
- Option „Private Key nach Ausrollen löschen“
Akzeptanzkriterien
- Kein Caching, kein Logging
Abhängigkeiten
#012, #004
Notizen / Entscheidungen
- Ablauf: Web legt einen Vorgang
PRIVATE_KEY_ABHOLENan (Protokollprivatekey.angefordert) → der Worker öffnet den Umschlag und legt den Key mitSSHKV_APP_SECRETverschlüsselt inVorgang.ergebnisab ({ abholung: "v1.…" }) → das Web holt ihn genau einmal ab: ein einzigesUPDATE … FROM (SELECT … FOR UPDATE) RETURNINGliest das alte Ergebnis und setzt{ abgeholt: true }; fünf gleichzeitige Abholungen ergeben genau eine (Test). Abholen darf nur, wer angefordert hat. Protokollprivatekey.angezeigt/privatekey.heruntergeladenmit Fingerprint, nie mit Key. - Die Status-Schnittstelle
/api/vorgaenge/[id]zeigt für eine bereitliegende Abholung nur{ bereit: true }, nie das Chiffrat. - Verfall nach 2 Minuten (
ABHOLFRIST_MSinsrc/lib/schluessel.ts), dreifach abgesichert: der Worker räumt alle 30 s und vor jeder neuen Abholung auf (abholungenAufraeumen, ein einziges UPDATE →{ verfallen: true }), und das Web gibt eine ältere Abholung nicht mehr heraus (überschreibt sie aber trotzdem). Ein Worker-Takt statt pg-boss-Zeitplan: dessen Raster ist eine Minute. - Anzeige im Dialog (Monospace, Kopier-Knopf, „Wird nicht erneut angezeigt“); der Key lebt
nur im Zustand des Dialogs und wird beim Schließen verworfen. Download als Datei
<slug>überPOST /api/private-key/[vorgangId](Formular, kein GET – ein Link oder Vorabruf würde die einmalige Abholung verbrauchen),Content-Disposition: attachment,Cache-Control: no-store. Der Route Handler prüft selbst Recht, Bestätigung und Herkunft: wegenReferrer-Policy: no-referrersenden Browser bei Formular-POSTsOrigin: null, maßgeblich ist deshalbSec-Fetch-Site: same-origin(oder ein passender Origin). - Re-Auth: Fehlt die frische Bestätigung, leitet die Oberfläche nach
/bestaetigen?ziel=/schluessel/<id>?privatekey=1und öffnet danach den Dialog wieder. Der Mock-OIDC liefertauth_timenur, wenn man es in die Claims schreibt – die E2E tut das. - Löschen („Private Key aus SSHKeyVault löschen“): neues Recht
privateKeyLoeschen(nur Admin, mit Bestätigung), ausgeführt als Worker-VorgangPRIVATE_KEY_LOESCHEN(privateKeyVerwerfeninsrc/worker/geheimnisse.ts), damit nur diese Datei die Chiffrat-Felder anfasst – auch zum Leeren. Setzt Chiffrat, DEK und Version aufnullundprivatGeloeschtAm; Protokollprivatekey.geloescht. CHECKschluessel_geloescht_ohne_private_key(Migration aus #012). - Deployment-Keys gibt sshkv weder heraus noch löscht es deren Private Key: der Key ist root auf allen Hosts, und ohne ihn käme der Worker nirgends mehr hin. Im Notfall wird rotiert.