# #023 Geführte Rotation **Status:** erledigt **Meilenstein:** v0.4.0 **Labels:** backend, ui **Geschätzt:** 3 h **Tatsächlich:** 0 h 20 min ## Ziel Neuer Schlüssel an allen Verwendungen, Test, alter entfernt. ## Aufgaben - [x] „Rotieren“ auf der Schlüsselseite, Schrittfolge `/schluessel/[id]/rotieren` (wiederaufnehmbar) - [x] Neuer Schlüssel gleichen Typs, Name „… (rotiert TT.MM.JJJJ)“, `vorgaengerId` = alter - [x] Zuweisungen übernehmen: Public gleiche Ziele/Optionen, Private gleicher Host/Benutzer/Alias mit neuem Pfad - [x] Dry-Run + Ausrollen, Verbindungstests, erst dann den alten widerrufen (#024) → `ROTIERT` - [x] Personen-Key ohne Private Key: nur Hinweis „neuer Public Key nötig“ - [x] Rotationskette auf der Schlüsselseite (Vorgänger/Nachfolger verlinkt) ## Akzeptanzkriterien - [x] DB-Tests: Schritte, Reihenfolge, Rechte, Namen/Pfade (`tests/rotation.test.ts`) - [x] Integrationstest: neuer Schlüssel funktioniert, alter von beiden Hosts weg, `ssh ` geht weiter - [x] E2E im Browser (`tests/e2e/rotation.spec.ts`), Screenshot `rotation-desktop.png` ## Abhängigkeiten #020, #024 ## Notizen / Entscheidungen - **Fünf Schritte:** Neuer Schlüssel → Zuweisungen übernehmen → Dry-Run & Ausrollen → Verbindungstests → Alten widerrufen. Der aktuelle Schritt wird aus der Datenbank errechnet (`rotationsStand`), deshalb lässt sich die Rotation jederzeit neu laden und fortsetzen; jeder Schritt ist idempotent. - **Kette sofort:** Der Worker setzt `vorgaengerId` schon beim Erzeugen (neues Feld in der Vorgangs-Eingabe) – auch ein abgebrochener Browser hinterlässt keinen verwaisten Schlüssel. `vorgaengerId` ist eindeutig: zwei gleichzeitige Rotationen scheitern verständlich. - **Gleicher Alias, zwei Zuweisungen:** `privat.ts` schreibt je Alias genau einen config-Eintrag, die jüngste Zuweisung gewinnt (an der Position der ersten). So zeigt `ssh ` nach dem Ausrollen auf die neue Datei, während die alte bis zum Widerruf daneben liegt. - **Neuer Pfad:** `-rJJJJMMTT`, bei Kollision mit laufender Nummer – auch entfernte Zuweisungen belegen einen Pfad (eindeutiger Index über alle Zustände). - **Grün heißt aktuell:** Der alte Schlüssel wird nur widerrufen, wenn für jede Private-Zuweisung mit Alias ein erfolgreicher `VERBINDUNGSTEST` nach dem letzten Ausrollen vorliegt (Server prüft das). Ohne Private-Zuweisung mit Alias entfällt der Test. - **Abschluss:** Widerruf aus #024 ohne Zielkonten (der Nachfolger nutzt sie weiter); ein aktiver Nachfolger macht aus `WIDERRUFEN` den Status `ROTIERT`. - Ablaufdatum: Der Nachfolger erbt es nur, wenn es in der Zukunft liegt. Eine Passphrase wird nicht übernommen (sshkv erzeugt ohne). - Rechte: `schluesselVerwalten` + `publicKeyAusrollen`, bei Private-Zuweisungen `privateKeyAusrollen`. Deployment-Key: eigene Rotation (später).