Files
sshkeyvault/docs/betrieb/abnahme.md
T
florian.wildeandClaude Opus 5.5 4219701b29 feat(anmeldung): lokale Benutzerkonten mit Pflicht-TOTP (#035)
Ohne authentik gab es nur den Notfall-Admin – ein geteiltes Konto ohne
Rollen und ohne erkennbare Akteure im Audit-Log. Admins legen jetzt
lokale Konten an; sshkv erzeugt ein Einmal-Passwort, das bei der ersten
Anmeldung ersetzt wird, danach ist TOTP Pflicht.

Rollen, Sperren (beendet Sitzungen sofort) und Zurücksetzen unter
Einstellungen → Benutzer, nur für Admins mit frischer Bestätigung.
SSHKV_LOKALE_KONTEN=0 schaltet lokale Konten ab; der Notfall-Admin
bleibt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 22:36:25 +02:00

8.2 KiB
Raw Blame History

Abnahme vor Version 1.0.0

Was die automatischen Tests nicht abdecken können: der echte Anbieter (authentik), echte Hosts und der Betrieb auf dem Zielserver. Diese Liste geht genau das durch. Jeder Punkt nennt das erwartete Ergebnis; was abweicht, kommt als Issue in docs/issues/ (oder direkt als Notiz hier darunter).

Voraussetzung: Installation nach installation.md ist durch, docker compose ps zeigt db, web und worker als healthy.

Abnahme durch: ____________________ Datum: __________ Version/Commit: __________


A · Installation und Grundschutz

  • https://<sshkv>/api/gesundheit antwortet mit {"status":"ok","version":…}; über http:// gibt es keine Anmeldung (Secure-Cookie).
  • Von außerhalb des internen Netzes ist die Oberfläche nicht erreichbar (Reverse Proxy / Firewall liefert 403 oder Timeout).
  • Der Web-Container sieht den Master-Key nicht: docker compose exec web ls /run/secrets → leer bzw. Fehler.
  • Worker-Log beim Start: Master-Key passt, Deployment-Key vorhanden (docker compose logs worker | head).
  • Antwort-Header prüfen (curl -sI https://<sshkv>/anmelden): Content-Security-Policy, Strict-Transport-Security, X-Frame-Options/frame-ancestors, Referrer-Policy.

B · Anmeldung mit authentik

  • Anmelden mit einem Konto in sshkv-admins → Rolle Admin (unten links in der Navigation).
  • Konto in sshkv-operators → Operator; Konto in sshkv-readers → Leser.
  • Konto in keiner dieser Gruppen → kein Zugang, verständliche Meldung, Eintrag anmeldung.verweigert im Audit-Log.
  • Gruppe in authentik entziehen → bei der nächsten Anfrage gilt die neue Rolle bzw. kein Zugang (die Rolle kommt aus der Datenbank, nicht aus dem alten Token – spätestens nach erneuter Anmeldung).
  • Erneute Bestätigung: als Admin „Private Key anzeigen“ wählen → authentik fragt das Passwort (und ggf. MFA) wirklich neu ab; danach geht die Aktion fünf Minuten lang ohne erneute Frage. Kritischer Punkt: Kommt authentik ohne neue Eingabe zurück, meldet sshkv „auth_time fehlt oder ist zu alt“ – dann die Flow-Einstellung in authentik prüfen.
  • Abmelden beendet die Sitzung (Zurück-Knopf zeigt keine Daten mehr).

B2 · Lokale Konten (wenn ohne oder neben authentik)

  • Einstellungen → „Konten verwalten“ → Konto anlegen (frische Bestätigung nötig) → das Einmal-Passwort erscheint genau einmal; nach Neuladen ist es weg.
  • Erste Anmeldung im privaten Browserfenster: eigenes Passwort setzen (unter 12 Zeichen wird abgelehnt), TOTP einrichten, Rolle stimmt (unten links „über lokales Konto“).
  • Sperren → die Sitzung im anderen Fenster endet beim nächsten Klick; Entsperren → wieder anmeldbar.
  • Zurücksetzen → neues Einmal-Passwort, TOTP wird neu eingerichtet.
  • Audit-Log enthält Anlegen, Passwort gesetzt, Sperren – kein Passwort im Klartext.
  • Mit SSHKV_LOKALE_KONTEN=0 und docker compose up -d: lokales Konto kommt nicht mehr hinein, der Notfall-Admin schon.

C · Notfall-Admin

  • /anmelden → „Notfallzugang“: Benutzer notfall + Passwort aus .env → TOTP-Einrichtung erzwungen (QR-Code mit Authenticator-App scannen).
  • Erneute Anmeldung verlangt Passwort und TOTP-Code; falscher Code wird abgelehnt.
  • Nach fünf Fehlversuchen in 15 Minuten → „Zu viele Fehlversuche“, auch mit richtigem Passwort.
  • SSHKV_BREAKGLASS_PASSWORD danach aus der .env entfernen (der Hash steht in der Datenbank) und das Passwort im Passwortmanager ablegen.

D · Echte Hosts aufnehmen

Mindestens zwei Hosts, möglichst unterschiedlich (z. B. Debian 12 und Ubuntu 24.04), einer mit Anmeldeart Dienstbenutzer, einer mit root.

  • Host anlegen → „Host-Key abrufen“ → Fingerprint mit dem Host vergleichen (ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub auf dem Host) → vertrauen.
  • Bootstrap per Einmal-Passwort: danach existiert sshkv mit /etc/sudoers.d/sshkv, das Passwort steht nirgends (Audit-Log, docker compose logs, Datenbank: grep -r im Dump findet es nicht).
  • Bootstrap per Kopier-Befehl auf dem zweiten Host: Befehl in die Root-Shell einfügen, danach „Einrichtung erneut prüfen“ → eingerichtet.
  • „Verbindung prüfen“ → erreichbar; sshd-Befund wird angezeigt (z. B. PasswordAuthentication).
  • Host-Key auf einem Host absichtlich tauschen (ssh-keygen -A nach Löschen der Keys, sshd neu starten) → nächster Abgleich meldet Host-Key geändert, sshkv verbindet sich nicht mehr, bis ein Admin den neuen Key bestätigt. Danach zurückdrehen.

E · Schlüssel ausrollen (Kern-Anwendungsfall)

  • Vorhandene authorized_keys eines Hosts mit eigenen Zeilen (Kommentare, Optionen, leere Zeilen) vorher sichern: cp ~/.ssh/authorized_keys /tmp/vorher.
  • Public Key eines Admins auf beide Hosts ausrollen → Dry-Run zeigt nur den verwalteten Block; nach „Anwenden“ ist alles außerhalb des Blocks byte-gleich (diff gegen /tmp/vorher zeigt nur den Block).
  • Backup-Datei authorized_keys.sshkv.bak.* liegt daneben; Rechte 0600, Verzeichnis 0700.
  • Host-zu-Host-Assistent: Vorlage „Deployment-Zugang“ von Host A nach Host B → Verbindungstest grün; auf A: ssh <alias> id -un funktioniert ohne Rückfrage (known_hosts gesetzt).
  • Datei ~/.ssh/config auf A: eigener Inhalt außerhalb des sshkv-Blocks unverändert.
  • Zielkonto anlegen (z. B. deploy mit sudo) → Benutzer und sudoers-Datei entstehen, visudo -c auf dem Host ist sauber.

F · Drift, Rotation, Widerruf

  • Im verwalteten Block auf einem Host von Hand eine Zeile ändern → Abgleich meldet Abweichung; Übersicht zeigt den Host; Dry-Run stellt den Soll-Stand wieder her.
  • Fremden Key außerhalb des Blocks eintragen → Abgleich meldet unbekannten Schlüssel; „übernehmen“ funktioniert.
  • Geführte Rotation des Assistenten-Schlüssels: Alias auf A funktioniert vorher, während und nachher; alte Datei ist weg.
  • Rotation einmal abbrechen und danach erneut rotieren (#032, #033).
  • Widerruf eines Schlüssels mit einem nicht erreichbaren Host (Host kurz stoppen) → „Widerruf unvollständig“; nach dem Wiederanlauf abschließbar.
  • Deployment-Key rotieren (Einstellungen → Instanz) über alle echten Hosts – danach klappen Abgleich und Ausrollen weiter; der alte Fingerprint steht auf keinem Host mehr.
  • Aussperrschutz: Den Deployment-Key auf einem Host per Hand aus dem Block löschen und ausrollen → sshkv schreibt ihn zurück bzw. verweigert eine Änderung, die ihn entfernen würde.

G · Audit-Log und Benachrichtigungen

  • Audit-Log zeigt alle Aktionen aus D–F mit Akteur; „Kette prüfen“ → intakt.
  • CSV-Export öffnet sich sauber in LibreOffice/Excel (Umlaute, Trennzeichen).
  • Syslog-Kopie kommt beim Zielserver an (falls SSHKV_SYSLOG_* gesetzt).
  • Testversand E-Mail (Microsoft 365) und Webhook aus den Einstellungen kommt an.
  • Am nächsten Morgen: tägliche Zusammenfassung ist da und nennt die offenen Befunde.

H · Sicherung und Wiederherstellung

  • sudo bash deploy/sichern.sh läuft durch; Sicherung landet am vorgesehenen Ort.
  • Master-Key getrennt gesichert (Passwortmanager/Tresor), Wiederauffinden geprobt.
  • Wiederherstellung auf einer Wegwerf-Instanz nach sicherung.md: danach Anmeldung, Private Key anzeigen und ein Abgleich funktionieren.
  • Mit falschem Master-Key verweigert der Worker den Start.

I · Oberfläche

  • Smartphone (schmale Ansicht): Navigation, Übersicht, Hostliste und Dry-Run bedienbar.
  • Hell/dunkel: Die App ist bewusst dunkel (Tresor-Design); Kontrast in heller Umgebung ausreichend lesbar.
  • Handbuch (docs/anleitung/README.md) passt zu dem, was die Oberfläche zeigt.

Ergebnis

  • Alle Punkte erledigt oder als Issue erfasst und als nicht blockierend eingestuft.
  • Freigabe für 1.0.0 – danach: Version in package.json, CHANGELOG-Abschnitt [1.0.0], Tag v1.0.0, Push.

Notizen