diff --git a/README.md b/README.md index 122d39b..8327c39 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ Notfallplan) und [`docs/betrieb/sicherung.md`](docs/betrieb/sicherung.md). - [Installation und Betrieb](docs/betrieb/installation.md) – Docker Compose, authentik, Reverse Proxy - [Sicherung und Wiederherstellung](docs/betrieb/sicherung.md) – `deploy/sichern.sh`, `deploy/wiederherstellen.sh` - [Sicherheit](docs/betrieb/sicherheit.md) – Aufbau, Bedrohungsanalyse, Notfallplan +- [Abnahme vor 1.0.0](docs/betrieb/abnahme.md) – Prüfliste für authentik, echte Hosts und Betrieb ## Installation (Docker Compose) diff --git a/docs/betrieb/abnahme.md b/docs/betrieb/abnahme.md new file mode 100644 index 0000000..eafb869 --- /dev/null +++ b/docs/betrieb/abnahme.md @@ -0,0 +1,130 @@ +# 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](installation.md) ist durch, `docker compose ps` +zeigt `db`, `web` und `worker` als *healthy*. + +**Abnahme durch:** ____________________ **Datum:** __________ **Version/Commit:** __________ + +--- + +## A · Installation und Grundschutz + +- [ ] `https:///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:///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). + +## 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 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](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 diff --git a/docs/issues/034-abnahme-checkliste.md b/docs/issues/034-abnahme-checkliste.md new file mode 100644 index 0000000..04bd68a --- /dev/null +++ b/docs/issues/034-abnahme-checkliste.md @@ -0,0 +1,23 @@ +# #034 Abnahme-Checkliste für 1.0.0 +**Status:** erledigt +**Meilenstein:** v1.0.0 +**Labels:** docs +**Geschätzt:** 0,5 h **Tatsächlich:** 0,25 h + +## Ziel +Was die automatischen Tests nicht abdecken – echter authentik, echte Hosts, Betrieb auf dem Zielserver – als abhakbare Liste mit erwartetem Ergebnis je Punkt, damit die Abnahme von 1.0.0 vollständig und nachvollziehbar ist. + +## Aufgaben +- [x] `docs/betrieb/abnahme.md`: Installation, authentik, Notfall-Admin, Hosts, Ausrollen, Drift/Rotation/Widerruf, Audit-Log/Benachrichtigungen, Sicherung, Oberfläche +- [x] Aussagen gegen den Code geprüft (Knopfnamen, Healthcheck, Ratenbegrenzung, Header) +- [x] Verlinkt im README + +## Akzeptanzkriterien +- [x] Jeder Punkt nennt ein prüfbares Ergebnis +- [x] Die kritischen offenen Punkte (authentik-Neuanmeldung mit `auth_time`, Byte-Gleichheit außerhalb des Blocks auf echten Hosts) sind ausdrücklich enthalten + +## Abhängigkeiten +#029 + +## Notizen / Entscheidungen +- Die Abnahme selbst macht der Betreiber; 1.0.0 wird erst danach getaggt.