From 0a58f6ce3231c157644e695734e1485a7cab97a8 Mon Sep 17 00:00:00 2001 From: Florian Wilde Date: Mon, 12 Oct 2026 11:05:57 +0200 Subject: [PATCH] chore(release): 0.2.0 - Phase 2 abgeschlossen Schluessel, Personen, Private Key anzeigen, authorized_keys-Parser und Ausrollen von Public Keys; vorgezogen das Ausrollen von Private Keys mit ssh-config und known_hosts. CHANGELOG und DEVLOG nachgetragen. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 20 ++++++++++++++ DEVLOG.md | 33 ++++++++++++++++++++++-- docs/issues/016-public-key-ausrollen.md | 18 ++++++++----- docs/issues/017-private-key-ausrollen.md | 18 ++++++++----- package.json | 2 +- 5 files changed, 76 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b6f634..6173977 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,26 @@ und dieses Projekt folgt [Semantic Versioning](https://semver.org/lang/de/). ### Sicherheit +## [0.2.0] – 2026-10-12 + +Phase 2: Schlüssel, Personen und Ausrollen von Public Keys – dazu vorgezogen aus Phase 3 das Ausrollen von Private Keys. + +### Hinzugefügt +- Schlüssel erzeugen (ed25519, RSA-4096, optional mit Passphrase), Paar oder Public Key importieren, Public Key exportieren; Liste mit Filter, Detailseite mit Verwendungen (#012) +- Personen verwalten; Self-Service „Meine Schlüssel“: jeder Benutzer hinterlegt eigene Public Keys, wirksam erst nach Zuweisung (#013) +- Private Key anzeigen und herunterladen – nur Admin, nur nach erneuter Bestätigung, genau einmal abholbar; Private Key aus SSHKeyVault löschen (#014) +- authorized_keys-Parser und verwalteter Block: fremde Zeilen bleiben bytegenau, beschädigte Blöcke werden nie überschrieben, Deployment-Key ist vor dem Entfernen geschützt (#015) +- Public Keys Hosts oder Hostgruppen zuweisen, Dry-Run mit Diff, Ausrollen mit Backup und optimistischer Sperre; Ansicht je Schlüssel und je Host (#016) +- Private Key auf einen Quell-Host ausrollen, mit verwaltetem Block in `~/.ssh/config` und `known_hosts` (#017) +- Verbindungstest von Host A nach Host B (#020, Teil) +- Zielbenutzer anlegen und sudoers pflegen – Grundlage, Oberfläche folgt (#018, Teil) +- Vorlagen: Platzhalter und Prüfung – Grundlage, Oberfläche folgt (#019, Teil) + +### Sicherheit +- Importierte Private Keys reisen nur verschlüsselt im Job; Vorgang, Protokoll und Browser sehen sie nie (Tests) +- Private-Key-Ziele nimmt der Dry-Run nur für Admins auf +- Dateien in fremden Home-Verzeichnissen werden als deren Benutzer gelesen und geschrieben (Symlink-Schutz) + ## [0.1.0] – 2026-10-12 Phase 1: Grundgerüst, Anmeldung, Hosts und Bootstrap. diff --git a/DEVLOG.md b/DEVLOG.md index e231466..9d57c92 100644 --- a/DEVLOG.md +++ b/DEVLOG.md @@ -2,8 +2,8 @@ ## Zeitübersicht -**Gesamt (Wandzeit):** 1 h 25 min -**Summe der Issue-Zeiten:** 4 h 23 min – Sub-Agenten arbeiteten parallel an #002, #006, #008 und #011. +**Gesamt (Wandzeit):** 2 h 25 min +**Summe der Issue-Zeiten:** 7 h 08 min – Sub-Agenten arbeiteten parallel an #002, #006, #008, #011 und #012–#014. | Issue | Zeit | |---|---| @@ -19,10 +19,20 @@ | #009 Host-Key-Prüfung | 0 h 25 min | | #010 Bootstrap | 0 h 35 min | | #011 Test-SSH-Container | 0 h 15 min | +| #012 Schlüssel erzeugen, importieren, exportieren | 0 h 25 min | +| #013 Personen und Self-Service | 0 h 10 min | +| #014 Private Key anzeigen | 0 h 10 min | +| #015 authorized_keys-Parser | 0 h 15 min | +| #016 Public Key ausrollen | 0 h 40 min | +| #017 Private Key ausrollen | 0 h 45 min | +| #018 Zielbenutzer und sudoers (Teil) | 0 h 10 min | +| #019 Vorlagen (Teil) | 0 h 10 min | +| #020 Verbindungstest (Teil) | 0 h 05 min | | Meilenstein | Zeit | |---|---| | v0.1.0 | 1 h 25 min (Wandzeit) | **abgeschlossen** +| v0.2.0 | 1 h 00 min (Wandzeit) | **abgeschlossen** --- @@ -55,3 +65,22 @@ - **Eigene Testdatenbank je parallelem Lauf:** Sub-Agent und Hauptsitzung leerten sich gegenseitig die Tabellen (in `CLAUDE.md` festgehalten). - **Prisma-Client in den Worker gebündelt:** Im Standalone-Abbild fand die ESM-Auflösung `@prisma/client` sonst nicht (erst im echten Docker-Stapel aufgefallen). - **Worker-Benutzer in Compose wählbar** (`SSHKV_WORKER_USER`), weil Docker-Secrets ohne Swarm die Rechte der Host-Datei tragen. + +## 2026-10-12 · Sitzung 2 · 10:07–11:07 +**Issue:** #012–#020 +**Version:** 0.2.0 + +### Erledigt +- #012–#014 (Schlüssel, Personen/Self-Service, Private Key anzeigen) von einem Sub-Agenten in eigenem Worktree, danach gemergt; Konflikte in `useVorgang` und der Handler-Tabelle zusammengeführt +- #015 authorized_keys-Parser und Merge mit 26 Randfall-Tests +- #016 Public Keys ausrollen: Soll, Trockenlauf, Ausrollen, Hostseite, Schlüsselseite – Integrationstests und E2E +- #017 Private Key mit `~/.ssh/config` und `known_hosts` – Integrationstest „A meldet sich bei B an“, E2E für den ganzen Kern-Anwendungsfall +- Vorgezogen: #018 Zielkonto-Skripte (Integrationstests), #019 Vorlagen-Logik, #020 Verbindungstest +- Stand: 410 Unit-/DB-Tests, 30 Integrationstests, 12 E2E-Abläufe grün + +### Entscheidungen +- **Phase 3 teilweise vorgezogen,** weil Florian „weiter“ sagte, während der Agent noch an Phase 2 arbeitete – nur Teile ohne Schemaänderung, damit die Migrationen des Agenten nicht kollidieren. +- **Trockenlauf und Ausrollen als zwei Vorgänge mit Hash-Sperre:** Es wird nie etwas anderes geschrieben, als jemand gesehen hat. +- **Private-Key-Ziele im Dry-Run nur für Admins:** Sonst hätte ein Operator einen vorbereiteten Private Key ausrollen lassen können (Freigabe Frage 3). +- **Keine Sicherungskopie alter Private Keys** auf dem Host; config-Block am Dateianfang, weil ssh den ersten Treffer nimmt. +- **Ein Hook `useVorgang` für alle:** zwei parallel entstandene Fassungen zusammengeführt, beide Schnittstellen bleiben. diff --git a/docs/issues/016-public-key-ausrollen.md b/docs/issues/016-public-key-ausrollen.md index c66cbfc..187638a 100644 --- a/docs/issues/016-public-key-ausrollen.md +++ b/docs/issues/016-public-key-ausrollen.md @@ -1,21 +1,27 @@ # #016 Public Key ausrollen mit Dry-Run und Backup -**Status:** offen +**Status:** erledigt **Meilenstein:** v0.2.0 **Labels:** backend, ui -**Geschätzt:** 4 h **Tatsächlich:** – +**Geschätzt:** 4 h **Tatsächlich:** 0 h 40 min ## Ziel Zuweisungen Schlüssel × Host/Gruppe × Benutzer; Dry-Run-Diff, Backup, atomares Schreiben, Rechte. ## Aufgaben -- [ ] Remote-Skript -- [ ] Optimistische Sperre über Datei-Hash -- [ ] Diff-Box im Schlüsseldetail +- [x] Remote-Skript +- [x] Optimistische Sperre über Datei-Hash +- [x] Diff-Box im Schlüsseldetail ## Akzeptanzkriterien -- [ ] Integrationstest Ausrollen/Entfernen +- [x] Integrationstest Ausrollen/Entfernen ## Abhängigkeiten #015, #010 ## Notizen / Entscheidungen +- **Zwei Schritte:** TROCKENLAUF hält je Ziel Diff, Hinweise und die Hashes vorher/nachher fest; AUSROLLEN führt genau diesen Plan aus und bricht je Ziel ab, wenn sich Datei **oder** Soll geändert hat. Ein Trockenlauf gilt 15 Minuten und nur einmal. +- **Auf dem Host** alles als Zielbenutzer (`runuser`), Hash dort erneut geprüft, Sicherung (letzte 5 bleiben), Temp-Datei + `mv`, 0700/0600. +- **Nur gültiges UTF-8** wird bearbeitet, sonst ginge „bytegenau“ verloren. +- **Soll** = direkte und Gruppen-Zuweisungen; der Deployment-Key gehört immer zum Soll des Anmeldebenutzers (Aussperrschutz aus #015). +- Gruppen-Verwendungen: Zustand je Verwendung, Fehlschlag auf einem Host gewinnt. Zustand je Host folgt mit dem Abgleich (#021). +- Oberfläche: Zuweisen auf der Schlüsselseite, Dry-Run je Schlüssel und je Host. diff --git a/docs/issues/017-private-key-ausrollen.md b/docs/issues/017-private-key-ausrollen.md index 3f7847d..9a96962 100644 --- a/docs/issues/017-private-key-ausrollen.md +++ b/docs/issues/017-private-key-ausrollen.md @@ -1,21 +1,27 @@ # #017 Private Key ausrollen und ~/.ssh/config -**Status:** offen +**Status:** erledigt **Meilenstein:** v0.3.0 **Labels:** backend, ui -**Geschätzt:** 4 h **Tatsächlich:** – +**Geschätzt:** 4 h **Tatsächlich:** 0 h 45 min ## Ziel Private Key auf Zielhost/-benutzer/-pfad, verwalteter Block in `~/.ssh/config`, `known_hosts`. ## Aufgaben -- [ ] Übertragung nur über stdin -- [ ] Warnung bei Passphrase -- [ ] nur Admin +- [x] Übertragung nur über stdin +- [x] Warnung bei Passphrase +- [x] nur Admin ## Akzeptanzkriterien -- [ ] Integrationstest +- [x] Integrationstest ## Abhängigkeiten #016 ## Notizen / Entscheidungen +- **Dateien je Ziel:** Private Key (0600, keine Sicherungskopie), `.pub` (0644), verwalteter Block **am Anfang** von `~/.ssh/config` (ssh nimmt den ersten Treffer), verwalteter Block in `known_hosts` mit den vertrauten Host-Keys des Ziels unter dem HostName/Port aus der config. +- **Nie fremde Dateien überschreiben:** Liegt am Zielpfad schon etwas anderes, scheitert das Ziel im Trockenlauf. +- **Plan ohne Inhalt:** Der Trockenlauf zeigt beim Private Key nur die Aktion; weder Vorgang noch Protokoll noch Browser sehen den Schlüssel. +- **Nur Admins:** Zuweisen verlangt `privateKeyAusrollen`; der Trockenlauf nimmt Private-Key-Ziele nur für Admins auf. +- **Widerruf:** löschen oder umbenennen (`…sshkv-widerrufen.`), config- und known_hosts-Einträge verschwinden mit. +- Integrationstest: A meldet sich per Alias bei B an (StrictHostKeyChecking=yes). E2E im Browser für den ganzen Kern-Anwendungsfall. diff --git a/package.json b/package.json index 9612b07..3996aac 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sshkv", - "version": "0.1.0", + "version": "0.2.0", "private": true, "type": "module", "description": "SSHKeyVault – zentrale Verwaltung von SSH-Schluesseln mit Weboberflaeche",