Files
florian.wildeandClaude Opus 5.5 f6ed66fc7e feat(rotation): gefuehrte Rotation mit Test vor dem Widerruf (#023)
Einen Schlüssel zu tauschen soll keinen Ausfall riskieren: Der Nachfolger
übernimmt alle aktiven Zuweisungen, wird ausgerollt und per
Verbindungstest geprüft – erst wenn alle Tests seit dem letzten Ausrollen
grün sind, entfernt der Widerruf aus #024 den alten Schlüssel, der dann
als ROTIERT endet.

Private Keys bekommen einen neuen Pfad (-rJJJJMMTT) bei gleichem Alias.
Damit ssh den neuen nimmt, schreibt privat.ts je Alias nur noch einen
config-Eintrag; die jüngste Zuweisung gewinnt. Der Worker setzt
vorgaengerId schon beim Erzeugen, so ist die Kette auch nach einem
Abbruch vollständig und die Schrittfolge wiederaufnehmbar.

Personen-Keys ohne Private Key in sshkv bekommen nur einen Hinweis:
dafür braucht es einen neuen Public Key von der Person.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 13:30:39 +02:00

2.9 KiB
Raw Permalink Blame History

#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

  • „Rotieren“ auf der Schlüsselseite, Schrittfolge /schluessel/[id]/rotieren (wiederaufnehmbar)
  • Neuer Schlüssel gleichen Typs, Name „… (rotiert TT.MM.JJJJ)“, vorgaengerId = alter
  • Zuweisungen übernehmen: Public gleiche Ziele/Optionen, Private gleicher Host/Benutzer/Alias mit neuem Pfad
  • Dry-Run + Ausrollen, Verbindungstests, erst dann den alten widerrufen (#024) → ROTIERT
  • Personen-Key ohne Private Key: nur Hinweis „neuer Public Key nötig“
  • Rotationskette auf der Schlüsselseite (Vorgänger/Nachfolger verlinkt)

Akzeptanzkriterien

  • DB-Tests: Schritte, Reihenfolge, Rechte, Namen/Pfade (tests/rotation.test.ts)
  • Integrationstest: neuer Schlüssel funktioniert, alter von beiden Hosts weg, ssh <alias> geht weiter
  • 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 <alias> nach dem Ausrollen auf die neue Datei, während die alte bis zum Widerruf daneben liegt.
  • Neuer Pfad: <alter Pfad ohne -rJJJJMMTT>-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).