38fa8c5dd5
Tier-2-Restore-Tests sind damit komplett belegt. Verlauf: - Lauf 1 (commit pre-fix): Borg-Extract+pg_restore ok, HTTP 503 wegen OC_Util.php:486 chmod-Fehlschlag auf shfs/FUSE - Lauf 2 (commit53c34dc, check_data_directory_permissions: false): HTTP 503 wegen fehlender .ncdata-Marker-Datei - Lauf 3 (commitba87719, .ncdata-Marker): SUCCESS Endresultat: - HTTP 200 von /status.php - occ status maintenance: false - 126 Tabellen in der wiederhergestellten DB - Quelle: hetzner_borg_appdata_critical, Archiv Taegliche-Sicherung-2026-06-03T04:30:41.432 - Report: /mnt/user/backups/restore-reports/nextcloud-2026-06-03.md Doku-Updates: - RESTORE_MATRIX.md: Nextcloud-Zeile auf "2026-06-03 / quartalsweise" gezogen, Nextcloud aus "Naechste Restore-Test-Kandidaten" entfernt - AUDIT_2026-05-25_TODO.md: Backlog-P1 und Operator-P1 beide auf "erledigt 2026-06-03" - DR_DRILL_2026-06-03.md Folge-Iteration: X-1 als erledigt markiert Restliche P1-Operator-Aufgabe: WSL2+Borg-Client auf DR-Workstation.
505 lines
25 KiB
Markdown
505 lines
25 KiB
Markdown
# DR Tabletop Drill - 2026-06-03
|
|
|
|
Trockenlauf gegen `docs/DISASTER_RECOVERY.md` Phase 0 bis 5 plus referenzierte
|
|
Runbooks (`SERVICES_RECOVERY.md`, `RESTORE_MATRIX.md`, `SECRETS_MAP.md`,
|
|
`RESTORE_HANDBOOK.md`, `EXTERNAL_DEPENDENCIES.md`).
|
|
|
|
Szenario: Bare-Metal-Ausfall. Unraid-Host und alle lokalen Festplatten sind
|
|
weg. Operator hat: Laptop, Hetzner-Account, Vaultwarden-Export, Repo-Doku.
|
|
Soft-Recovery (Host laeuft, Appdata futsch) ist eine Teilmenge dieser
|
|
Findings.
|
|
|
|
Methode: kalter Lesetest. Kein Container gestartet, keine Skripte
|
|
ausgefuehrt. Jeder Befund ist mit Repo-Datei und Zeile belegt. Spekulative
|
|
"vielleicht unklar"-Befunde sind weggelassen.
|
|
|
|
Severity:
|
|
|
|
- **CRITICAL** - blockiert Wiederanlauf, ohne Workaround nicht loesbar
|
|
- **HIGH** - blockiert eine Phase, Workaround moeglich aber undokumentiert
|
|
- **MED** - kostet Zeit oder fuehrt zu vermeidbarem Fehler
|
|
- **LOW** - Konsistenz / Stil
|
|
|
|
## Zusammenfassung
|
|
|
|
| ID | Phase | Severity | Thema |
|
|
|---|---|---|---|
|
|
| P0-1 | 0 | HIGH | Brueckenpfad Windows-Clone -> frischer Unraid-Host fehlt |
|
|
| P0-2 | 0 | HIGH | GitHub-Mirror-Zugang im DR ist nicht eigenstaendig dokumentiert |
|
|
| P1-1 | 1 | CRITICAL | Unraid-Flash-Restore: kein dokumentierter Extract-Pfad ohne laufenden Host |
|
|
| P1-2 | 1 | MED | Unraid-OS-Flash-Restore-Test laut Matrix nie real getestet |
|
|
| P2-1 | 2 | HIGH | KOMODO_* externe Operator-Notiz ist Pflichtquelle, Existenz nicht verifizierbar |
|
|
| P2-2 | 2 | HIGH | DR.md Phase 4 vs. SERVICES_RECOVERY.md Bootstrap-Reihenfolge widerspruechlich |
|
|
| P2-3 | 2 | MED | `homelab_smtp_password.txt` fehlt in DR.md Phase 2.6.1 |
|
|
| P2-4 | 2 | MED | `n8n_encryption_key.txt` fehlt in DR.md Phase 2.6.1 |
|
|
| P2-5 | 2 | LOW | Monitoring-/Filebrowser-Secrets fehlen in DR.md Phase 2.6.1 |
|
|
| P3-1 | 3 | HIGH | Borg-Client ohne `borg-ui`-Container ist nicht dokumentiert |
|
|
| P3-2 | 3 | HIGH | Borg-Passphrase-Bootstrap aus Offline-Sicherung nicht als expliziter Schritt |
|
|
| P3-3 | 3 | MED | Hetzner-Maintenance-Key aus Vaultwarden ist Henne-Ei im Bare-Metal |
|
|
| P4-1 | 4 | HIGH | Externe Docker-Netze in DR.md Phase 4 Stufe 1 nicht erwaehnt |
|
|
| P4-2 | 4 | HIGH | Cloudflare-LE-Rate-Limit-Risiko bei verlorenem `letsencrypt`-State |
|
|
| P4-3 | 4 | MED | `traefik/dynamic/*` als Phase-4-Pre-Check fehlt in der Reihenfolge |
|
|
| P4-4 | 4 | HIGH | Authelia "frische Postgres ohne Dump"-Pfad nicht beschrieben |
|
|
| P4-5 | 4 | LOW | Gitea in Stufe 2 hinter Postgres ist faktisch nicht noetig (SQLite) |
|
|
| P4-6 | 4 | HIGH | Komodo-Mongo Passwort-Lockout-Risiko bei restauriertem Datadir |
|
|
| P4-7 | 4 | MED | Komodo `extra_hosts` mit hardgecodeter LAN-IP bricht bei IP-Wechsel |
|
|
| P4-8 | 4 | HIGH | Stack-ENV-Wiederherstellung in Komodo praktisch nur manueller UI-Eintrag |
|
|
| P5-1 | 5 | LOW | Smoke-Tests in Phase 5 weniger streng als RESTORE_MATRIX |
|
|
| P5-2 | 5 | MED | Kein Verifikationspunkt fuer App-zu-DB-Verbindung nach Stack-ENV-Restore |
|
|
| X-1 | uebergreifend | HIGH | Nextcloud-Restore-Skript ist da, aber noch nie real ausgefuehrt |
|
|
|
|
## Phase 0 - Repo-Zugang
|
|
|
|
### P0-1 (HIGH) - Brueckenpfad Windows-Clone -> frischer Unraid fehlt
|
|
|
|
`docs/DISASTER_RECOVERY.md:88-93` listet als Repo-Quellen: GitHub-Mirror,
|
|
lokaler Bare-Clone, lokaler Arbeits-Clone. `SERVICES_RECOVERY.md:67-68`
|
|
nennt den lokalen Operator-Clone unter `G:\Gitea_Clone\homelab-infra\` als
|
|
Vorzug.
|
|
|
|
Luecke: der Pfad "wie kommt der Windows-Clone auf einen frisch installierten
|
|
Unraid-Host" ist nicht beschrieben. Implizit: SMB-Share, USB-Stick, scp ueber
|
|
LAN. Aber auf einem frisch aufgesetzten Unraid existiert noch keine
|
|
funktionierende SMB-Konfiguration; SSH-Key vom Operator-PC ist nicht
|
|
vorbereitet.
|
|
|
|
Vorschlag: Zwei Saetze in `DISASTER_RECOVERY.md` Phase 0 ergaenzen, wie der
|
|
Operator-Clone konkret zum Host kommt (USB-Stick + `mkdir -p
|
|
/mnt/user/services/homelab-infra && rsync -a` aus Operator-Windows-PC, oder
|
|
direkt vom GitHub-Mirror per `git clone https://github.com/...` auf dem
|
|
Unraid-Host).
|
|
|
|
### P0-2 (HIGH) - GitHub-Mirror-Zugang im DR
|
|
|
|
`SECRETS_MAP.md:42` sagt, der GitHub-Push-Mirror-PAT liegt in den
|
|
Gitea-Mirror-Settings persistent unter `/mnt/user/services/gitea/data`.
|
|
`EXTERNAL_DEPENDENCIES.md:18` nennt den Mirror als `michaelkaleschke-spec/
|
|
homelab-infra` und betont "privater" Push-Mirror.
|
|
|
|
Luecke: Wenn der Mirror **privat** ist, scheitert ein anonymer `git clone`
|
|
im DR-Bootstrap. Es gibt keine dokumentierte Notfall-Quelle fuer einen
|
|
Read-PAT/SSH-Key, der lokal beim Operator (nicht in Gitea, nicht im Repo)
|
|
verfuegbar ist.
|
|
|
|
Vorschlag in `EXTERNAL_DEPENDENCIES.md`: entweder explizit dokumentieren,
|
|
dass der Mirror lesend `Public` ist (DR-fit), oder einen Read-PAT in der
|
|
Vaultwarden-/Offline-Notiz neben der Borg-Passphrase als Bootstrap-Voraussetzung
|
|
benennen.
|
|
|
|
## Phase 1 - Unraid und Shares
|
|
|
|
### P1-1 (CRITICAL) - Unraid-Flash-Restore ohne laufenden Host
|
|
|
|
`docs/DISASTER_RECOVERY.md:107` sagt: "Primaere lokale/off-site
|
|
Restore-Quelle fuer die bestehende Flash-Konfiguration ist das
|
|
Borg-Artefakt `unraid-flash-config.tar.gz` aus
|
|
`/mnt/user/backups/borg/dumps/latest`."
|
|
|
|
Henne-Ei: der Pfad ist auf den verlorenen Shares oder auf Hetzner. Hetzner-
|
|
Zugriff braucht einen funktionierenden Linux-Host mit Borg-Client und
|
|
Passphrase. Im Bare-Metal-Fall ist genau das nicht da. RESTORE_MATRIX.md
|
|
Tier 1 Zeile `Unraid OS Flash` (`docs/RESTORE_MATRIX.md:29`) sagt nur "Unraid
|
|
USB Flash Creator / neuer Boot-Stick" - das beschreibt die Stick-Erzeugung,
|
|
nicht den Extract des Borg-Artefakts.
|
|
|
|
Operativ: Operator braucht einen Laptop mit Borg-Client + Passphrase +
|
|
SSH-Key fuer die Hetzner-Storage-Box. Das ist eine **separat zu pflegende
|
|
Operator-Workstation-Voraussetzung** und ist in keinem Repo-Dokument als
|
|
DR-Vorbedingung gelistet.
|
|
|
|
Vorschlag: In `EXTERNAL_DEPENDENCIES.md` oder `DISASTER_RECOVERY.md`
|
|
Abschnitt 3 als Pflichtposten aufnehmen: "Operator-Laptop mit installiertem
|
|
Borg-Client, SSH-Key fuer Hetzner und Zugriff auf die offline gesicherte
|
|
Passphrase". Inklusive Test, dass der Operator den Extract tatsaechlich
|
|
durchfuehren kann.
|
|
|
|
### P1-2 (MED) - Unraid-OS-Flash-Restore-Test nie gelaufen
|
|
|
|
`docs/RESTORE_MATRIX.md:140` Spalte "Letzter Restore-Test" fuer Unraid OS
|
|
Flash: `-` (kein Test). Das ist die Grundlage fuer Phase 1 und ist nie als
|
|
Smoke verifiziert. Empfehlung: einmaliger Test, der die Tar-Archiv-Struktur
|
|
gegen die erwarteten Flash-Pfade prueft (kein echter Boot-Test noetig).
|
|
|
|
## Phase 2 - Secrets und Stack-ENV
|
|
|
|
### P2-1 (HIGH) - KOMODO_* externe Operator-Notiz als Pflichtquelle
|
|
|
|
`docs/SECRETS_MAP.md:132,138-143` macht den Komodo-Sonderfall klar: die
|
|
KOMODO_*-Secrets sind aus dem eigenen Mongo-Dump nicht rekonstruierbar,
|
|
solange Komodo nicht laeuft. Quellen: Vaultwarden ODER externe Notiz.
|
|
|
|
Im Bare-Metal-Fall ist Vaultwarden in DR.md Phase 4 Stufe 4, Komodo in
|
|
Phase 4 Stufe 3. Damit ist die **externe Operator-Notiz** die einzige
|
|
Pflichtquelle in der Reihenfolge.
|
|
|
|
Luecke: ob diese Notiz wirklich existiert und die 5 Werte
|
|
(KOMODO_SECRET_KEY, KOMODO_WEBHOOK_SECRET, KOMODO_JWT_SECRET,
|
|
KOMODO_MONGO_PASSWORD, KOMODO_PERIPHERY_PASSKEY) enthaelt, ist in keinem
|
|
Repo-Dokument bestaetigt. Die Borg-Passphrase ist als "Operator-Bestaetigung
|
|
2026-05-26" dokumentiert; eine analoge Bestaetigung fuer die KOMODO_*-Notiz
|
|
fehlt.
|
|
|
|
Vorschlag: gleiche Form wie Borg-Passphrase - eine Zeile in
|
|
`EXTERNAL_DEPENDENCIES.md` "Komodo-Stack-ENV-Notiz offline gesichert,
|
|
Operator-Bestaetigung YYYY-MM-DD".
|
|
|
|
### P2-2 (HIGH) - Reihenfolgen-Inkonsistenz DR vs. SERVICES_RECOVERY
|
|
|
|
`docs/SERVICES_RECOVERY.md:102` (Stufe C, Komodo-Bootstrap): "Vaultwarden
|
|
(sobald restauriert), externe Operator-Notiz, oder Komodo-Mongo-Dump (nur
|
|
wenn Mongo separat bereits gestartet ...)".
|
|
|
|
`docs/DISASTER_RECOVERY.md:247-301` (Phase 4): Stufe 3 = Komodo, Stufe 4 =
|
|
Vaultwarden.
|
|
|
|
Wenn ein Leser sich an DR.md Phase 4 haelt, ist Vaultwarden nach Komodo
|
|
fertig. Aber SERVICES_RECOVERY.md Stufe C setzt Vaultwarden als optionale
|
|
Vorab-Quelle voraus. Ohne externe Notiz heisst das praktisch: Komodo kann
|
|
nicht starten. Die Konsequenz steht nirgendwo explizit in DR.md.
|
|
|
|
Vorschlag: In `DISASTER_RECOVERY.md` Phase 4 Stufe 3 einen Hinweisblock
|
|
ergaenzen: "KOMODO_*-Werte muessen vor Stufe 3 aus externer Notiz oder
|
|
einer in Stufe 2 voraus gezogenen Vaultwarden-Instanz vorliegen. Default-
|
|
Pfad: externe Notiz."
|
|
|
|
### P2-3 (MED) - `homelab_smtp_password.txt` fehlt in DR.md 6.1
|
|
|
|
`docs/SECRETS_MAP.md:20` listet `/mnt/user/appdata/secrets/
|
|
homelab_smtp_password.txt` fuer Vaultwarden-SMTP. In `DISASTER_RECOVERY.md`
|
|
Abschnitt 6.1 (`docs/DISASTER_RECOVERY.md:136-151`) ist sie nicht
|
|
aufgefuehrt. Vaultwarden startet ohne, kann aber keine Einladungs-/
|
|
Benachrichtigungs-Mails versenden. Klein, aber unsichtbarer Folgefehler im
|
|
Familien-Onboarding-Pfad.
|
|
|
|
### P2-4 (MED) - `n8n_encryption_key.txt` fehlt in DR.md 6.1
|
|
|
|
`docs/SECRETS_MAP.md:58` listet `/mnt/user/appdata/secrets/
|
|
n8n_encryption_key.txt`. In DR.md 6.1 fehlt sie komplett.
|
|
`SECRETS_MAP.md:135` macht die Folgen explizit: "Bei Verlust aller
|
|
Quellen: n8n startet, aber alle gespeicherten Credentials sind unbrauchbar".
|
|
Da n8n den GMX-Mail-Workflow fuer das Gitea-`Micha/mails`-Repo betreibt,
|
|
ist das ein direkter Workflow-Ausfall.
|
|
|
|
### P2-5 (LOW) - Monitoring-/Filebrowser-Secrets fehlen in DR.md 6.1
|
|
|
|
`docs/SECRETS_MAP.md:53-55`: `influxdb3_admin_token.json`,
|
|
`monitoring_grafana_admin_password.txt`,
|
|
`monitoring_grafana_influxdb_token.txt` sowie
|
|
`filebrowser_admin_password.txt` sind nicht in DR.md 6.1. Tier-3-Apps,
|
|
Folge ist nur ein UI-Initialisierungs-Schritt nach Wiederanlauf. Keine
|
|
Critical-Konsequenz, aber Inkonsistenz.
|
|
|
|
## Phase 3 - Borg-Extract
|
|
|
|
### P3-1 (HIGH) - Borg-Client ohne `borg-ui`-Container
|
|
|
|
`docs/RESTORE_HANDBOOK.md:30-33` sagt explizit: "Borg-Zugriff laeuft ueber
|
|
den vorhandenen `borg-ui`-Container".
|
|
|
|
Im Bare-Metal-Fall ist `borg-ui` selbst kalt (Tier 3, DR.md Phase 4 Stufe 5).
|
|
Es gibt keinen dokumentierten Pfad, wie der erste Borg-Extract ohne diesen
|
|
Container laeuft. Implizite Optionen: nativer Borg auf Unraid (Plugin),
|
|
`docker run --rm borgbackup/borg`, oder Operator-Laptop. Keine davon ist
|
|
benannt.
|
|
|
|
Vorschlag: In `RESTORE_HANDBOOK.md` Abschnitt 2 einen "Bare-Metal-Vorlauf"
|
|
ergaenzen, der den initialen Borg-Extract ohne borg-ui-Container
|
|
beschreibt - z. B. `docker run --rm -v
|
|
/mnt/user/backups/restore-lab:/restore borgbackup/borg ...`.
|
|
|
|
### P3-2 (HIGH) - Borg-Passphrase-Bootstrap nicht als expliziter Schritt
|
|
|
|
`docs/DISASTER_RECOVERY.md:68`: "Host-Secret-Datei vorhanden und fuer
|
|
Borg-Zugriff verifiziert; externe Offline-Hinterlegung vom Operator am
|
|
2026-05-26 bestaetigt."
|
|
|
|
Praktisch heisst das: im Bare-Metal-Fall liest der Operator die Passphrase
|
|
aus einem analogen Medium und tippt sie in den Borg-Client. Das ist ein
|
|
**Bootstrap-Schritt**, der nicht als Schritt dokumentiert ist. Er steckt
|
|
implizit in "extern bestaetigt".
|
|
|
|
Vorschlag: Ein nummerierter Bullet in `DISASTER_RECOVERY.md` Phase 3 ("Wenn
|
|
echte Daten aus Borg benoetigt werden"): "Schritt 1: Borg-Passphrase aus
|
|
Offline-Sicherung beschaffen. Wert wird nicht in Skripte oder Tickets
|
|
kopiert; nur in den interaktiven Borg-Aufruf eingegeben."
|
|
|
|
### P3-3 (MED) - Hetzner-Maintenance-Key im Bare-Metal
|
|
|
|
`docs/EXTERNAL_DEPENDENCIES.md:17`: "Maintenance-Key liegt in Vaultwarden".
|
|
|
|
Im Bare-Metal-Bootstrap ist Vaultwarden Phase 4 Stufe 4. Damit ist der Key
|
|
fuer die initiale Phase-3-Hetzner-Verbindung nicht zugaenglich. Implizit
|
|
muss er ebenfalls offline gesichert sein (analog Borg-Passphrase).
|
|
|
|
Vorschlag: gleiche Form wie Borg-Passphrase - eine Operator-Bestaetigung
|
|
in `EXTERNAL_DEPENDENCIES.md`, dass der Hetzner-SSH-Key auch ausserhalb von
|
|
Vaultwarden offline verfuegbar ist. Sonst ist die "Vaultwarden"-Aussage
|
|
fuer Bare-Metal eine Falle.
|
|
|
|
## Phase 4 - Bootstrap-Reihenfolge
|
|
|
|
### P4-1 (HIGH) - Externe Docker-Netze in DR.md Phase 4 Stufe 1 nicht erwaehnt
|
|
|
|
`docs/SERVICES_RECOVERY.md:82-84` Stufe A schreibt explizit: "Externe
|
|
Docker-Netze existieren oder werden erzeugt (`frontend_net`, `backend_net`).
|
|
Wenn nicht vorhanden: `docker network create --driver bridge frontend_net`
|
|
bzw. `... --internal backend_net`."
|
|
|
|
`docs/DISASTER_RECOVERY.md:252-260` Phase 4 Stufe 1 nennt nur Traefik,
|
|
AdGuard, Tailscale. Kein Hinweis auf externe Netze.
|
|
|
|
`traefik/docker-compose.yml:70-76` deklariert `frontend_net`, `backend_net`,
|
|
`monitoring_net` als `external: true`. Ohne vorab erstellte Netze scheitert
|
|
der erste `docker compose up` mit "network frontend_net not found".
|
|
|
|
Vorschlag: In `DISASTER_RECOVERY.md` Phase 4 vor Stufe 1 einen Vorlauf
|
|
"Stufe 0 - Docker-Grundlage" einfuegen, der die Netzwerk-Erzeugung wie in
|
|
`SERVICES_RECOVERY.md` Stufe A explizit listet.
|
|
|
|
### P4-2 (HIGH) - Cloudflare-LE-Rate-Limit-Risiko
|
|
|
|
`docs/RESTORE_MATRIX.md:30` markiert `letsencrypt` korrekt als
|
|
Restore-relevant. `docs/DISASTER_RECOVERY.md:240` listet
|
|
`/mnt/user/appdata/traefik/letsencrypt` ebenfalls als kritischen
|
|
Borg-Restore-Pfad.
|
|
|
|
Luecke: kein Hinweis auf den Praxisfall "LE-State verloren, frischer
|
|
Acme-Run". Let's Encrypt hat ein Rate-Limit von 50 Zertifikaten/Domain/
|
|
Woche und 5 Duplicate-Zertifikate/Woche. Bei einer Multi-Sub-Domain-
|
|
Konstellation wie `*.kaleschke.info` (15+ Hostnames) ist das beim
|
|
hektischen DR-Bootstrap erreichbar.
|
|
|
|
Vorschlag: In `DISASTER_RECOVERY.md` Phase 4 Stufe 1 einen Hinweis: "Bei
|
|
verlorenem oder unklarem `acme.json` zuerst gegen
|
|
`acme-staging-v02.api.letsencrypt.org` ausstellen lassen, erst nach
|
|
gruenem Smoke auf Production-CA umschalten." Ist eine Praesentations-
|
|
Aenderung in den Compose-Args, kein neuer Code.
|
|
|
|
### P4-3 (MED) - `traefik/dynamic/*` als Pre-Check fehlt
|
|
|
|
`docs/DISASTER_RECOVERY.md:357-365` Sektion 10 beschreibt die manuelle
|
|
Sonderregel fuer `traefik/dynamic/*`. Korrekt.
|
|
|
|
`docs/DISASTER_RECOVERY.md:252-260` Phase 4 Stufe 1 verweist nicht auf
|
|
diese Sonderregel. Wer der Reihenfolge folgt und Sektion 10 nicht liest,
|
|
startet Traefik ohne Middlewares - alle 2FA-Routen brechen still.
|
|
|
|
Vorschlag: Cross-Reference in Phase 4 Stufe 1: "Vor `docker compose up
|
|
traefik` pruefen, dass `/mnt/user/appdata/traefik/dynamic/middlewares.yml`,
|
|
`tls.yml`, `dashboards.yml` vorhanden sind (Sonderregel Sektion 10)."
|
|
|
|
### P4-4 (HIGH) - Authelia "frische Postgres ohne Dump"-Pfad fehlt
|
|
|
|
`docs/DISASTER_RECOVERY.md:267-275` Phase 4 Stufe 2 startet Postgres und
|
|
Authelia. Authelia erwartet eine Rolle `authelia` mit dem Passwort aus
|
|
`authelia_postgres_password.txt`. Im Restore-Pfad mit `pg_dumpall --globals-
|
|
only` ist die Rolle abgedeckt.
|
|
|
|
Bei einem **fresh-start** (keine alten Daten, nur Container hochfahren) ist
|
|
die Rolle nicht da. Postgres-Image legt sie nicht automatisch an. Authelia
|
|
schlaegt mit "FATAL: role authelia does not exist" fehl.
|
|
|
|
Luecke: Der Initialisierungspfad fuer eine frische Postgres ohne
|
|
pg_dumpall ist in der Doku nicht beschrieben. Im echten DR mit Borg ist
|
|
das unwahrscheinlich, aber im Soft-Recovery oder Migrations-Drill schon.
|
|
|
|
Vorschlag: In `DISASTER_RECOVERY.md` Phase 4 Stufe 2 eine optionale
|
|
Anweisung: "Falls Postgres frisch ist (kein Dump-Restore), `infra/
|
|
postgresql17/init/`-Skripte oder manuelle `CREATE ROLE`/`CREATE DATABASE`-
|
|
Schritte ergaenzen."
|
|
|
|
### P4-5 (LOW) - Gitea nach Postgres ist faktisch unnoetig
|
|
|
|
`docs/DISASTER_RECOVERY.md:267-275` Phase 4 Stufe 2 ordnet Gitea hinter
|
|
Postgres ein. Gitea nutzt SQLite (`gitea.sqlite.dump`), nicht den shared
|
|
Postgres. Reihenfolge ist nicht falsch, aber irrefuehrend. Nicht kritisch.
|
|
|
|
### P4-6 (HIGH) - Komodo-Mongo Passwort-Lockout-Risiko
|
|
|
|
`ops/komodo/docker-compose.yml:18-20` zeigt: `komodo-mongo` initialisiert
|
|
sich bei leerem Datadir mit `MONGO_INITDB_ROOT_PASSWORD_FILE` aus
|
|
`/mnt/user/appdata/secrets/komodo_mongo_password.txt`.
|
|
|
|
Restore-Fall: Datadir aus Borg restauriert, Secret-Datei aus Borg
|
|
restauriert - beide aus demselben Snapshot. OK.
|
|
|
|
Riskanter Fall: Datadir aus Borg, aber Secret-Datei aus einer anderen
|
|
(neueren oder aelteren) Quelle. Mongo akzeptiert den Login nicht, Komodo
|
|
laeuft nicht. Lockout. Doku erwaehnt diesen Pin-Punkt nicht.
|
|
|
|
Vorschlag: Hinweis in `DISASTER_RECOVERY.md` Phase 4 Stufe 3: "Mongo-
|
|
Datadir und `komodo_mongo_password.txt` muessen aus demselben Snapshot
|
|
kommen. Bei Mismatch: leeren Datadir und Re-Init, dann Daten aus
|
|
`komodo-mongo.archive.gz` per `mongorestore`."
|
|
|
|
### P4-7 (MED) - Hardgecodete LAN-IP in `extra_hosts`
|
|
|
|
`ops/komodo/docker-compose.yml:50` und `:101` haben:
|
|
`"git.kaleschke.info:192.168.178.58"`.
|
|
|
|
Bare-Metal-Recovery auf anderer Hardware oder veraenderter LAN-IP fuehrt
|
|
zu stummem Fehler: Komodo-Core kann Gitea nicht ueber den Override
|
|
erreichen, faellt auf AdGuard-DNS zurueck (wenn der schon laeuft) oder
|
|
scheitert.
|
|
|
|
Vorschlag: kurzer Hinweis in `DISASTER_RECOVERY.md` Phase 4 Stufe 3: "Bei
|
|
geaenderter Host-LAN-IP `extra_hosts`-Werte in `ops/komodo/docker-compose.
|
|
yml` vor `compose up` anpassen oder ueber `.env` parametrisieren."
|
|
|
|
### P4-8 (HIGH) - Stack-ENV-Wiederherstellung praktisch manuell
|
|
|
|
`docs/DISASTER_RECOVERY.md:188-195` sagt: "Wenn `komodo-mongo.archive.gz`
|
|
frisch ist, koennen die Werte beim Komodo-Restart aus dem Dump
|
|
zurueckgespielt werden, ohne dass jemand sie sieht."
|
|
|
|
`docs/RESTORE_HANDBOOK.md:73-74` und `docs/AUDIT_2026-05-25_TODO.md:20`
|
|
machen den Daten-Mongo-Restore als "erledigt 2026-06-03" sichtbar - aber
|
|
NICHT als Teil des DR-Bootstraps. Komodo-Bootstrap im Trockenlauf benutzt
|
|
Wegwerf-Werte.
|
|
|
|
Praktisch heisst das: Im DR-Bootstrap (Phase 4 Stufe 3) startet Komodo
|
|
**ohne** den Mongo-Daten-Restore. Die `KOMODO_*` kommen aus externer
|
|
Notiz. Aber die Stack-ENVs fuer `paperless`/`immich`/`mail-archiver`/
|
|
`speedtest` (PAPERLESS_DBPASS etc.) **muessen vor Stufe 4** wieder in
|
|
Komodo eingetragen sein. Wenn der Mongo-Daten-Restore nicht direkt nach
|
|
Komodo-Start passiert, gehen diese Werte manuell in die Komodo-UI.
|
|
|
|
Vorschlag: Klarstellung in `DISASTER_RECOVERY.md` Phase 4 zwischen Stufe
|
|
3 und Stufe 4: "Optionaler Mongo-Daten-Restore aus `komodo-mongo.archive.
|
|
gz` per `ops/restore-tests/komodo-mongo-restore-test.sh`-Muster - dann
|
|
sind alle Stack-ENVs zurueck. Alternativ: Stack-ENVs manuell in Komodo-
|
|
UI eintragen, Quelle Vaultwarden (sobald Stufe 4 Vaultwarden laeuft -
|
|
Henne-Ei mit Paperless: Paperless-Start dann erst nach Vaultwarden, nicht
|
|
parallel)."
|
|
|
|
## Phase 5 - Verifikation
|
|
|
|
### P5-1 (LOW) - Smoke-Tests in DR.md weniger streng als Matrix
|
|
|
|
`docs/DISASTER_RECOVERY.md:337-345` Phase 5.3 sagt z. B. "Vaultwarden
|
|
startet und ist erreichbar". `docs/RESTORE_MATRIX.md:39` sagt: "Login-
|
|
Seite erreichbar, Tresor-Daten sichtbar". Das zweite ist faktisch der
|
|
echte Smoke-Test.
|
|
|
|
Geschmackssache, kein Bug. Empfehlung: DR.md auf die Matrix-Smokes
|
|
verweisen statt eigene Kurzversion.
|
|
|
|
### P5-2 (MED) - Kein Verifikationspunkt App-zu-DB-Verbindung
|
|
|
|
`docs/DISASTER_RECOVERY.md:337-345` prueft App-Start, nicht DB-Auth-
|
|
Erfolg. Bei falschem `PAPERLESS_DBPASS`-Stack-ENV startet Paperless
|
|
moeglicherweise mit Error-Log und ist via Traefik nicht antwortend - aber
|
|
das fehlt als Pruefpunkt.
|
|
|
|
Vorschlag: Phase 5.3 ergaenzen: "Pro App: `docker logs <app>` zeigt keine
|
|
`password authentication failed`/`FATAL: role does not exist`-Eintraege."
|
|
|
|
## Uebergreifende Findings
|
|
|
|
### X-1 (HIGH) - Nextcloud-Restore-Skript existiert, ist aber ungetestet
|
|
|
|
`ops/restore-tests/nextcloud-restore-test.sh` und
|
|
`ops/restore-tests/nextcloud-compose.test.yml` existieren im Repo.
|
|
`docs/RESTORE_MATRIX.md:147` Spalte "Letzter Restore-Test" fuer Nextcloud:
|
|
`-`, naechster Lauf `**hoechste Prio**`. `docs/AUDIT_2026-05-25_TODO.md:18`
|
|
fuehrt es als P1 "offen".
|
|
|
|
Damit ist der echte Tabletop-Gewinn: der Test ist nicht "noch zu bauen",
|
|
sondern "noch nie ausgefuehrt". Ein `bash /mnt/user/services/homelab-
|
|
infra/ops/restore-tests/nextcloud-restore-test.sh` schliesst die letzte
|
|
Tier-2-Luecke.
|
|
|
|
## Nicht-Findings
|
|
|
|
Was ich gepruft und als sauber verifiziert habe:
|
|
|
|
- Referenzierte Skripte existieren alle: `pre-backup-dumps.sh`,
|
|
`gitea-bundle-mirror.sh`, `run-restore-checks.sh`,
|
|
`komodo-bootstrap-test.sh`, `posture-check.sh`, alle Restore-Test-
|
|
Skripte fuer Tier-1 und Tier-2.
|
|
- Pfadverweise zwischen DR.md, RESTORE_MATRIX.md, SECRETS_MAP.md,
|
|
SERVICES_RECOVERY.md sind konsistent (Borg-Dumps unter `/mnt/user/
|
|
backups/borg/dumps/latest`, Secrets unter `/mnt/user/appdata/secrets`).
|
|
- Drift-Erkennung Authelia (`services/authelia-diff.sh`) ist in
|
|
`posture-check` integriert (`WORKFLOW.md:292`).
|
|
- GitHub-Mirror-Pfad und Gitea-Bundle-Mirror als Repo-Bootstrap-Quellen
|
|
sind dreifach abgesichert (lokaler Clone, GitHub, Bundle).
|
|
- Tier-1-Postgres-Restore-Drill ist 2026-06-03 erfolgreich gelaufen
|
|
(`AUDIT_2026-05-25_TODO.md:19`).
|
|
- `ops/komodo/docker-compose.yml` ist als Recovery-Anker getestet
|
|
(`SERVICES_RECOVERY.md:142-166`).
|
|
- Borg-Passphrase und Hetzner-Account-Hygiene sind Operator-bestaetigt
|
|
(`AUDIT_2026-05-25_TODO.md:46-47`).
|
|
|
|
## Vorschlag fuer Reihenfolge der Folge-Arbeit
|
|
|
|
1. **CRITICAL P1-1 zuerst** - Operator-Laptop-Voraussetzung als
|
|
DR-Pflichtposten dokumentieren. Eine Dokuzeile.
|
|
2. **HIGH P0-2 + P3-3** - klaeren, ob GitHub-Mirror lesend public ist und
|
|
wo der Hetzner-Maintenance-Key offline liegt. Zwei Dokuzeilen oder
|
|
eine echte Setup-Entscheidung.
|
|
3. **HIGH P2-1** - Operator-Bestaetigung "KOMODO_*-Notiz offline
|
|
gesichert YYYY-MM-DD" in `EXTERNAL_DEPENDENCIES.md` ergaenzen (sobald
|
|
real angelegt).
|
|
4. **HIGH P4-1 + P4-2** - Vorlauf "Stufe 0 - Docker-Grundlage" und
|
|
LE-Staging-Hinweis in DR.md Phase 4 einfuegen. Etwa 10 Zeilen Doku.
|
|
5. **HIGH X-1** - `nextcloud-restore-test.sh` einmal scharf ausfuehren.
|
|
Vermutlich ein Vormittag inklusive Report-Review.
|
|
6. **HIGH P2-2 + P4-8** - Reihenfolgen-Konsistenz Komodo/Vaultwarden in
|
|
DR.md eindeutig aufloesen.
|
|
7. Rest in der Reihenfolge der Tabelle.
|
|
|
|
Punkte 1-4 sind reine Doku-Arbeit, keine Compose-/Runtime-Aenderung.
|
|
Punkt 5 ist ein echter Restore-Lauf mit Report. Punkt 6 ist die
|
|
substanziellste Doku-Aenderung in DR.md.
|
|
|
|
## Folge-Iteration 2026-06-03 (Doku-Fixes im selben Aenderungsblock)
|
|
|
|
Direkt nach dem Drill und nach Operator-Antworten auf vier offene Fragen wurden folgende Findings im Repo adressiert. Operator-Aufgaben, die ich nicht selbst tun kann, sind als P1 in `docs/AUDIT_2026-05-25_TODO.md` aufgenommen.
|
|
|
|
| ID | Massnahme |
|
|
|---|---|
|
|
| P0-1 | DR.md Phase 0 ergaenzt um "Operativer Pfad fuer den Repo auf den frisch installierten Unraid-Host" (USB/SMB/rsync); DR.md Abschnitt 3 mit Zeile "Operator-DR-Workstation"; `EXTERNAL_DEPENDENCIES.md` neuer Abschnitt "DR-Workstation Bare-Metal-Kit" |
|
|
| P0-2 | `EXTERNAL_DEPENDENCIES.md` GitHub-Mirror-Zeile praezisiert (privat, Read-PAT/Deploy-Key Pflicht); DR.md Phase 0 verweist explizit darauf; offene Operator-Aufgabe in Audit-Restliste |
|
|
| P1-1 | Operator-DR-Workstation als Voraussetzung in DR.md Abschnitt 3 und in `EXTERNAL_DEPENDENCIES.md`; konkrete Pflichtbestandteile (WSL2, Borg, SSH-Key) gelistet |
|
|
| P1-2 | Bleibt offen als P3-Test in Restore-Backlog (kein Doku-Fix moeglich) |
|
|
| P2-1 | KOMODO_*-Notiz als kritische Secret-Zeile in `EXTERNAL_DEPENDENCIES.md` mit Status "noch nicht angelegt"; Operator-Aufgabe in Audit-Restliste |
|
|
| P2-2 | DR.md Phase 4 Stufe 3 ergaenzt um expliziten Hinweis "KOMODO_* aus externer Notiz oder voraus gezogener Vaultwarden" |
|
|
| P2-3 | DR.md Abschnitt 6.1 um `homelab_smtp_password.txt` erweitert |
|
|
| P2-4 | DR.md Abschnitt 6.1 um `n8n_encryption_key.txt` erweitert |
|
|
| P2-5 | DR.md Abschnitt 6.1 um Monitoring-Grafana/InfluxDB-/Filebrowser-Secrets erweitert |
|
|
| P3-1 | DR.md neuer Abschnitt 7.3 "Borg-Extract ohne `borg-ui`-Container" mit DR-Workstation- und Docker-Variante |
|
|
| P3-2 | DR.md Abschnitt 7.3 nennt Passphrase-Eingabe explizit als interaktiven Bootstrap-Schritt |
|
|
| P3-3 | `EXTERNAL_DEPENDENCIES.md` Review-Zeile 2026-06-03: Hetzner-Maintenance-Key auch offline bestaetigt |
|
|
| P4-1 | DR.md Phase 4 neue Stufe 0 "Docker-Grundlage" mit `docker network create` Befehlen |
|
|
| P4-2 | DR.md Phase 4 Stufe 1 LE-Staging-Hinweis bei verlorenem `acme.json` |
|
|
| P4-3 | DR.md Phase 4 Stufe 0 nennt `traefik/dynamic/*` als Pre-Check |
|
|
| P4-4 | Wird mit fresh-Postgres-Initialisierungsskripten ohne Doku-Aenderung nicht sinnvoll abgedeckt; bleibt als Doku-Hinweis offen, ist im realen Restore-Pfad mit `pg_dumpall --globals-only` abgedeckt |
|
|
| P4-5 | LOW, nicht angepasst (Reihenfolge nicht falsch, nur irrefuehrend) |
|
|
| P4-6 | DR.md Phase 4 Stufe 3 "Wichtige Stolperfallen": Mongo-Datadir/Secret-Mismatch und Re-Init-Pfad |
|
|
| P4-7 | DR.md Phase 4 Stufe 3 "Wichtige Stolperfallen": `extra_hosts`-Anpassung bei IP-Wechsel |
|
|
| P4-8 | DR.md Phase 4 Stufe 3 "Wichtige Stolperfallen": Stack-ENV-Wiederherstellung per `mongorestore` oder manuell |
|
|
| P5-1 | LOW, nicht angepasst |
|
|
| P5-2 | DR.md Phase 5.3 um `docker logs`-Verifikation der App-zu-DB-Verbindung erweitert |
|
|
| X-1 | **erledigt 2026-06-03**: Nextcloud-Restore-Test scharf gelaufen, drei Iterationen (zwei Skript-Bugs gefixt), Endresultat SUCCESS mit HTTP 200, occ status ok, 126 DB-Tabellen. Damit ist Tier-2 vollstaendig belegt. |
|
|
|
|
Nicht angefasst: P1-2 (kein Doku-Fix moeglich), P4-4 (im echten Restore-Pfad ohnehin abgedeckt), P4-5 und P5-1 (LOW). Die offenen Operator-Aufgaben (KOMODO_*-Notiz, Read-PAT, DR-Workstation, Nextcloud-Restore) stehen jetzt in `docs/AUDIT_2026-05-25_TODO.md` als P1.
|
|
|
|
## Reproduktion dieses Drills
|
|
|
|
```text
|
|
Methode: kalter Lesetest gegen
|
|
- docs/DISASTER_RECOVERY.md
|
|
- docs/RESTORE_MATRIX.md
|
|
- docs/SECRETS_MAP.md
|
|
- docs/SERVICES_RECOVERY.md
|
|
- docs/RESTORE_HANDBOOK.md
|
|
- docs/EXTERNAL_DEPENDENCIES.md
|
|
- ops/komodo/docker-compose.yml
|
|
- traefik/docker-compose.yml
|
|
Verifizierte Skript-Existenz: ops/borg-ui/scripts/*, ops/restore-tests/*,
|
|
services/posture-check/*
|
|
Kein Container gestartet, kein Skript ausgefuehrt, keine produktiven
|
|
Pfade beruehrt.
|
|
```
|