From 6cdc49df49e3cce345701c72ad25e3dc1115d0bf Mon Sep 17 00:00:00 2001 From: Florian Wilde Date: Mon, 12 Oct 2026 09:17:46 +0200 Subject: [PATCH] docs: Stand und Entscheidungen zu #006 festhalten (#006) Der framework-freie Kern ist fertig; Deployment-Key, Master-Key im Worker und der Nachweis am Datenbank-Dump haengen an Datenbank und Worker und kommen mit #007. Die Entscheidungen (16 bcrypt-Runden, Fehlercodes, RSA-Mindestlaenge abschaltbar fuers Beobachten, Umschlag-Format, Dateirechte des Master-Keys) stehen im Issue, damit die Anbindung nicht raten muss. Co-Authored-By: Claude Opus 5.5 --- .../006-geheimnisse-schluesselformat.md | 47 +++++++++++++++---- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/docs/issues/006-geheimnisse-schluesselformat.md b/docs/issues/006-geheimnisse-schluesselformat.md index b8b83ca..5d6ce97 100644 --- a/docs/issues/006-geheimnisse-schluesselformat.md +++ b/docs/issues/006-geheimnisse-schluesselformat.md @@ -1,5 +1,5 @@ # #006 Verschlüsselte Ablage und OpenSSH-Schlüsselformat -**Status:** offen +**Status:** in Arbeit **Meilenstein:** v0.1.0 **Labels:** sicherheit **Geschätzt:** 4 h **Tatsächlich:** – @@ -8,19 +8,50 @@ 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 -- [ ] Master-Key aus `SSHKV_MASTER_KEY_FILE` (32 Byte), nur im Worker -- [ ] AES-256-GCM, DEK je Schlüssel, AAD = Schlüssel-ID + Fingerprint, Master-Key-Version -- [ ] ed25519 und RSA-4096 erzeugen (Node `crypto`), OpenSSH-Format schreiben und lesen (optional Passphrase: bcrypt-pbkdf + aes256-ctr) -- [ ] Fingerprint SHA256 wie `ssh-keygen -l` +- [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] 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 -- [ ] Log-Redaction +- [x] Log-Redaction (`schwaerzen`; Einbau in Logger/Protokoll folgt) ## Akzeptanzkriterien -- [ ] `ssh-keygen -y -f` liest erzeugte Schlüssel (mit und ohne Passphrase) -- [ ] Fingerprints identisch zu `ssh-keygen -lf` +- [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 ## Abhängigkeiten #003 ## Notizen / Entscheidungen +- **Kern fertig; Anbindung an Datenbank/Worker folgt mit #007.** Offen sind Deployment-Key beim + ersten Start, Master-Key nur im Worker laden und der Nachweis „Datenbank-Dump enthält keinen + Klartext“. +- Module: `src/lib/ssh/openssh.ts` (Erzeugen/Schreiben/Lesen, Fingerprint, Public-Key-Zeilen), + `src/lib/krypto/umschlag.ts` (Envelope Encryption, Master-Key laden), + `src/lib/protokoll-schwaerzen.ts` (Redaction). Alle framework-frei. +- Eigener Serializer für `openssh-key-v1` statt `ssh-keygen`: keine Private Keys als Temp-Dateien. + Die Tests prüfen gegen das echte `ssh-keygen` (beide Richtungen, mit/ohne Passphrase). +- Passphrase: `aes256-ctr` + `bcrypt` mit **16 Runden** (ssh-keygen nimmt heute 24). `bcrypt-pbkdf` + ist reines JavaScript; 16 Runden halten das im Worker unter einer Sekunde. Beim Lesen gilt die + Rundenzahl aus der Datei (Obergrenze 1000 gegen absichtlich teure Importe). + Andere Cipher (z. B. `aes256-gcm@openssh.com` via `ssh-keygen -Z`) werden mit klarer Meldung + abgelehnt. Private-Key-Import nur für ed25519 und RSA. +- `SchluesselFormatFehler` trägt einen `code` (`ungueltig`, `nicht_unterstuetzt`, + `passphrase_fehlt`, `passphrase_falsch`, `zu_schwach`); Meldungen sind feste Sätze ohne Eingabe. +- RSA-Mindestlänge 2048 Bit standardmäßig in `publicKeyZeileLesen` und `privateKeyLesen`; + `{ rsaMindestBits: 0 }` für das reine Beobachten vorhandener `authorized_keys` (Drift). +- Kommentare mit Steuerzeichen werden abgelehnt, sonst ließe sich über einen Kommentar eine zweite + Zeile in `authorized_keys` einschleusen. +- Umschlag-Format je Puffer: `Version (1) | IV (12) | Tag (16) | Daten`. AAD Klartext = `zusatz` + (vorgesehen: `schluessel::`), AAD DEK = `sshkv-dek:v`. + `versiegeln(..., masterKeyVersion = 1)`; beim Öffnen muss der Aufrufer den Master-Key passend zur + Version wählen. +- Master-Key-Datei mit Gruppe/Welt-Leserecht → Abbruch, außer `SSHKV_MASTER_KEY_UNSICHER_ERLAUBT=1` + (dann Warnung). Achtung Docker: Secrets ohne Swarm sind Bind-Mounts mit den Rechten der + Host-Datei – dort `chmod 400` und passenden Eigentümer setzen. +- Schwärzen greift auf Feldnamen, die einen der Wortstämme `privat`, `passwort`, `password`, + `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]`.