diff --git a/docs/DISASTER_RECOVERY.md b/docs/DISASTER_RECOVERY.md index 222aa07..53e5b45 100644 --- a/docs/DISASTER_RECOVERY.md +++ b/docs/DISASTER_RECOVERY.md @@ -8,7 +8,7 @@ Verwandte Dokumente: - `docs/ROLLBACK.md` - Rueckweg bei Fehlern im laufenden GitOps-Betrieb - `docs/RESTORE_MATRIX.md` - Restore-Quellen und Verifikationsregeln pro Dienst -- `docs/RESTORE_HANDBOOK.md` - praktische Restore-Betriebsanleitung +- `ops/restore-tests/README.md` - Restore-Test-Betrieb und Werkzeuge - `docs/SERVICES_RECOVERY.md` - Recovery-kritische `/mnt/user/services`-Pfade, Gitea-Mirror und Komodo-Bootstrap - `docs/EXTERNAL_DEPENDENCIES.md` - externe Provider/Konten und Ausfall-Szenarien - `ops/borg-ui/BACKUP_SCOPE.md` - Zielbild des Borg-Scopes @@ -565,15 +565,14 @@ und physisch ausserhalb des Rechners abgelegt sein. --- -## 11. Offene Vorbereitungs-To-dos +## 11. Laufende Vorbereitung -- Unraid-USB-/Flash-Backup regelmaessig ueber `unraid-flash-config.tar.gz` und optional Unraid Connect pruefen -- Borg-Passphrase ist laut Operator-Bestaetigung vom 2026-05-26 extern/offline hinterlegt; bei Reviews nur Existenz/Lesbarkeit der Offline-Kopie pruefen, nie den Wert dokumentieren -- Komodo Stack-ENV-Werte zentral ausserhalb von Komodo dokumentieren -- regelmaessige automatisierte Restore-Smoke-Tests fuer Vaultwarden, Gitea und Paperless etablieren +Offene Punkte werden in `docs/MASTER_TODO.md` gefuehrt. Daueraufgaben: + +- Unraid-Flash-Artefakt regelmaessig pruefen (`ops/maintenance/check-unraid-flash-backup.sh`) +- Offline-Kopien (Borg-Passphrase, KOMODO_*-Notiz, DR-Keys) bei Reviews nur auf Auffindbarkeit pruefen, nie Werte dokumentieren - `komodo-mongo`-Dump nach Major-Upgrades gezielt kontrollieren -- `baerchen` Recovery-USB-Boot-/SMB-Test nach erfolgreichem erstem Full-Lauf - verifizieren +- Restore-Drills nach Kadenz aus `ops/restore-tests/schedule.md` rotieren --- diff --git a/docs/README.md b/docs/README.md index cbf2ffc..d57b24b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -20,7 +20,6 @@ Diese Datei trennt aktive Betriebsdokumentation von historischer Arbeitsdoku. Ne |---|---| | `DISASTER_RECOVERY.md` | Wiederanlauf nach Host-/Systemausfall | | `RESTORE_MATRIX.md` | Restore-Quellen, Dumps, Secrets und Smoke-Tests je Dienst | -| `RESTORE_HANDBOOK.md` | praktische Restore-Anleitung | | `SERVICES_RECOVERY.md` | Gitea-/Komodo-/Services-Bootstrap | | `ROLLBACK.md` | Rueckweg bei GitOps-/Deploy-Fehlern | | `GITOPS_DRIFT_RUNBOOK.md` | Pflichtmatrix bei Drift zwischen Git, Komodo, Docker und Host | diff --git a/docs/RESTORE_HANDBOOK.md b/docs/RESTORE_HANDBOOK.md deleted file mode 100644 index 18a0d64..0000000 --- a/docs/RESTORE_HANDBOOK.md +++ /dev/null @@ -1,250 +0,0 @@ -# Restore Handbook - KalliLab CORE - -Stand: 2026-06-03 - -Dieses Handbuch ist die praktische Betriebsanleitung fuer Restore-Checks und Restore-Lab in KalliLab CORE. - -Es ergaenzt: - -- `docs/RESTORE_MATRIX.md` -- `docs/DISASTER_RECOVERY.md` -- `ops/restore-tests/*` - ---- - -## 1. Ziel - -Dieses Handbuch beantwortet vier Fragen: - -1. Was ist die Restore-Quelle? -2. Wo wird getestet? -3. Wie pruefen wir Erfolg? -4. Wie machen wir das regelmaessig mit wenig Handarbeit? - ---- - -## 2. Grundmuster - -Alle validierten Restore-Tests folgen demselben Muster: - -- Quelle bleibt das produktive Borg-Repo bei Hetzner -- Borg-Zugriff laeuft ueber den vorhandenen `borg-ui`-Container -- Passphrase kommt aus `/mnt/user/appdata/secrets/borg_repo_passphrase.txt` -- Testdaten landen unter `/mnt/user/backups/restore-lab/` -- Reports landen unter `/mnt/user/backups/restore-reports` -- Testinstanzen laufen lokal ohne Traefik und ohne produktive Domain -- nach Erfolg werden Testcontainer und Testdaten wieder entfernt - ---- - -## 3. Bereits praktisch verifiziert - -### Vaultwarden - -- Erstlauf: 2026-05-07 -- Nachweis: Borg-Restore, Testcontainer, Login-Seite erreichbar - -### Gitea - -- Erstlauf: 2026-05-07 -- Nachweis: Borg-Restore, Web-UI, SSH-TCP-Port - -### Paperless - -- Erstlauf: 2026-05-07, Folgelauf: 2026-05-31 -- Nachweis: Borg-Datei-Restore, Dump-Import in Test-Postgres, Login-Seite, Doc-Count - -### Immich - -- Erstlauf: 2026-05-27 -- Nachweis: DB-Dump-Restore in VectorChord-Test-Postgres, HTTP-Smoke, Asset-Count -- Hinweis: Foto-Dateien-Restore ist bewusst nicht Teil des Smokes - -### Authelia - -- Erstlauf: 2026-06-03 -- Nachweis: Config-Borg-Restore, `authelia config validate`, HTTP-Health `/api/health` -- Hinweis: Daten-Restore des produktiven Dumps ist bewusst nicht Teil des Smokes (Storage-Encryption-Key-Kopplung) - -### Komodo Bootstrap - -- Erstlauf: 2026-05-30 -- Nachweis: Compose-Validierung, Mongo healthy, Core HTTP, Periphery running -- Hinweis: Daten-Restore aus `komodo-mongo.archive.gz` ist noch nicht getestet - ---- - -## 4. Verzeichnisstruktur - -### Produktiv - -- `/mnt/user/appdata` -- `/mnt/user/services` -- `/mnt/user/documents` -- `/mnt/user/backups/borg/dumps/latest` - -### Restore-Lab - -- `/mnt/user/backups/restore-lab/vaultwarden` -- `/mnt/user/backups/restore-lab/gitea` -- `/mnt/user/backups/restore-lab/paperless` -- `/mnt/user/backups/restore-lab/immich` -- `/mnt/user/backups/restore-lab/authelia` -- `/mnt/user/backups/restore-lab/komodo` -- `/mnt/user/backups/restore-lab/_failed` (Diagnose-Material bei Fehllaeufen) - -### Reports - -- `/mnt/user/backups/restore-reports` - ---- - -## 5. Restore-Frequenz - -- jeden Montag, 06:30: Frische-Check fuer Dumps und Reports -- 1. Samstag im Monat, 07:00: Vaultwarden -- 3. Samstag im Monat, 07:15: Gitea -- 2. Samstag in ungeraden Monaten, 08:00: Paperless -- 2. Sonntag in Feb/Mai/Aug/Nov, 08:30: Immich -- 2. Samstag in geraden Monaten, 07:30: Authelia -- 1. Kalendertag im Monat, 09:00: Zufaelliger Restore aus Pool - -Vollstaendiger Kalender mit Cron-Ausdruecken und Shell-Guards steht in `ops/restore-tests/schedule.md`. - ---- - -## 6. Betriebsmodus - -Stand 2026-06-03 ist der Betrieb auf V1+ (V1 mit ntfy): - -- validierte Bash-Host-Jobs fuer Vaultwarden, Gitea, Paperless, Immich, Authelia, Komodo-Bootstrap -- Host-Job-Definitionen und Cron-Vorlagen liegen im Repo (`ops/restore-tests/unraid-user-scripts.md`) -- `ntfy`-Wrapper sendet Erfolg an `homelab-info`, Fehler an `homelab-alerts` -- Frische-Check prueft zusaetzlich pg-Custom-Format-Dumps per `pg_restore --list` Header-Validierung -- bei Fehlschlag wird das Restore-Lab nach `_failed/` verschoben statt geloescht - -Noch geplant fuer V2: - -- Hermes-Zusammenfassung ueber vorhandene Reports -- Sammelreports und Report-Rotation -- weitere Dienste (Nextcloud, Mailarchiver, Mealie) - ---- - -## 7. User Script Jobs auf Unraid - -Die Vorlagen stehen in: - -- `ops/restore-tests/unraid-user-scripts.md` - -Host-Repo-Pfad: - -```text -/mnt/user/services/homelab-infra -``` - -Jobs: - -1. `restore-freshness-weekly` -2. `restore-vaultwarden-monthly` -3. `restore-gitea-monthly` -4. `restore-paperless-bimonthly` -5. `restore-immich-quarterly` -6. `restore-authelia-bimonthly` -7. `monthly-random-restore` - ---- - -## 8. Erfolgskriterien - -Ein Restore-Test gilt nur dann als erfolgreich, wenn: - -- Restore-Quelle lesbar war -- Daten im Restore-Lab ankamen -- Testcontainer startete -- Smoke-Test erfolgreich war -- Report geschrieben wurde - -Nur `Container laeuft` reicht nicht. - ---- - -## 9. Sicherheitsregeln - -- keine produktiven Pfade beschreiben -- keine produktiven Container fuer Restore-Tests verwenden -- keine produktiven Domains fuer Testinstanzen verwenden -- keine Secrets im Repo -- keine Restore-Automatik fuer neue Dienste ohne bewusste Freigabe - ---- - -## 10. Schnellstart - -### Frische-Check - -Auf dem Unraid-Host: - -```bash -bash /mnt/user/services/homelab-infra/ops/restore-tests/run-restore-checks.sh freshness -``` - -### Vaultwarden Restore-Check - -```bash -bash /mnt/user/services/homelab-infra/ops/restore-tests/run-restore-checks.sh vaultwarden -``` - -### Gitea Restore-Check - -```bash -bash /mnt/user/services/homelab-infra/ops/restore-tests/run-restore-checks.sh gitea -``` - -### Paperless Restore-Check - -```bash -bash /mnt/user/services/homelab-infra/ops/restore-tests/run-restore-checks.sh paperless -``` - -### Immich Restore-Check - -```bash -bash /mnt/user/services/homelab-infra/ops/restore-tests/run-restore-checks.sh immich -``` - -### Authelia Restore-Check - -```bash -bash /mnt/user/services/homelab-infra/ops/restore-tests/run-restore-checks.sh authelia -``` - -### Komodo Bootstrap Trockenlauf - -```bash -bash /mnt/user/services/homelab-infra/ops/restore-tests/run-restore-checks.sh komodo-bootstrap -``` - -### Optional mit `ntfy` - -```bash -bash /mnt/user/services/homelab-infra/ops/restore-tests/run-restore-job-with-ntfy.sh freshness homelab-info -``` - ---- - -## 11. Naechste Ausbaustufen - -1. Nextcloud-Restore-Test (mit `occ maintenance:mode`-Choreographie) -2. Mailarchiver-Restore-Test -3. Mealie-Restore-Test -4. Komodo-Mongo-Daten-Restore (echtes `mongorestore` statt reinem Bootstrap) -5. Shared-PostgreSQL-18-Cluster-Restore-Drill (globals + per-DB-Dumps) -6. Traefik-Restore-Test (mit `dynamic/` und LE-State) -7. Hermes-Zusammenfassung ueber vorhandene Reports -8. Report-Rotation (archivieren nach 12 Monaten) -9. Negativ-Test: bewusst kaputten Dump in den Frische-Check einfuettern - -## 12. Report-Aufbewahrung - -Reports unter `/mnt/user/backups/restore-reports` werden dauerhaft aufbewahrt. Bei wachsender Anzahl (ca. 50-60 pro Jahr) empfiehlt sich eine jaehrliche Archivierung alter Reports in einen Unterordner `_archive/YYYY/`. Der Frische-Check warnt bei `MAX_REPORT_AGE_DAYS=45`, loescht aber bewusst nicht automatisch. diff --git a/docs/RESTORE_MATRIX.md b/docs/RESTORE_MATRIX.md index ccd196b..3036473 100644 --- a/docs/RESTORE_MATRIX.md +++ b/docs/RESTORE_MATRIX.md @@ -170,173 +170,14 @@ wurden alle am 2026-06-03 abgeschlossen und sind in der Reifegrad-Tabelle belegt Verbleibende offene Restore-Pfade ohne vollstaendigen Test: -1. **Unraid OS Flash** - Artefakt-Validierung am 2026-06-05 erfolgreich (siehe Reifegrad-Tabelle und Runbook unten); offen bleibt nur der **physische Ersatzstick-Boot-Test**. -2. **Tailscale** - State-/Reconnect-Pfad dokumentiert testen +1. **Unraid OS Flash** - Artefakt-Validierung am 2026-06-05 erfolgreich (siehe Reifegrad-Tabelle und `ops/restore-tests/unraid-flash-runbook.md`); offen bleibt nur der **physische Ersatzstick-Boot-Test**. +2. **Tailscale** - State-/Reconnect-Pfad dokumentiert testen (`ops/restore-tests/tailscale-runbook.md`) --- -## Restore-Test-Runbooks (Entwurf) +## Restore-Test-Runbooks -Diese Abschnitte sind vorbereitete Checklisten fuer die noch untesteten Restore-Pfade. -Sie sind **nicht** als produktive Anleitungen zu verwenden, bevor ein erster Testlauf -die konkreten Artefaktnamen und Pfade bestaetigt hat. - -### Unraid OS Flash - -**Voraussetzungen:** -- Borg-Artefakt `unraid-flash-config.tar.gz` und `unraid-flash-config.tar.gz.sha256` unter `/mnt/user/backups/borg/dumps/latest` oder im Hetzner-Borg-Repo verfuegbar -- Neuer leerer USB-Stick (Empfehlung: 16 GB, USB 2.0 kompatibel) -- Unraid USB Flash Creator oder manueller Restore-Pfad -- Offline-gesicherte Borg-Passphrase verfuegbar - -**Checkliste Artefakt-Validierung (ohne produktiven Stick):** - -Automatisiert via Repo-Skript `ops/maintenance/check-unraid-flash-backup.sh` -(read-only, keine Extraktion). Manuelle Einzelschritte: - -1. SHA256-Pruefung: `sha256sum -c unraid-flash-config.tar.gz.sha256` -2. Artefakt-Inhalt pruefen: `tar -tzf unraid-flash-config.tar.gz | head -40` — erwartet `config/` als Prefix -3. Kern-Configs vorhanden: `super.dat`, `disk.cfg`, `ident.cfg`, `share.cfg`, `network.cfg`, `docker.cfg`, `go`, `domain.cfg` -4. Keine produktiven Konfigurationspfade (z. B. `config/ssh/`) ausserhalb des Test-Environments extrahieren -5. Manifest-Datei auf Vollstaendigkeit pruefen - -**Validierungsergebnis 2026-06-05 (read-only per SSH):** Artefakt frisch -(2026-06-05 04:00, ~16 h alt beim Test), `sha256sum -c` = OK, 390 Eintraege, -alle 8 Kern-Configs vorhanden. Das Archiv enthaelt erwartungsgemaess -Secret-Material (SSH-Host-Keys, Tailscale-State, `passwd`/`shadow`/`smbpasswd`, -`Trial.key`) und ist wie Secret-Backup zu behandeln. Es wurde nichts extrahiert, -nur Eintragsnamen gelistet. Offen bleibt der physische Ersatzstick-Boot-Test. - -**Checkliste vollstaendiger Restore-Test (auf Wegwerf-Stick):** - -1. Neuen USB-Stick mit Unraid USB Flash Creator formatieren und Basis-Unraid draufspielen -2. `config/`-Verzeichnis aus `unraid-flash-config.tar.gz` in den `/boot/config`-Pfad des neuen Sticks extrahieren -3. Im Testrahmen booten (kein Array starten, keine Shares mounten) -4. Pruefen: Unraid-Grundkonfiguration (Shares, Hostname, Netzwerk) ist sichtbar -5. Array-Zuordnung lesbar, ohne Drive-Assigns zu bestaetigen - -**Smoke-Test-Kriterium:** Unraid bootet, Hostname ist `Kallilabcore`, Share-Konfiguration ist sichtbar, kein Array gestartet. - -**Sonderregel:** Das Artefakt enthaelt Host-Konfiguration und SSH-Keys und ist wie Secret-Material zu behandeln. Nicht auf oeffentlichen oder unverschluesselten Testzielen extrahieren. - ---- - -### AdGuard Home - -**Validierungsergebnis 2026-06-06:** Automatisierter Test -`ops/restore-tests/adguard-restore-test.sh` auf Unraid erfolgreich ausgefuehrt. -Report: `/mnt/user/backups/restore-reports/adguard-2026-06-06.md`. -Getestet wurden Borg-Extract der Config, `AdGuardHome.yaml`-Struktur, -isolierter Testcontainer `restoretest-adguard` auf localhost-Ports, -HTTP `/control/status` = `401`, DNS-Smoke `git.kaleschke.info -> 192.168.178.58`, -7 Filterlisten-Eintraege. Testdaten wurden nach Erfolg bereinigt. - -**Voraussetzungen:** -- Borg-Archiv mit `/mnt/user/appdata/adguard/conf` zugaenglich (produktives Repo oder Teststand) -- Testpfad unter `/mnt/user/backups/restore-lab/adguard` vorbereitet -- Docker-Faehigkeit auf dem Testhost oder in der Restore-Lab-Umgebung - -**Automatisierter Test:** - -```bash -/mnt/user/services/homelab-infra/ops/restore-tests/run-restore-checks.sh adguard -``` - -**Manuelle Checkliste:** - -1. Borg-Extract des letzten Archivs nach `/mnt/user/backups/restore-lab/adguard/conf`: - ``` - borg extract ::ARCHIV /mnt/user/appdata/adguard/conf - ``` -2. Konfigurationsdatei `AdGuardHome.yaml` auf Vollstaendigkeit pruefen (YAML-Syntax valide) -3. Testcontainer starten (kein produktiver DNS-Port 53, stattdessen z. B. `15353`): - ```yaml - ports: - - "127.0.0.1:15353:53/udp" - - "127.0.0.1:13001:80/tcp" - volumes: - - /mnt/user/backups/restore-lab/adguard/conf:/opt/adguardhome/conf - ``` -4. `http://127.0.0.1:13001/control/status` erreichbar (`200`, `401` oder `403` sind fuer den Smoke ausreichend) -5. DNS-Aufloesung: `dig @127.0.0.1 -p 15353 git.kaleschke.info` gibt plausible Antwort -6. Testcontainer stoppen und Testpfad aufraeumen - -**Smoke-Test-Kriterium:** AdGuard-Web-UI laeuft, DNS-Aufloesung antwortet, Filterlisten sind geladen. - -**Keine Secrets:** AdGuard Home verwendet keine dokumentierten Repo-Secrets; Login-Credentials liegen in der `AdGuardHome.yaml` im Borg-Archiv. - ---- - -### Tailscale - -**Voraussetzungen:** -- Borg-Archiv mit `/mnt/user/appdata/tailscale` zugaenglich -- Testpfad unter `/mnt/user/backups/restore-lab/tailscale` vorbereitet -- Achtung: Der Tailscale-State ist maschinenspezifisch. Ein Restore auf denselben produktiven Host wuerde die laufende Verbindung verdraengen. Nur auf einem Wegwerf- oder Offline-Host testen. - -**Checkliste Artefakt-Validierung (ohne produktiven Host):** - -1. Borg-Extract nach `/mnt/user/backups/restore-lab/tailscale` -2. State-Verzeichnis auf erwartete Dateien pruefen: `tailscaled.state` vorhanden -3. Dateisystem-Rechte pruefen: `tailscaled.state` muss fuer `root` zugaenglich sein - -**Checkliste Reconnect-Test (auf Wegwerf-Host oder VM):** - -1. Tailscale-Container mit dem gemounteten State-Pfad starten -2. `tailscale status` zeigt `Connected` oder den erwarteten Hostnamen -3. Tailscale-Admin-Konsole (`login.tailscale.com`) zeigt Geraet als `Online` -4. SSH ueber Tailscale-IP auf den Testhost moeglich -5. Testcontainer stoppen; Wegwerf-Geraet in der Tailscale-Admin-Konsole entfernen - -**Smoke-Test-Kriterium:** Container verbindet sich mit bestehendem Tailscale-Account (kein neues Re-Auth noetig), Tailscale-IP ist erreichbar. - -**Hinweis:** Falls der State veraltet ist (Key expired), wird Tailscale einen Re-Auth anfordern. Das ist ein valides Testergebnis und belegt, wie lang der Reconnect-Pfad bei abgelaufenem Key ist. - ---- - -### Redis 8 (Shared) - -**Validierungsergebnis 2026-06-06:** Automatisierter Test -`ops/restore-tests/redis-restore-test.sh` auf Unraid erfolgreich ausgefuehrt. -Report: `/mnt/user/backups/restore-reports/redis-2026-06-06.md`. -Getestet wurde das Pre-Cutover-Artefakt -`/mnt/user/backups/borg/dumps/latest/shared-redis-pre-redis8-20260531-185011` -in einer isolierten Redis-8.8-Testinstanz auf `127.0.0.1:16379`. -Ergebnis: `PING` = `PONG`, `redis_version` = `8.8.0`, AOF aktiv (`1`), -`DBSIZE` = `1`. Produktiver Port und produktiver Datenpfad wurden nicht genutzt. - -**Voraussetzungen:** -- Pre-Cutover-Backup unter `/mnt/user/backups/borg/dumps/latest/shared-redis-pre-redis8-` vorhanden, oder Borg-Archiv mit `/mnt/user/appdata/redis` -- Secret-Datei `redis_password.txt` fuer Testinstanz verfuegbar (aus Borg, nicht als Wert dokumentieren) -- Testpfad unter `/mnt/user/backups/restore-lab/redis` vorbereitet - -**Automatisierter Test:** - -```bash -/mnt/user/services/homelab-infra/ops/restore-tests/run-restore-checks.sh redis -``` - -**Manuelle Checkliste:** - -1. RDB/AOF-Datei aus dem Backup in den Testpfad kopieren: - ``` - cp /mnt/user/backups/borg/dumps/latest/shared-redis-pre-redis8-/dump.rdb \ - /mnt/user/backups/restore-lab/redis/ - ``` - (oder Borg-Extract aus dem Appdata-Archiv) -2. Testcontainer starten (kein produktiver Port 6379, stattdessen z. B. `16379`): - ```yaml - ports: - - "127.0.0.1:16379:6379" - volumes: - - /mnt/user/backups/restore-lab/redis:/data - command: redis-server --requirepass --appendonly yes - ``` -3. Verbindungstest: `redis-cli -p 16379 -a PING` antwortet `PONG` -4. Redis-Version pruefen: `redis-cli -p 16379 -a INFO server | grep redis_version` zeigt `8.x` -5. Stichprobe Key-Bestand: `redis-cli -p 16379 -a DBSIZE` zeigt plausible Zahl (nicht 0) -6. Testcontainer stoppen und Testpfad aufraeumen - -**Smoke-Test-Kriterium:** Redis 8 startet mit dem Restore-Datenpfad, `PING` antwortet, `DBSIZE` ist nicht 0. - -**Shared Redis Besonderheit:** Shared Redis wird produktiv nur von Paperless genutzt (AOF aktiv). Bei einem echten Restore nach App-Absturz: Erst Redis aus Backup hochziehen, dann Paperless. Nextcloud hat eigene Redis-Instanz ohne Passwort. +Die Ablaeufe je Dienst liegen als Runbooks und automatisierte Skripte unter +`ops/restore-tests/` (Einstieg: `ops/restore-tests/README.md`). Fuer die noch +offenen Pfade: `ops/restore-tests/unraid-flash-runbook.md` und +`ops/restore-tests/tailscale-runbook.md`. \ No newline at end of file diff --git a/docs/SERVICES_RECOVERY.md b/docs/SERVICES_RECOVERY.md index 36e5310..9e826cd 100644 --- a/docs/SERVICES_RECOVERY.md +++ b/docs/SERVICES_RECOVERY.md @@ -142,8 +142,7 @@ Erst nach erfolgreichem Komodo-Bootstrap werden produktive Stacks ueber den doku Trockenlauf gegen Wegwerf-Pfade ist seit 2026-05-29 als Repo-Skript abgelegt: `ops/restore-tests/komodo-bootstrap-compose.test.yml`, -`ops/restore-tests/komodo-bootstrap-test.sh`, -`ops/restore-tests/komodo-bootstrap-plan.md` und +`ops/restore-tests/komodo-bootstrap-test.sh` und `ops/restore-tests/komodo-bootstrap-runbook.md`. Aufruf: ```bash @@ -203,13 +202,4 @@ Authoritativ ist `docs/SECRETS_MAP.md`. Fuer den Kaltstart ist diese Reihenfolge - Wenn Gitea und Komodo beide down sind, gewinnt der externe GitHub-Mirror als Repo-Quelle. - Wenn Borg ohne Passphrase nicht entschluesselbar ist, ist Recovery blockiert. Die Offline-Sicherung wurde am 2026-05-26 vom Operator bestaetigt; bei Reviews nur pruefen, dass sie weiterhin auffindbar und lesbar ist. -## Naechste Aufgaben - -| Status | Aufgabe | -|---|---| -| erledigt (Skript + Host-Test) | Gitea-Bundle- oder Mirror-Mechanik final entscheiden | -| erledigt | Komodo-Bootstrap-Quelle finalisieren | -| erledigt (Doku) | Komodo-Kaltstart in linearen Stufen A-F dokumentieren | -| erledigt 2026-05-29 | Komodo-Trockenlauf-Skript in `ops/restore-tests/` analog zu Immich vorbereiten | -| erledigt 2026-05-30 | Restore-Kommandos nach erstem Trockenlauf mit echten Pfaden ergaenzen | -| erledigt | Services-Recovery in `docs/DISASTER_RECOVERY.md` verlinken | +Offene Folgepunkte werden in `docs/MASTER_TODO.md` gefuehrt. diff --git a/ops/h-drive-nearline/README.md b/ops/h-drive-nearline/README.md new file mode 100644 index 0000000..641276c --- /dev/null +++ b/ops/h-drive-nearline/README.md @@ -0,0 +1,73 @@ +# H:/ Nearline-Backup — Struktur und Betrieb + +Stand: 2026-06-10 + +## Rolle der H:/ + +Die externe HDD (asmedia ASM235, 7.4 TB, Laufwerk `H:`) dient ausschließlich als +**Nearline-Backup-Spiegel** für kritische Dumps und Git-Bundles. + +Sie ist kein Primär-Backup (das ist Hetzner/Borg) und kein dauerhaftes Archiv. + +## Sollzustand + +``` +H:\ +└── kallilab-nearline-backups\ + ├── borg-dumps\latest\ ← aktuelle DB-Dumps (per Script) + ├── git-bundles\gitea\ ← Gitea-Repo-Bundles (per Script) + ├── _dr-kit\ ← SSH-Keys, Offline-Secrets (manuell) + ├── _logs\ ← Robocopy-Logs je Lauf + └── _reports\ ← Markdown-Reports je Lauf +``` + +Nichts weiteres gehört dauerhaft auf die H:/. +Temporäre Recovery- oder Backup-Ordner aus Notfallsituationen sind nach +Abschluss zu löschen. + +## Automatischer Pull + +`pull-critical-backups.ps1` zieht per Robocopy vom Unraid-SMB-Share: + +- `\\192.168.178.58\backups\borg\dumps\latest` → `borg-dumps\latest\` +- `\\192.168.178.58\backups\git-bundles\gitea` → `git-bundles\gitea\` + +Das Script schließt bewusst aus: +- `unraid-flash-config.tar.gz` (0600 root:root, nicht per SMB zugänglich → Restore aus Hetzner-Borg) +- Migration-/Cutover-Verzeichnisse (`immich-vectorchord-*`, `pg18-major-*`, `redis8-*` etc.) + +## _dr-kit + +Enthält offline hinterlegte Schlüssel und Secrets für den DR-Fall: +- `dr-hetzner` / `dr-hetzner.pub` — SSH-Key für Hetzner Storage Box +- `dr-readonly` / `dr-readonly.pub` — Read-only Deploy-Key +- `KOmodo Secrets.txt` — Komodo Stack ENV-Offline-Dokumentation + +Diese Dateien sind **manuell** zu pflegen und **nicht** vom Pull-Script verwaltet. + +## Archiv-Ordner + +Temporäre Notfall-Artefakte verbleiben als `_archiv-*`-Ordner bis zur bewussten +Löschentscheidung: + +| Ordner | Inhalt | Anlassdatum | +|---|---|---| +| `kallilab-recovery\_archiv-nvme-crash-image-2026-05-14\` | nvme0n1 Disk-Image (1863 GB) + Crash-Runbooks aus dem Mai-2026-Ausfall | 2026-05-14 | + +## Aufräum-Historie + +| Datum | Aktion | +|---|---| +| 2026-06-10 | `OneDrive - Stroetmann Group\` gelöscht (leer) | +| 2026-06-10 | SSH-Keys + Secrets aus nearline-Root in `_dr-kit\` verschoben | +| 2026-06-10 | Migration-Artefakt-Verzeichnisse in `borg-dumps\latest\` gelöscht (immich-vectorchord-*, pg18-major-*, redis8-*, nextcloud-redis-pre-redis8-*, shared-redis-pre-redis8-*) | +| 2026-06-10 | Pre-major-prod-Dumps gelöscht (PG17→PG18-Migration abgeschlossen) | +| 2026-06-10 | `kallilab-recovery\2026-05-15\` gelöscht (DNS-Restore-Reste) | +| 2026-06-10 | `kallilab-recovery\2026-05-14\` → `_archiv-nvme-crash-image-2026-05-14\` umbenannt | +| 2026-06-10 | `kallilab-recovery\disk1-phase2-2026-05-23\` gelöscht (1677 GB Media-Share-Kopie; Unraid-Share verifiziert vollständig) | + +## Offene Punkte + +- `Windows-Neuaufsetzen-Backup\` (48 GB): nach vollständiger Rückspielung auf D:\ löschen +- `_archiv-nvme-crash-image-2026-05-14\` (1863 GB): löschen sobald sicher, dass nichts mehr aus dem alten System benötigt wird +- Log-Rotation für `_logs\` und `_reports\`: manuell oder per Script, Empfehlung 30 Tage diff --git a/ops/restore-tests/README.md b/ops/restore-tests/README.md index ffbb5c3..034299a 100644 --- a/ops/restore-tests/README.md +++ b/ops/restore-tests/README.md @@ -1,109 +1,85 @@ -# Restore Tests +# Restore-Tests - Betrieb und Werkzeuge -Kontrollierte Restore-Tests fuer `homelab-infra`. +Typ: Runbook/Tool-Doku · Stand: 2026-06-11 · Status: aktiv -Ziel: +Kontrollierte Restore-Tests fuer `homelab-infra`. Dieses Dokument ist das +**einzige** Betriebsdokument fuer Restore-Tests (das fruehere +`docs/RESTORE_HANDBOOK.md` ist hierin aufgegangen). Verwandt: -- Backups durch echte Test-Restores verifizieren -- produktive Pfade nicht beschreiben -- Testlaeufe spaeter weitgehend automatisieren +- `docs/RESTORE_MATRIX.md` - Restore-Quellen, Secrets, Smoke-Tests und **Test-Reifegrad je Dienst** (einziger Status-Ort) +- `docs/DISASTER_RECOVERY.md` - echter Wiederanlauf +- `schedule.md` - Kadenz, Cron-Ausdruecke und Shell-Guards +- `unraid-user-scripts.md` - Unraid-User-Script-Vorlagen fuer die Host-Jobs ## Grundregeln -- Restore-Quelle bleibt im Backup-Bereich, z. B. `/mnt/user/backups/borg` -- Test-Restores laufen nur in `/mnt/user/backups/restore-lab` -- Reports landen in `/mnt/user/backups/restore-reports` -- Test-Container nutzen das Praefix `restoretest-` -- keine produktiven Volumes schreibend mounten -- keine produktiven Domains fuer Testinstanzen uebernehmen +- Restore-Quelle bleibt das produktive Borg-Repo bei Hetzner; Zugriff ueber den vorhandenen `borg-ui`-Container +- Passphrase kommt aus `/mnt/user/appdata/secrets/borg_repo_passphrase.txt`, nie aus UI-Interna +- Testdaten landen nur unter `/mnt/user/backups/restore-lab/`; bei Fehlschlag wird nach `_failed/` verschoben statt geloescht +- Reports landen unter `/mnt/user/backups/restore-reports` +- Testcontainer nutzen das Praefix `restoretest-`, localhost-Ports, keine produktive Domain, keine Traefik-Route +- keine produktiven Volumes schreibend mounten, keine produktiven Pfade beschreiben +- keine Restore-Automatik fuer neue Dienste ohne bewusste Freigabe -## Geplante Struktur +## Erfolgskriterien -- `schedule.md`: Intervalle und Verantwortlichkeiten -- `common.sh`: gemeinsame Helfer fuer Borg-Lookup, Borg-Extract und Compose-Cleanup; prueft vor Borg-Operationen auch `borg-ui:/data/borg.db` und `borg-ui:/local/secrets/borg_repo_passphrase.txt` -- `vaultwarden-restore-test.ps1`: erster Mini-Restore-Ablauf -- `vaultwarden-restore-test.sh`: hosttauglicher Vaultwarden-Restore-Job -- `vaultwarden-plan.md`: konkreter Vaultwarden-Testplan -- `vaultwarden-compose.test.yml`: isolierte Testinstanz fuer Vaultwarden -- `gitea-restore-test.ps1`: Gitea-Mini-Restore-Ablauf -- `gitea-restore-test.sh`: hosttauglicher Gitea-Restore-Job -- `gitea-plan.md`: konkreter Gitea-Testplan -- `gitea-compose.test.yml`: isolierte Testinstanz fuer Gitea -- `paperless-restore-test.ps1`: Paperless-Mini-Restore-Ablauf -- `paperless-restore-test.sh`: hosttauglicher Paperless-Restore-Job -- `paperless-plan.md`: konkreter Paperless-Testplan -- `paperless-compose.test.yml`: isolierte Testinstanz fuer Paperless inkl. Test-Postgres und Test-Redis -- `immich-restore-test.ps1`: Immich-Mini-Restore-Ablauf als Plan-/Windows-Scaffold -- `immich-restore-test.sh`: hosttauglicher Immich-Restore-Job, erster echter Lauf noch offen -- `immich-plan.md`: konkreter Immich-Testplan -- `immich-runbook.md`: Operator-Runbook fuer den ersten Immich-Lauf -- `immich-compose.test.yml`: isolierte Testinstanz fuer Immich inkl. VectorChord/pgvector-Test-Postgres und Test-Redis -- `authelia-restore-test.sh`: Authelia-Restore-Job (Config-Smoke; Erstlauf 2026-06-03 erfolgreich) -- `authelia-compose.test.yml`: isolierte Testinstanz fuer Authelia inkl. Test-Postgres, Filesystem-Notifier (kein echter SMTP-Versand) -- `authelia-plan.md`: konkreter Authelia-Testplan -- `authelia-runbook.md`: Operator-Runbook fuer den ersten Authelia-Lauf -- `adguard-restore-test.sh`: AdGuard-Home-Restore-Job (Config + isolierter Container + HTTP/DNS-Smoke; Erstlauf 2026-06-06 erfolgreich) -- `adguard-compose.test.yml`: isolierte AdGuard-Testinstanz auf localhost-Ports `13001` und `15353` -- `redis-restore-test.sh`: Redis-8-Restore-Job (Pre-Cutover-Artefakt + isolierter Container + PING/INFO/DBSIZE; Erstlauf 2026-06-06 erfolgreich) -- `redis-compose.test.yml`: isolierte Redis-8-Testinstanz auf localhost-Port `16379` -- `nextcloud-restore-test.sh`: Nextcloud-Restore-Job (Scaffold; **blockiert** durch Unraid shfs-chmod-Inkompatibilitaet - siehe unten) -- `nextcloud-compose.test.yml`: isolierte Testinstanz fuer Nextcloud inkl. Test-Postgres und Test-Redis +Ein Restore-Test gilt nur dann als erfolgreich, wenn Quelle lesbar war, Daten +im Restore-Lab ankamen, der Testcontainer startete, der **fachliche** +Smoke-Test gelang und ein Report geschrieben wurde. "Container laeuft" allein +reicht nicht. -- `check-restore-freshness.ps1`: woechentlicher Frische-Check fuer Dumps und Reports -- `run-restore-checks.ps1`: einfacher Dispatcher fuer Restore-Jobs -- `check-restore-freshness.sh`: hosttauglicher Frische-Check -- `negative-freshness-alert-test.sh`: sicherer Negativtest fuer den Frische-Alarmweg; nutzt synthetische leere Testpfade unter `/mnt/user/backups/restore-lab/freshness-negative`, veraendert keine produktiven Dumps und sendet bei erkanntem Fehler einen Test-Alert nach `homelab-alerts` -- `run-restore-checks.sh`: hosttauglicher Dispatcher -- `common.sh`: gemeinsame Host-Helferfunktionen -- `automation-plan.md`: Host-Job- und Automatisierungsmodell +## Aufbau des Verzeichnisses -## Automatisierungsmodell +Pro Dienst existieren bis zu drei Artefakte: -- Ausfuehrung: Unraid User Script / Host-Job -- Logik: Repo-Skripte in diesem Verzeichnis -- Ergebnis: Markdown-Report -- Meldung: `ntfy` -- Hermes: optional nur fuer Zusammenfassung und Auswertung +- `-restore-test.sh` - automatisierter Host-Job (produktive Wahrheit) +- `-compose.test.yml` - isolierte Testinstanz +- `-runbook.md` - manueller Ablauf bzw. Besonderheiten -Wichtig: +Dazu zentrale Helfer: -- die Bash-Skripte `*.sh` sind die produktive Host-Variante -- `check-restore-freshness.ps1` und die `*.ps1`-Dateien bleiben als lokale Plan-/Hilfsvariante nutzbar -- im Windows-Clone fehlen die `/mnt/user/...`-Pfade naturgemaess +- `run-restore-checks.sh` - Dispatcher (Host), `run-restore-checks.ps1` (lokale Planvariante) +- `run-restore-job-with-ntfy.sh` - Wrapper: Erfolg -> `homelab-info`, Fehler -> `homelab-alerts` +- `check-restore-freshness.sh` / `.ps1` - woechentlicher Frische-Check fuer Dumps und Reports (prueft pg-Dumps per `pg_restore --list`) +- `negative-freshness-alert-test.sh` - sicherer Negativtest des Alarmwegs (synthetischer Leerpfad, quartalsweise) +- `common.sh` - gemeinsame Borg-/Compose-Helfer +- `automation-plan.md` - Host-Job- und Automatisierungsmodell -## Validiertes Grundmuster +## Betriebsmodus -Stand nach dem ersten echten Vaultwarden-Test: +Stand 2026-06-11 ist der Betrieb auf V1+ (validierte Bash-Host-Jobs mit ntfy): -- Borg-Quelle bleibt das produktive Remote-Repo bei Hetzner -- Borg-Zugriff laeuft praktisch ueber den vorhandenen `borg-ui`-Container -- SSH-Trust wird ueber `known_hosts` im `borg-ui`-Container hergestellt -- die Borg-Passphrase kommt fuer Restore-Tests aus einer Host-Secret-Datei -- Restore-Ziel liegt immer getrennt unter `/mnt/user/backups/restore-lab` -- Reports liegen unter `/mnt/user/backups/restore-reports` -- Testinstanzen bekommen keine produktive Domain und keine Traefik-Route +- Host-Jobs laufen als Unraid User Scripts vom Repo-Spiegel `/mnt/user/services/homelab-infra` +- Kadenz und Cron-Ausdruecke: `schedule.md` (woechentlicher Frische-Check, monatliche/quartalsweise Dienst-Rotation, monatlicher Zufalls-Restore) +- Job-Vorlagen: `unraid-user-scripts.md` -Das ist das bevorzugte Muster fuer weitere dateibasierte Restore-Tests wie `gitea`. +## Schnellstart -Fuer datenbankgestuetzte Dienste wie `paperless` kommt zusaetzlich ein isolierter Dump-Restore in Test-Postgres dazu. +```bash +# Frische-Check +bash /mnt/user/services/homelab-infra/ops/restore-tests/run-restore-checks.sh freshness -## Status +# Dienst-Restore-Check (vaultwarden|gitea|paperless|immich|authelia|adguard|redis|komodo-bootstrap|nextcloud) +bash /mnt/user/services/homelab-infra/ops/restore-tests/run-restore-checks.sh -Aktuell ist das erste validierte Muster vorhanden. +# Negativtest des Alarmwegs (quartalsweise) +bash /mnt/user/services/homelab-infra/ops/restore-tests/run-restore-checks.sh freshness-negative -- echter Vaultwarden-Restore am 2026-05-07 erfolgreich verifiziert -- echter Gitea-Restore am 2026-05-07 erfolgreich verifiziert -- echter Paperless-Restore am 2026-05-07 erfolgreich verifiziert -- Immich-Restore-Test am 2026-05-27 erfolgreich verifiziert; Test-Postgres wurde nach der VectorChord-Migration am 2026-05-31 auf das produktive Immich-Postgres-Image umgestellt -- Authelia-Restore-Smoke am 2026-06-03 erfolgreich verifiziert; bewusst ohne produktiven Dump-Restore wegen Storage-Encryption-Key-Kopplung -- AdGuard-Home-Restore-Smoke am 2026-06-06 erfolgreich verifiziert; Borg-Config-Restore, HTTP `/control/status` 401, DNS-Smoke ok, 7 Filterlisten-Eintraege, Report `/mnt/user/backups/restore-reports/adguard-2026-06-06.md` -- Redis-8-Restore-Smoke am 2026-06-06 erfolgreich verifiziert; Pre-Cutover-Artefakt, Redis 8.8, PING ok, AOF aktiv, DBSIZE 1, Report `/mnt/user/backups/restore-reports/redis-2026-06-06.md` -- Bash-Dispatcher und Bash-Restore-Jobs am 2026-05-07 erfolgreich hostseitig verifiziert -- Restore-Lab und Report-Pfade auf dem Host angelegt -- `ntfy`-Wrapper ist fuer Host-Jobs verfuegbar -- Frische-Negativtest ist als sicherer Host-Job verfuegbar und am 2026-06-06 auf Unraid validiert: `ops/restore-tests/run-restore-checks.sh freshness-negative`. Ergebnis: synthetischer leerer Dump-Pfad erzeugte 10 Criticals, Test-Alert ging nach `homelab-alerts`, produktive Dump-Pfade blieben unangetastet. Report: `/mnt/user/backups/restore-reports/freshness-negative-2026-06-06-130320.md`. -- Nextcloud-Restore-Test: Scaffold existiert, aber **blockiert**. Nextcloud 33 fuehrt zur Laufzeit `chmod()` auf Dateien unter `/var/www/html` aus (`OC_Util.php:486`). Auf Unraids FUSE/shfs User-Shares ist `chmod` strukturell nicht moeglich, was zu permanenter 503 fuehrt. Loesungsoptionen: (a) Restore-Lab auf ein Cache-Drive statt User Share legen, (b) Docker-Volumes statt Bind-Mounts verwenden, (c) tmpfs-Mount fuer html/ + `rsync` der Borg-Daten hinein. Bis dahin ist Nextcloud als Backlog-Item dokumentiert. -- Komodo-Mongo-Daten-Restore am 2026-06-03 erfolgreich: 86904 Dokumente (inkl. 32 Stacks), Report `/mnt/user/backups/restore-reports/komodo-mongo-restore-2026-06-03.md` -- naechste grosse Kandidaten sind Mailarchiver und Mealie; Nextcloud bleibt blockiert (shfs-chmod) +# Mit ntfy-Meldung +bash /mnt/user/services/homelab-infra/ops/restore-tests/run-restore-job-with-ntfy.sh freshness homelab-info +``` -Vor dem ersten echten Testlauf je neuem Dienst muessen Zielpfade, Quellpfade und Bereinigungsschritte bewusst freigegeben werden. +## Status je Dienst + +Einziger Status-Ort ist die **Reifegrad-Tabelle** in `docs/RESTORE_MATRIX.md` +(letzter Test, Typ, naechster Lauf). Hier nur Besonderheiten: + +- **Nextcloud:** Test am 2026-06-03 erfolgreich, aber mit Unraid-shfs-Eigenheit: Nextcloud fuehrt `chmod()` unter `/var/www/html` aus, was auf FUSE/shfs scheitert. Das Skript patcht `check_data_directory_permissions: false` und legt den `.ncdata`-Marker an. +- **Authelia:** bewusst Config-Smoke ohne produktiven Dump-Restore (Storage-Encryption-Key-Kopplung). +- **Immich:** Foto-Dateien-Restore ist bewusst nicht Teil des Smokes (separater DR-Drill); Test-Postgres nutzt das produktive VectorChord-Image. +- **Unraid-Flash / Tailscale:** noch ohne vollstaendigen Erstlauf - `unraid-flash-runbook.md`, `tailscale-runbook.md`; offene Schritte in `docs/MASTER_TODO.md`. + +## Naechste Ausbaustufen + +1. Hermes-Zusammenfassung ueber vorhandene Reports (geparkt mit Hermes) +2. Report-Rotation: Reports werden dauerhaft aufbewahrt; bei wachsender Anzahl jaehrlich nach `_archive/YYYY/` verschieben. Der Frische-Check warnt ab `MAX_REPORT_AGE_DAYS=45`, loescht aber nie automatisch. diff --git a/ops/restore-tests/authelia-plan.md b/ops/restore-tests/authelia-plan.md deleted file mode 100644 index 6f1add4..0000000 --- a/ops/restore-tests/authelia-plan.md +++ /dev/null @@ -1,89 +0,0 @@ -# Authelia Restore Test Plan - -## Ziel - -Nachweisen, dass die Authelia-Konfiguration aus dem produktiven Borg-Archiv in einer isolierten Testumgebung wieder lauffaehig ist und der HTTP-Health-Endpunkt antwortet, ohne dass dabei produktive Secrets, produktives Postgres oder produktiver SMTP-Versand beruehrt werden. - -Bewusst **nicht** Teil dieses Tests: - -- Restore mit produktiven Authelia-Secrets. Der Test nutzt ausschliesslich Wegwerf-Werte fuer `AUTHELIA_SESSION_SECRET`, `AUTHELIA_STORAGE_ENCRYPTION_KEY` und `AUTHELIA_STORAGE_POSTGRES_PASSWORD`. SMTP- und Legacy-JWT-Env-Werte werden bewusst nicht gesetzt, damit Authelia keinen `notifier.smtp`-Block oder deprecated `jwt_secret` aus Env erzeugt. -- SMTP-Realanruf an GMX. Die minimale Test-Konfiguration setzt nur den Filesystem-Notifier. -- Forward-Auth gegen Traefik. Test laeuft nur auf `127.0.0.1:19091`, keine Traefik-Route. -- WebAuthn-/Duo-/OIDC-Identity-Provider-Endpunkte. Smoke prueft `/api/health`. -- **pg_restore des produktiven `postgresql17-authelia.dump`**. Authelia verschluesselt Storage-Werte mit `AUTHELIA_STORAGE_ENCRYPTION_KEY`. Ein Restore mit produktiven Daten in eine Test-Instanz mit Wegwerf-Key schlaegt im Startup-Check **by design** fehl ("the configured encryption key does not appear to be valid for this database"). Frische des produktiven Dumps wird ueber `check-restore-freshness.sh` ueberwacht; Daten-Decrypt-Drill ist eine separate DR-Aufgabe und braucht eine eigene Sicherheits-Choreographie mit kontrollierter Schluessel-Verwendung. Beobachtet im Erstlauf 2026-06-03 (Commit-Reihe `cacf77b..8d71dfb`); seit dem 2026-06-03-Folgecommit ist der Dump-Restore explizit aus dem Smoke entfernt. - -## Quelle - -- Backup-Quelle: produktives Borg-Archiv (`hetzner_borg_appdata_critical`) -- fachlich relevante Pfade im Archiv: - - `local/appdata/authelia/config` (verpflichtend) - - `local/borg-dumps/latest/postgresql17-authelia.dump` (existiert ggf. im Archiv; wird vom Smoke bewusst NICHT eingespielt, siehe oben) -- produktive Secrets unter `/mnt/user/appdata/secrets/authelia_*.txt` werden **nicht** gemountet - -## Test-Ziel - -- Restore-Lab: `/mnt/user/backups/restore-lab/authelia` -- Testdatenpfade: - - `/mnt/user/backups/restore-lab/authelia/config` (restaurierte Originalkonfiguration + `configuration.yml.original`) - - `/mnt/user/backups/restore-lab/authelia/test-config` (Runtime-Mount mit minimaler Test-`configuration.yml`) - - `/mnt/user/backups/restore-lab/authelia/postgres` (Test-Postgres-Datadir) - - `/mnt/user/backups/restore-lab/authelia/dumps/latest/postgresql17-authelia.dump` (falls extrahiert) - - `/mnt/user/backups/restore-lab/authelia/test-config/notifier/notifications.txt` (Filesystem-Notifier-Ausgabe) -- Testcontainer: - - `restoretest-authelia` (Image-Pin wie Produktion) - - `restoretest-authelia-postgres` (postgres:18.4, gleiche Major wie shared Postgres) -- Testport: `127.0.0.1:19091:9091` -- Report-Ziel: `/mnt/user/backups/restore-reports/authelia-YYYY-MM-DD.md` - -## Schutzregeln - -- produktive Pfade `/mnt/user/appdata/authelia/*` werden **nicht** beschrieben -- produktive Secret-Dateien `/mnt/user/appdata/secrets/authelia_*.txt` werden **nicht** gemountet -- produktive shared PostgreSQL 18 wird **nicht** angesprochen (`test-config/configuration.yml` definiert nur Test-Postgres) -- echter SMTP-Versand wird **nicht** ausgeloest (`test-config/configuration.yml` definiert nur Filesystem-Notifier) -- produktive Domain `auth.kaleschke.info` wird **nicht** uebernommen -- Testcontainer publishen nur auf `127.0.0.1`, keine LAN-/Tailscale-Bindung -- Borg-Passphrase wird aus `/mnt/user/appdata/secrets/borg_repo_passphrase.txt` gelesen und nirgendwo geloggt - -## Geplanter Ablauf - -1. Restore-Lab-Pfade leer anlegen -2. `local/appdata/authelia/config` aus dem aktuellsten Borg-Archiv extrahieren -3. minimale `test-config/configuration.yml` erzeugen; restaurierte Begleitdateien wie `users_database.yml` bleiben im Runtime-Mount, produktive externe Abhaengigkeiten werden nicht uebernommen; `notifier` auf Filesystem, `ntp.disable_startup_check: true`, `storage` auf Test-Postgres -4. Test-Postgres mit `ops/restore-tests/authelia-compose.test.yml` **frisch** hochfahren (keine Daten aus Dump - siehe Encryption-Key-Begruendung oben) -5. `authelia config validate` gegen `test-config/configuration.yml` laufen lassen -6. `restoretest-authelia` starten und HTTP-Health `http://127.0.0.1:19091/api/health` pollen -7. Report unter `/mnt/user/backups/restore-reports/authelia-YYYY-MM-DD.md` schreiben -8. Testcontainer stoppen und Restore-Lab bereinigen (`--keep-data` ueberschreibt) - -## Smoke-Test - -Minimal erfolgreich: - -- Borg-Extract der Authelia-Config gelingt -- Test-Postgres startet `healthy` -- `authelia config validate` laeuft ohne Fehler durch -- HTTP `200` auf `/api/health` innerhalb 120 s - -Optional spaeter: - -- vollstaendigen Auth-Flow gegen Test-User aus `users_database.yml` durchspielen -- WebAuthn-Endpunkt /api/secondfactor/webauthn pruefen -- ForwardAuth-Pfad gegen Mock-Backend testen - -## Bekannte Komplikationen - -| Risiko | Beschreibung | Mitigation | -|---|---|---| -| Testkonfig-Schema-Drift | Authelia erwartet nach Upgrade andere Keys in der Minimal-Konfig | bei `config validate`-Fehler Test-Block im Skript anpassen | -| SMTP-Startup-Check blockiert Start | Wenn Authelia trotz `disable_startup_check` SMTP probiert | Container-Logs lesen, ggf. Notifier-Block weiter haerten | -| NTP-Lookup im Test-Netz | Container hat keinen DNS-Resolver fuer `time.cloudflare.com` | im Smoke per `ntp.disable_startup_check: true` deaktiviert | -| Storage-Encryption-Key vs. Dump | siehe "Bewusst nicht Teil dieses Tests" - der Smoke laeuft FRISCH ohne Dump | by design - Daten-Decrypt-Drill ist separate Aufgabe | -| identity_validation Schema-Drift | Aelteres/neueres Authelia-Schema erwartet andere Keys | Validate-Config Output lesen, ggf. Test-Block anpassen | -| users_database.yml mit produktiven Hashes | Daten werden ins Restore-Lab kopiert, aber niemals gemountet auf produktive Domain | OK; Testpfad ist isoliert, kein Browser-Zugang ueber LAN | - -## Status - -- Skript- und Compose-Scaffold abgelegt am 2026-06-02 -- Erstlauf am 2026-06-03 erfolgreich: Config aus Borg, minimale Test-Konfiguration, frisches Test-Postgres, HTTP `/api/health` `200`, Report `/mnt/user/backups/restore-reports/authelia-2026-06-03.md` -- Fuer die Rotation vorgesehen: zweiter Samstag in geraden Monaten, 07:30 diff --git a/ops/restore-tests/gitea-plan.md b/ops/restore-tests/gitea-plan.md deleted file mode 100644 index babd3d0..0000000 --- a/ops/restore-tests/gitea-plan.md +++ /dev/null @@ -1,59 +0,0 @@ -# Gitea Restore Test Plan - -## Ziel - -Nachweisen, dass ein Gitea-Backup in einer isolierten Testumgebung wieder startbar ist und sowohl Web-UI als auch SSH-Port wieder verfuegbar sind. - -## Quelle - -- Backup-Quelle: Borg / Share-Backup -- fachlich relevanter Datenpfad: `/mnt/user/services/gitea/data` -- keine separaten Secret-Dateien dokumentiert - -## Test-Ziel - -- Restore-Lab: `/mnt/user/backups/restore-lab/gitea` -- Testdatenpfad: `/mnt/user/backups/restore-lab/gitea/data` -- Testcontainer: `restoretest-gitea` -- Testports: - - Web: `127.0.0.1:13000:3000` - - SSH: `127.0.0.1:12222:22` -- Report-Ziel: `/mnt/user/backups/restore-reports/gitea-YYYY-MM-DD.md` - -## Schutzregeln - -- produktiven Pfad `/mnt/user/services/gitea/data` nie beschreiben -- produktive Domain `git.kaleschke.info` nicht fuer die Testinstanz uebernehmen -- produktiven SSH-Port `222` nicht fuer die Testinstanz uebernehmen -- keine Traefik-Labels fuer die Testinstanz -- Testcontainer nur gegen Restore-Lab-Daten starten - -## Geplanter Ablauf - -1. Restore-Ziel unter `/mnt/user/backups/restore-lab/gitea` vorbereiten -2. Gitea-Daten aus Backup in `restore-lab/gitea/data` wiederherstellen -3. Testinstanz mit `ops/restore-tests/gitea-compose.test.yml` starten -4. lokalen Smoke-Test gegen `http://127.0.0.1:13000` und `127.0.0.1:12222` ausfuehren -5. Report unter `/mnt/user/backups/restore-reports/` schreiben -6. Testcontainer stoppen und Testumgebung bereinigen oder bewusst stehen lassen - -## Smoke-Test - -Minimal erfolgreich: - -- Container startet -- Web-UI antwortet -- mindestens ein bestehendes Repository-Verzeichnis ist im Restore-Lab sichtbar -- SSH-Port reagiert auf Verbindungsaufbau - -Optional spaeter: - -- Login-Seite gezielt pruefen -- SQLite-Datei `gitea.db` oder Nachfolger explizit bestaetigen -- `gitea doctor` oder interner Healthcheck als Zusatz - -## Noch offen vor dem ersten echten Lauf - -- exakter Borg-Restore-Befehl bzw. Restore-Quelle auf dem Host -- Bereinigungsstrategie fuer alte Restore-Lab-Daten -- ob Reports spaeter zusaetzlich per `ntfy` referenziert werden diff --git a/ops/restore-tests/immich-plan.md b/ops/restore-tests/immich-plan.md deleted file mode 100644 index 98a359c..0000000 --- a/ops/restore-tests/immich-plan.md +++ /dev/null @@ -1,89 +0,0 @@ -# Immich Restore Test Plan - -## Ziel - -Nachweisen, dass `immich.dump` aus dem produktiven Borg-Archiv in einer isolierten Testumgebung wieder einspielbar ist und Immich-Server damit anlaufen, einloggen und Asset-Metadaten anzeigen kann. - -Bewusst **nicht** Teil dieses Tests: - -- Wiederherstellung produktiver Foto-Dateien aus `/mnt/user/photos/immich` und `/mnt/user/photos/family_archive`. Der Smoke-Test bleibt DB-/UI-zentriert. -- Machine-Learning-Container. Spart Image-Pull-Zeit und Resource-Last; ML-Features sind im Smoke-Test nicht erforderlich. -- Echte Browser-Login-Sequenz. Smoke-Test prueft nur, dass die Login-Seite ausgeliefert wird und die DB-Tabellen `asset` und `"user"` lesbar sind. - -## Quelle - -- Backup-Quelle: produktives Borg-Archiv (`hetzner_borg_appdata_critical` oder lokales Mirror) -- fachlich relevanter Dump im Archiv: - - `local/borg-dumps/latest/immich.dump` -- Erzeuger: `ops/borg-ui/scripts/pre-backup-dumps.sh`, Funktion `dump_pg_db immich_postgres ... immich immich` mit `pg_dump -Fc` -- produktive Foto-Pfade werden im Smoke-Test bewusst **nicht** angefasst - -## Test-Ziel - -- Restore-Lab: `/mnt/user/backups/restore-lab/immich` -- Testdatenpfade: - - `/mnt/user/backups/restore-lab/immich/postgres` (Test-Postgres-Datadir) - - `/mnt/user/backups/restore-lab/immich/upload` (leeres Upload-Volume, Immich-Server braucht den Pfad nur als Mountpoint) - - `/mnt/user/backups/restore-lab/immich/dumps/latest/immich.dump` (extrahierter Dump) -- Testcontainer: - - `restoretest-immich-server` - - `restoretest-immich-postgres` (`ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0` - identisch zur Produktion, weil VectorChord-Backups ein Image mit VectorChord brauchen) - - `restoretest-immich-redis` (`redis:8.8.0-alpine`, rebuildbar) -- Testport Web: `127.0.0.1:12283:2283` -- Report-Ziel: `/mnt/user/backups/restore-reports/immich-YYYY-MM-DD.md` - -## Schutzregeln - -- produktive Pfade `/mnt/user/photos/immich` und `/mnt/user/photos/family_archive` werden **nicht** in den Test-Container gemountet -- produktive Domain `immich.kaleschke.info` wird **nicht** uebernommen -- keine Traefik-Labels fuer die Testinstanz -- keine produktive `immich_postgres`-/`immich_redis`-Instanz fuer den Test verwenden -- ML-Container bleibt weg -- Testcontainer publishen nur auf `127.0.0.1`, nicht auf LAN- oder Tailscale-Interface -- Borg-Passphrase wird aus `/mnt/user/appdata/secrets/borg_repo_passphrase.txt` gelesen und niemals in Logs, Reports oder Doku geschrieben - -## Geplanter Ablauf - -1. Restore-Ziel unter `/mnt/user/backups/restore-lab/immich` vorbereiten (postgres, upload, dumps/latest) -2. `local/borg-dumps/latest/immich.dump` aus dem aktuellsten Borg-Archiv extrahieren -3. Test-Postgres (Immich-Postgres mit VectorChord) und Test-Redis mit `ops/restore-tests/immich-compose.test.yml` starten -4. `immich.dump` in Test-Postgres importieren (`pg_restore -Fc --clean --if-exists --no-owner --no-privileges`) -5. Testinstanz `restoretest-immich-server` starten -6. lokalen Smoke-Test gegen `http://127.0.0.1:12283` ausfuehren und Asset/User-Count aus DB lesen -7. Report unter `/mnt/user/backups/restore-reports/immich-YYYY-MM-DD.md` schreiben -8. Testcontainer stoppen und Restore-Lab bereinigen - -## Smoke-Test - -Minimal erfolgreich: - -- Test-Postgres startet `healthy` -- `pg_restore -Fc` laeuft ohne Fehler durch -- Immich-Server liefert HTTP `200`, `302` oder `303` auf `/` -- Response enthaelt mindestens einen der Marker `Immich`, `Login`, `Signin` -- `select count(*) from asset;` und `select count(*) from "user";` sind lesbar - -Optional spaeter: - -- Echte Login-Form via API ansprechen -- VectorChord-/pgvector-Extensions explizit per `\dx` pruefen -- Test mit gemountetem **read-only** Foto-Sample-Pfad und Thumbnail-Rendering -- Test inkl. ML-Container, sobald genug Test-Ressourcen verfuegbar - -## Bekannte Komplikationen - -| Risiko | Beschreibung | Mitigation | -|---|---|---| -| Dump-Groesse unbekannt | `pg_dump -Fc` der Immich-DB kann je nach Asset-/Face-Tabellen mehrere GB sein | Erster Lauf bewusst mit `--what-if`, anschliessend Operator-Test mit Zeitmessung | -| `pg_restore`-Dauer unbekannt | Index-/Constraint-Aufbau und VectorChord-Index-Build koennen lange dauern | Test-Postgres mit Health-Polling startet; Lauf nicht abbrechen ohne `pg_restore`-Exit | -| VectorChord-/pgvector-Extension-Mismatch | Wenn das Test-Postgres-Image nicht zu Produktion passt, kann der Restore oder Immich-Start fehlschlagen | Compose pinnt denselben Digest wie `apps/immich/docker-compose.yml` | -| Immich-Server-Migrations beim Start | Immich fuehrt beim ersten Start DB-Migrations aus; das kann nach Restore noch laufen, bevor Web-UI antwortet | Smoke-Test pollt HTTP bis zu 120 s, bevor er als Fehler markiert | -| Asset-Files fehlen | Der Test mountet kein Foto-Volume; Immich zeigt "missing" auf Asset-Detail-Seiten | Smoke-Test prueft nur Login-Page und DB-Counts, nicht Asset-Rendering | -| ML-Endpoint unreachable | Immich-Server kann ML-Endpoint nicht erreichen | `IMMICH_MACHINE_LEARNING_URL` zeigt bewusst auf einen nicht erreichbaren Hostnamen; Login bleibt funktional, ML-Features bleiben deaktiviert | - -## Noch offen vor dem ersten echten Lauf - -- Dump-Groesse `immich.dump` auf dem Host bestimmen (`ls -lh /mnt/user/backups/borg/dumps/latest/immich.dump`) -- Erwartete Restore-Dauer durch ersten Lauf mit `--keep-data` messen -- Pruefen, ob die Immich-Tabellen `assets`/`users` im aktuellen Schema noch existieren (Schema-Drift bei Major-Update wuerde die Asset-Count-Query brechen, das Skript faengt das tolerant ab) -- Schedule-Eintrag in `ops/restore-tests/schedule.md`: aktuell ist Immich nur als "spaeter, eigener Sprint" gefuehrt. Erst nach erstem erfolgreichen Lauf in Schedule aufnehmen, z. B. quartalsweise. diff --git a/ops/restore-tests/immich-runbook.md b/ops/restore-tests/immich-runbook.md index ac2ab95..19fc750 100644 --- a/ops/restore-tests/immich-runbook.md +++ b/ops/restore-tests/immich-runbook.md @@ -34,7 +34,6 @@ Vor dem ersten Lauf muss Operator entscheiden: - `ops/restore-tests/immich-compose.test.yml` - `ops/restore-tests/immich-restore-test.sh` - `ops/restore-tests/immich-restore-test.ps1` (Scaffold, kein Live-Run) - - `ops/restore-tests/immich-plan.md` - `ops/restore-tests/immich-runbook.md` ## Erster Lauf - trockene Variante diff --git a/ops/restore-tests/komodo-bootstrap-plan.md b/ops/restore-tests/komodo-bootstrap-plan.md deleted file mode 100644 index 38df411..0000000 --- a/ops/restore-tests/komodo-bootstrap-plan.md +++ /dev/null @@ -1,88 +0,0 @@ -# Komodo Bootstrap Trockenlauf - Plan - -## Ziel - -Nachweisen, dass `ops/komodo/docker-compose.yml` als Recovery-Anker fuer einen Komodo-Kaltstart tauglich ist, ohne den produktiven Komodo-Stack anzufassen. - -Bewusst **nicht** Teil dieses Tests: - -- Restore aus dem produktiven `komodo-mongo.archive.gz`-Dump (eigene Folgeaufgabe; dieser Test prueft nur das Compose-Bootstrap, nicht den Daten-Restore). -- docker.sock-Mount fuer die Test-Periphery (die Test-Periphery darf nie produktive Container managen). -- Traefik-Route oder Authelia-Anbindung (Test laeuft ausschliesslich auf `127.0.0.1:19120`). - -## Quelle - -- Bootstrap-Anker: `ops/komodo/docker-compose.yml` (Soll-Stand laut `docs/SERVICES_RECOVERY.md` Stufe A-F). -- Image-Digests: identisch zur Produktion fuer komodo-core und komodo-periphery; Mongo-Image identisch. - -## Test-Ziel - -- Restore-Lab: `/mnt/user/backups/restore-lab/komodo` -- Wegwerf-Pfade: - - `/mnt/user/backups/restore-lab/komodo/mongo` (Test-Mongo-Datadir) - - `/mnt/user/backups/restore-lab/komodo/core` (Repo-Cache) - - `/mnt/user/backups/restore-lab/komodo/keys` (gemeinsamer Keys-Pfad fuer Core+Periphery) - - `/mnt/user/backups/restore-lab/komodo/periphery` (Periphery-Etc) -- Testcontainer: - - `restoretest-komodo-mongo` - - `restoretest-komodo-core` (Test-Port `127.0.0.1:19120`) - - `restoretest-komodo-periphery` (ohne docker.sock) -- Compose-Project: `restoretest-komodo` (isoliert von Produktions-Project `komodo`) -- Report-Ziel: `/mnt/user/backups/restore-reports/komodo-bootstrap-YYYY-MM-DD.md` - -## Schutzregeln - -- produktive Datadirs `/mnt/user/appdata/komodo/{mongo,core,periphery}` werden **nicht** gemountet -- produktive Container `komodo-mongo`, `komodo-core`, `komodo-periphery` werden **nicht** gestoppt -- produktive `KOMODO_*`-Secrets werden **nicht** verwendet -- Test-Compose enthaelt nur Wegwerf-Werte fuer `KOMODO_SECRET_KEY`, `KOMODO_WEBHOOK_SECRET`, `KOMODO_JWT_SECRET`, `KOMODO_PASSKEY` und Mongo-Root-Password -- Test-Periphery laeuft ohne docker.sock-Mount und ohne `/mnt/user/services`-Mount -- Test-Port nur auf `127.0.0.1:19120`, keine LAN-/Tailscale-Bindung - -## Geplanter Ablauf - -1. Restore-Lab-Pfade leer anlegen -2. `docker compose config` auf dem Test-Compose validieren -3. Mongo und Core hochfahren, auf Mongo-`healthy` warten -4. HTTP-Smoke gegen `http://127.0.0.1:19120` (Login-Seite oder Auth-Redirect erwartet) -5. Periphery dazustarten, kurz beobachten -6. Mongo-`authenticated ping` mit Test-Credentials -7. Report schreiben -8. Cleanup `docker compose down -v` und Restore-Lab loeschen (ausser `--keep-data`) - -## Smoke-Test - -Minimal erfolgreich: - -- `docker compose config` valid -- Test-Mongo erreicht `healthy` -- Mongo-Authentifizierung mit Test-Creds funktioniert (`db.adminCommand({ping:1}).ok = 1`) -- Komodo-Core HTTP `200`, `302`, `303` oder `401` (alles ist ein valider Lebenszeichen) -- Test-Periphery container state `running` - -Optional spaeter: - -- Periphery-Verbindung gegen Test-Core verifizieren (braucht Periphery-Konfig mit `core_url`) -- Echtes Restore aus `komodo-mongo.archive.gz`-Dump in die Test-Mongo -- Schreiben einer Wegwerf-Resource (Server/Stack) ueber die API - -## Bekannte Komplikationen - -| Risiko | Beschreibung | Mitigation | -|---|---|---| -| Image-Drift | Komodo-Images aktualisieren ihre Major-Tag-Digests | Compose pinnt denselben Digest wie Produktion; bei Image-Update auch Test-Compose nachziehen | -| Port-Konflikt | wenn 19120 anderweitig belegt ist | nur `127.0.0.1`-Bind; bei Konflikt Port im Compose anpassen | -| Volume-Reste | unterbrochener Lauf laesst Wegwerf-Datadir liegen | Skript loescht Restore-Lab vor jedem Lauf; `--keep-data` ueberschreibt das bewusst | -| Periphery-Erreichbarkeit | Core sucht Periphery initial nicht aktiv | Test prueft nur Periphery `State.Status=running`; voller Handshake ist optional | - -## Bestaetigte Laeufe - -| Datum | Mode | Ergebnis | Report | -|---|---|---|---| -| 2026-05-30 | `--what-if` | Plan-Ausgabe wie erwartet | (kein Report, nur stdout) | -| 2026-05-30 | `--keep-data` | `SUCCESS`, 5/5 Checks gruen, Core HTTP `200`, Mongo healthy in ~6 s | `/mnt/user/backups/restore-reports/komodo-bootstrap-2026-05-30.md` | - -## Folgeschritte - -- Quartals-Belegung: Komodo-Bootstrap passt zum DR-Sanity-Check (`ops/restore-tests/schedule.md` Q2/Q4) und kann ohne Borg-Archiv jederzeit wiederholt werden. -- Optional fuer kuenftige Laeufe: echtes Restore aus `komodo-mongo.archive.gz` in die Test-Mongo, danach Schreiben einer Wegwerf-Resource ueber die API. diff --git a/ops/restore-tests/paperless-plan.md b/ops/restore-tests/paperless-plan.md deleted file mode 100644 index fd31381..0000000 --- a/ops/restore-tests/paperless-plan.md +++ /dev/null @@ -1,72 +0,0 @@ -# Paperless Restore Test Plan - -## Ziel - -Nachweisen, dass ein Paperless-Backup in einer isolierten Testumgebung wieder startbar ist und sowohl Dokumentenpfade als auch PostgreSQL-Dump sauber zusammenlaufen. - -## Quelle - -- Backup-Quelle: Borg / Share-Backup -- fachlich relevante Dateipfade: - - `/mnt/user/appdata/paperless-ngx/data` - - `/mnt/user/documents/paperless` - - `/mnt/user/documents/paperless/export` - - `/mnt/user/documents/scans_inbox` -- fachlich relevanter Dump: - - `/mnt/user/backups/borg/dumps/latest/postgresql17-paperless.dump` - -## Test-Ziel - -- Restore-Lab: `/mnt/user/backups/restore-lab/paperless` -- Testdatenpfade: - - `/mnt/user/backups/restore-lab/paperless/data` - - `/mnt/user/backups/restore-lab/paperless/media` - - `/mnt/user/backups/restore-lab/paperless/export` - - `/mnt/user/backups/restore-lab/paperless/consume` - - `/mnt/user/backups/restore-lab/paperless/postgres` -- Testcontainer: - - `restoretest-paperless` - - `restoretest-paperless-postgres` - - `restoretest-paperless-redis` -- Testport Web: `127.0.0.1:18120:8000` -- Report-Ziel: `/mnt/user/backups/restore-reports/paperless-YYYY-MM-DD.md` - -## Schutzregeln - -- produktive Pfade nie beschreiben -- produktive Domain `paperless.kaleschke.info` nicht fuer die Testinstanz uebernehmen -- keine Traefik-Labels fuer die Testinstanz -- keine produktive PostgreSQL- oder Redis-Instanz fuer den Test verwenden -- Testcontainer nur gegen Restore-Lab-Daten und isolierte Test-Backends starten - -## Geplanter Ablauf - -1. Restore-Ziel unter `/mnt/user/backups/restore-lab/paperless` vorbereiten -2. Paperless-Dateipfade aus Borg in das Restore-Lab wiederherstellen -3. Test-Postgres und Test-Redis mit `ops/restore-tests/paperless-compose.test.yml` starten -4. `postgresql17-paperless.dump` in Test-Postgres importieren -5. Testinstanz `restoretest-paperless` starten -6. lokalen Smoke-Test gegen `http://127.0.0.1:18120` ausfuehren -7. Report unter `/mnt/user/backups/restore-reports/` schreiben -8. Testcontainer stoppen und Testumgebung bereinigen - -## Smoke-Test - -Minimal erfolgreich: - -- Test-Postgres startet -- Dump-Import gelingt -- Paperless-Web-UI antwortet -- mindestens ein Dokument liegt im Restore-Lab-Medienpfad - -Optional spaeter: - -- Login-Seite gezielt pruefen -- Dokumentanzahl aus UI oder DB querpruefen -- OCR-/Task-Worker-Status verifizieren - -## Noch offen vor dem ersten echten Lauf - -- exakter Borg-Restore-Befehl fuer alle vier Dateipfade -- exakter `pg_restore`-Befehl im Test-Postgres -- wie stark wir `consume` im ersten Lauf ueberhaupt brauchen diff --git a/ops/restore-tests/tailscale-runbook.md b/ops/restore-tests/tailscale-runbook.md new file mode 100644 index 0000000..f5397ee --- /dev/null +++ b/ops/restore-tests/tailscale-runbook.md @@ -0,0 +1,34 @@ +# Tailscale - Restore-Runbook + +Typ: Runbook · Stand: 2026-06-11 · Status: aktiv (noch kein Erstlauf) + +Restore-Pfad fuer den Tailscale-State. Wichtig: Tailscale laeuft als +**natives Unraid-Plugin**; der funktionale State liegt unter +`/boot/config/plugins/tailscale/state` und ist Teil des Flash-Backups +(`docs/RESTORE_MATRIX.md` Tier 1). + +## Voraussetzungen + +- Zugriff auf das Flash-Backup-Artefakt bzw. ein Borg-Archiv mit dem State-Pfad +- Testpfad unter `/mnt/user/backups/restore-lab/tailscale` vorbereitet +- **Achtung:** Der Tailscale-State ist maschinenspezifisch. Ein Restore auf den produktiven Host wuerde die laufende Verbindung verdraengen. Nur auf einem Wegwerf- oder Offline-Host testen. + +## Artefakt-Validierung (ohne produktiven Host) + +1. State-Verzeichnis in den Testpfad extrahieren +2. Erwartete Dateien pruefen: `tailscaled.state` vorhanden +3. Dateisystem-Rechte pruefen: `tailscaled.state` muss fuer `root` zugaenglich sein + +## Reconnect-Test (auf Wegwerf-Host oder VM) + +1. Tailscale mit dem gemounteten State-Pfad starten +2. `tailscale status` zeigt `Connected` oder den erwarteten Hostnamen +3. Tailscale-Admin-Konsole zeigt das Geraet als `Online` +4. SSH ueber Tailscale-IP auf den Testhost moeglich +5. Testinstanz stoppen; Wegwerf-Geraet in der Tailscale-Admin-Konsole entfernen + +**Smoke-Test-Kriterium:** Instanz verbindet sich mit bestehendem Tailscale-Account (kein neues Re-Auth noetig), Tailscale-IP ist erreichbar. + +**Hinweis:** Falls der State veraltet ist (Key expired), fordert Tailscale ein +Re-Auth an. Das ist ein valides Testergebnis und belegt, wie lang der +Reconnect-Pfad bei abgelaufenem Key ist. diff --git a/ops/restore-tests/unraid-flash-runbook.md b/ops/restore-tests/unraid-flash-runbook.md new file mode 100644 index 0000000..f71d11a --- /dev/null +++ b/ops/restore-tests/unraid-flash-runbook.md @@ -0,0 +1,45 @@ +# Unraid OS Flash - Restore-Runbook + +Typ: Runbook · Stand: 2026-06-11 · Status: aktiv (Stick-Boot-Test offen) + +Restore-Pfad fuer die Unraid-Flash-Konfiguration. Artefakt-Validierung ist +automatisiert und belegt; offen bleibt nur der physische Ersatzstick-Boot-Test +(siehe `docs/MASTER_TODO.md`). + +## Voraussetzungen + +- Borg-Artefakt `unraid-flash-config.tar.gz` und `.sha256` unter `/mnt/user/backups/borg/dumps/latest` oder im Hetzner-Borg-Repo verfuegbar +- Neuer leerer USB-Stick (Empfehlung: 16 GB, USB 2.0 kompatibel) +- Unraid USB Flash Creator oder manueller Restore-Pfad +- Offline-gesicherte Borg-Passphrase verfuegbar + +## Artefakt-Validierung (ohne produktiven Stick) + +Automatisiert via Repo-Skript `ops/maintenance/check-unraid-flash-backup.sh` +(read-only, keine Extraktion). Manuelle Einzelschritte: + +1. SHA256-Pruefung: `sha256sum -c unraid-flash-config.tar.gz.sha256` +2. Artefakt-Inhalt pruefen: `tar -tzf unraid-flash-config.tar.gz | head -40` — erwartet `config/` als Prefix +3. Kern-Configs vorhanden: `super.dat`, `disk.cfg`, `ident.cfg`, `share.cfg`, `network.cfg`, `docker.cfg`, `go`, `domain.cfg` +4. Keine produktiven Konfigurationspfade (z. B. `config/ssh/`) ausserhalb des Test-Environments extrahieren +5. Manifest-Datei auf Vollstaendigkeit pruefen + +Letzte Validierung: 2026-06-05, Exit 0, sha256 OK, 390 Eintraege, 8/8 +Kern-Configs (siehe Reifegrad-Tabelle in `docs/RESTORE_MATRIX.md`). + +## Vollstaendiger Restore-Test (auf Wegwerf-Stick) + +1. Neuen USB-Stick mit Unraid USB Flash Creator formatieren und Basis-Unraid draufspielen +2. `config/`-Verzeichnis aus `unraid-flash-config.tar.gz` in den `/boot/config`-Pfad des neuen Sticks extrahieren +3. Im Testrahmen booten (kein Array starten, keine Shares mounten) +4. Pruefen: Unraid-Grundkonfiguration (Shares, Hostname, Netzwerk) ist sichtbar +5. Array-Zuordnung lesbar, ohne Drive-Assigns zu bestaetigen + +**Smoke-Test-Kriterium:** Unraid bootet, Hostname ist `Kallilabcore`, Share-Konfiguration ist sichtbar, kein Array gestartet. + +## Sonderregel + +Das Artefakt enthaelt Host-Konfiguration und Secret-Material (SSH-Host-Keys, +Tailscale-State, `passwd`/`shadow`/`smbpasswd`, Lizenz-Key) und ist wie +Secret-Backup zu behandeln. Nicht auf oeffentlichen oder unverschluesselten +Testzielen extrahieren. diff --git a/ops/restore-tests/vaultwarden-plan.md b/ops/restore-tests/vaultwarden-plan.md deleted file mode 100644 index e440511..0000000 --- a/ops/restore-tests/vaultwarden-plan.md +++ /dev/null @@ -1,56 +0,0 @@ -# Vaultwarden Restore Test Plan - -## Ziel - -Nachweisen, dass ein Vaultwarden-Backup in einer isolierten Testumgebung wieder startbar und fachlich nutzbar ist. - -## Quelle - -- Backup-Quelle: Borg / Share-Backup -- fachlich relevanter Datenpfad: `/mnt/user/appdata/vaultwarden` -- Produktives Admin-Token wird fuer den Restore-Smoke bewusst nicht gemountet; - die Testinstanz nutzt einen Wegwerf-Wert aus `vaultwarden-compose.test.yml`. - -## Test-Ziel - -- Restore-Lab: `/mnt/user/backups/restore-lab/vaultwarden` -- Testdatenpfad: `/mnt/user/backups/restore-lab/vaultwarden/data` -- Testcontainer: `restoretest-vaultwarden` -- Testport: `127.0.0.1:18080:80` -- Report-Ziel: `/mnt/user/backups/restore-reports/vaultwarden-YYYY-MM-DD.md` - -## Schutzregeln - -- produktiven Pfad `/mnt/user/appdata/vaultwarden` nie beschreiben -- produktive Domain `vault.kaleschke.info` nicht fuer die Testinstanz uebernehmen -- keine Traefik-Labels fuer die Testinstanz -- Testcontainer nur gegen Restore-Lab-Daten starten - -## Geplanter Ablauf - -1. Restore-Ziel unter `/mnt/user/backups/restore-lab/vaultwarden` vorbereiten -2. Vaultwarden-Daten aus Backup in `restore-lab/vaultwarden/data` wiederherstellen -3. Testinstanz mit `ops/restore-tests/vaultwarden-compose.test.yml` starten -4. lokalen Smoke-Test gegen `http://127.0.0.1:18080` ausfuehren -5. Report unter `/mnt/user/backups/restore-reports/` schreiben -6. Testcontainer stoppen und Testumgebung bereinigen oder bewusst stehen lassen - -## Smoke-Test - -Minimal erfolgreich: - -- Container startet -- Login-Seite antwortet -- Vaultwarden-Daten sind vorhanden - -Optional spaeter: - -- Admin-Endpunkt nur mit separatem Wegwerf-Token pruefen -- Websocket-Endpunkt pruefen -- Anzahl/Vorhandensein zentraler Daten artefaktisch verifizieren - -## Noch offen vor dem ersten echten Lauf - -- exakter Borg-Restore-Befehl bzw. Restore-Quelle auf dem Host -- Bereinigungsstrategie fuer alte Restore-Lab-Daten -- ob Reports nur auf dem Host liegen oder zusaetzlich per ntfy referenziert werden