feat(worker): Private Keys im Worker versiegeln, Deployment-Key (#006)

Der zweite Teil von #006 bindet den Kern an Datenbank und Worker. Nur
src/worker/geheimnisse.ts liest Chiffrat und Data-Key; ein struktureller
Test haelt fest, dass der Webprozess nichts von dort importiert und so
nie an den Master-Key kommt.

Die Zusatzdaten binden jedes Chiffrat an ID und Fingerprint seines
Schluessels: kopiert man es in eine andere Zeile, laesst es sich dort
nicht oeffnen. Der Deployment-Key entsteht beim ersten Start; ein Test
prueft, dass ein pg_dump keinen Klartext enthaelt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-12 09:33:18 +02:00
co-authored by Claude Opus 5.5
parent 855d77cf17
commit 9b807c13ae
5 changed files with 318 additions and 5 deletions
@@ -1,25 +1,25 @@
# #006 Verschlüsselte Ablage und OpenSSH-Schlüsselformat
**Status:** in Arbeit
**Status:** erledigt
**Meilenstein:** v0.1.0
**Labels:** sicherheit
**Geschätzt:** 4 h **Tatsächlich:** –
**Geschätzt:** 4 h **Tatsächlich:** 0 h 25 min
## Ziel
Envelope Encryption für Private Keys mit Master-Key aus Datei; eigene Erzeugung und Serialisierung von OpenSSH-Schlüsseln ohne temporäre Dateien; Deployment-Key der Instanz.
## Aufgaben
- [x] Master-Key aus Datei laden (32 Byte roh, Base64 oder Hex; Rechteprüfung) – `masterKeyLaden`
- [ ] Master-Key aus `SSHKV_MASTER_KEY_FILE` nur im Worker anbinden
- [x] Master-Key aus `SSHKV_MASTER_KEY_FILE` nur im Worker anbinden
- [x] AES-256-GCM, DEK je Schlüssel, AAD = Schlüssel-ID + Fingerprint, Master-Key-Version
- [x] ed25519 und RSA-4096 erzeugen (Node `crypto`), OpenSSH-Format schreiben und lesen (optional Passphrase: bcrypt-pbkdf + aes256-ctr)
- [x] Fingerprint SHA256 wie `ssh-keygen -l`
- [ ] Deployment-Key beim ersten Start erzeugen
- [x] Deployment-Key beim ersten Start erzeugen
- [x] Log-Redaction (`schwaerzen`; Einbau in Logger/Protokoll folgt)
## Akzeptanzkriterien
- [x] `ssh-keygen -y -f` liest erzeugte Schlüssel (mit und ohne Passphrase)
- [x] Fingerprints identisch zu `ssh-keygen -lf`
- [ ] Datenbank-Dump enthält keinen Klartext-Schlüssel
- [x] Datenbank-Dump enthält keinen Klartext-Schlüssel
## Abhängigkeiten
#003
@@ -55,3 +55,7 @@ Envelope Encryption für Private Keys mit Master-Key aus Datei; eigene Erzeugung
`passphrase`, `secret`, `geheimnis`, `token`, `masterkey`, `authorization`, `cookie` enthalten
(nicht nur als Präfix), sowie auf PEM-Private-Keys in jeder Zeichenkette (auch abgeschnitten,
auch in `message`/`stack` von Fehlern). Rohe Bytes werden zu `[Binärdaten]`.
- **Anbindung im Worker (Teil 2):** `src/worker/geheimnisse.ts` ist die einzige Stelle, die `privatVerschluesselt`/`dekVerschluesselt` liest; `tests/geheimnisse-grenze.test.ts` hält das strukturell fest, ebenso dass nichts außerhalb von `src/worker` von dort importiert.
- **Zusatzdaten** `schluessel:<id>:<fingerprint>`: Die ID wird deshalb vor dem Versiegeln vergeben (`randomUUID`), nicht von Prisma.
- **Deployment-Key** entsteht beim Start des Workers (ed25519, Kommentar `sshkv-deployment@<rechner>`). Gleichzeitige Starts: der partielle Index lässt nur einen zu, der zweite liest den ersten.
- **Dump-Test:** `pg_dump --data-only` der Testdatenbank enthält weder „PRIVATE KEY“ noch den Schlüsselkern.