#014 Private Key anzeigen und herunterladen #14

Closed
opened 2026-10-12 15:12:40 +00:00 by florian.wilde · 0 comments
Owner

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_ABHOLEN an (Protokoll privatekey.angefordert)
    → der Worker öffnet den Umschlag und legt den Key mit SSHKV_APP_SECRET verschlüsselt in
    Vorgang.ergebnis ab ({ abholung: "v1.…" }) → das Web holt ihn genau einmal ab: ein
    einziges UPDATE … FROM (SELECT … FOR UPDATE) RETURNING liest das alte Ergebnis und setzt
    { abgeholt: true }; fünf gleichzeitige Abholungen ergeben genau eine (Test). Abholen darf
    nur, wer angefordert hat. Protokoll privatekey.angezeigt / privatekey.heruntergeladen mit
    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_MS in src/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>
    über POST /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:
    wegen Referrer-Policy: no-referrer senden Browser bei Formular-POSTs Origin: null,
    maßgeblich ist deshalb Sec-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=1 und öffnet danach den Dialog wieder. Der
    Mock-OIDC liefert auth_time nur, 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-Vorgang PRIVATE_KEY_LOESCHEN
    (privateKeyVerwerfen in src/worker/geheimnisse.ts), damit nur diese Datei die
    Chiffrat-Felder anfasst – auch zum Leeren. Setzt Chiffrat, DEK und Version auf null und
    privatGeloeschtAm; Protokoll privatekey.geloescht. CHECK
    schluessel_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.

Quelle: docs/issues/014-private-key-anzeigen.md – dort wird dieses Issue gepflegt.

## Ziel Nur Admin, nur nach Re-Auth, immer protokolliert; Entschlüsselung im Worker. ## Aufgaben - [x] Einmal-Abholung - [x] Option „Private Key nach Ausrollen löschen“ ## Akzeptanzkriterien - [x] Kein Caching, kein Logging ## Abhängigkeiten #012, #004 ## Notizen / Entscheidungen - **Ablauf:** Web legt einen Vorgang `PRIVATE_KEY_ABHOLEN` an (Protokoll `privatekey.angefordert`) → der Worker öffnet den Umschlag und legt den Key mit `SSHKV_APP_SECRET` verschlüsselt in `Vorgang.ergebnis` ab (`{ abholung: "v1.…" }`) → das Web holt ihn **genau einmal** ab: ein einziges `UPDATE … FROM (SELECT … FOR UPDATE) RETURNING` liest das alte Ergebnis und setzt `{ abgeholt: true }`; fünf gleichzeitige Abholungen ergeben genau eine (Test). Abholen darf nur, wer angefordert hat. Protokoll `privatekey.angezeigt` / `privatekey.heruntergeladen` mit 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_MS` in `src/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>` über `POST /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: wegen `Referrer-Policy: no-referrer` senden Browser bei Formular-POSTs `Origin: null`, maßgeblich ist deshalb `Sec-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=1` und öffnet danach den Dialog wieder. Der Mock-OIDC liefert `auth_time` nur, 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-Vorgang `PRIVATE_KEY_LOESCHEN` (`privateKeyVerwerfen` in `src/worker/geheimnisse.ts`), damit nur diese Datei die Chiffrat-Felder anfasst – auch zum Leeren. Setzt Chiffrat, DEK und Version auf `null` und `privatGeloeschtAm`; Protokoll `privatekey.geloescht`. CHECK `schluessel_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. --- Quelle: `docs/issues/014-private-key-anzeigen.md` – dort wird dieses Issue gepflegt.
florian.wilde added this to the v0.2.0 milestone 2026-10-12 15:12:40 +00:00
florian.wilde added the sicherheit label 2026-10-12 15:12:40 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: florian.wilde/sshkeyvault#14