Expliziten 2FA-Block auf files/scrutiny reduziert (borg/code sind via
Catch-all *.kaleschke.info=two_factor weiterhin 2FA). Damit matcht die
Repo-access_control-Sektion den Host-Stand -> authelia-diff.sh wird clean,
sobald der Host-Repo-Mirror auf diesen Commit gezogen ist.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Schritt-fuer-Schritt Runbook fuer den letzten verbleibenden P1-Operator-
Punkt: WSL2 + Borg-Client + SSH-Keys + Quartals-Smoke-Skript auf dem
Operator-Gaming-PC einrichten.
7 Schritte, ~30-60 Min einmaliger Aufwand. Inhalt:
- WSL2 Ubuntu installieren
- borgbackup installieren
- Hetzner-DR-Key aus offline-USB nach ~/.ssh kopieren
- borg list Smoke gegen Hetzner Storage Box
- GitHub-Deploy-Key analog
- dr-smoke.sh Quartals-Skript ablegen
- Bestaetigung in EXTERNAL_DEPENDENCIES und AUDIT-Restliste nachziehen
Troubleshooting-Sektion fuer die haeufigsten Stolpersteine
(WSL-Update, Key-Permissions, Port-23-Block, HTTPS-vs-SSH-URL).
REPO_MAP.md um Verweis auf das neue Runbook ergaenzt.
Wenn dieses Runbook abgearbeitet ist, sind alle vier Bare-Metal-DR-Pillars
produktionsreif.
Zweiter Lauf am 2026-06-03 ergab nach dem ersten Fix (config-Permissions)
einen neuen Fehler: HTTP 503 mit "Your data directory is invalid. Ensure
there is a file called .ncdata in the root of the data directory."
Hintergrund: Nextcloud prueft beim HTTP-Request eine Marker-Datei `.ncdata`
mit festem Inhalt im Datenverzeichnis. Produktiv liegt der Marker unter
/mnt/user/documents/nextcloud-data/.ncdata. Der Smoke-Test mountet diesen
Pfad bewusst nicht, also war das Test-data-Verzeichnis leer und Nextcloud
hat den Marker vermisst.
Fix: Marker vor dem Container-Start anlegen. Die anderen Tier-2-Tests
(Paperless, Mealie, Mail-Archiver) brauchten so etwas nicht, weil ihre
Apps keine entsprechende Validierungs-Pruefung haben.
Erster Lauf am 2026-06-03 lief sauber durch alle Phasen (Borg-Extract,
pg_restore, Container alle gesund), schlug aber im HTTP-Smoke mit 503 fehl.
Ursache (aus dem preserved /mnt/user/backups/restore-lab/_failed/...):
- OC_Util.php:486 prueft die Permissions der data-Dir
- Skript hatte chmod -R a+rwX gesetzt (0777, letzte Stelle 7)
- Nextcloud versucht selbst chmod(0770) als www-data im Container
- Unraids shfs/FUSE lehnt chmod von Non-Root ab
- Nextcloud meldet "data directory readable by other people" -> 503
Fix: in der gepatchten config.php zusaetzlich
'check_data_directory_permissions' => false setzen. Nextcloud bietet
das in OC_Util:480 explizit als Opt-out an, fuer den isolierten Smoke
mit Wegwerf-Daten ist das vertretbar (kein Public, kein Traefik).
Produktiv bleibt der Check natuerlich an.
Patching erfolgt im bestehenden PHP-Injection-Block; idempotent (laeuft
keine Aenderung wenn beide Keys schon im config.php sind). Fallback-
sed-Pfad fuer Hosts ohne php ebenfalls erweitert.
Dritte der vier P1-Operator-Aufgaben aus dem DR-Tabletop teil-erledigt.
Die SSH-Schicht der DR-Workstation steht; verbleibend ist die
WSL2+Borg-Installation auf dem Gaming-PC.
Was passiert ist:
- ed25519-Keypair `dr-hetzner-2026-06-03` (Passphrase-frei) lokal erzeugt
- Public Key per `install-ssh-key` auf der Hetzner Storage Box autorisiert
- Smoke `ssh -p23 ... ls` passwortlos erfolgreich, vier Borg-Repos
sichtbar (`backup`, `backup2`, `hetzner_borg_appdata`,
`hetzner_borg_appdata_critical`)
- Private Key offline neben KOMODO_*-Notiz und GitHub-Deploy-Key gelegt
- Arbeitsplatz-Kopie nach USB-Transfer geloescht
EXTERNAL_DEPENDENCIES.md:
- DR-Workstation-Kit-Tabelle: SSH-Key-Zeile auf "offline gesichert"
- Review-Zeile 2026-06-03 erweitert mit Smoke-Ergebnis
AUDIT_2026-05-25_TODO.md:
- P1-Eintrag DR-Workstation umformuliert: SSH-Key ist erledigt,
Verbleibend ist nur noch WSL2 + Borg-Client-Installation
- Eintrag unter "Zuletzt geschlossen" mit Wirkung
Stand der DR-Bare-Metal-Pillars:
1. KOMODO_*-Notiz offline erledigt
2. GitHub-Mirror Read-Only Deploy-Key offline erledigt
3. Hetzner Storage Box DR-SSH-Key offline erledigt
4. WSL2 + Borg-Client auf DR-Workstation installiert offen
5. Nextcloud-Restore-Test als letzte Tier-2-Luecke schliessen offen
Befund: Drei `grep -R ... /usr/local/emhttp`-Prozesse aus einem FCP-Daily-
Scan-Run hingen seit ~7 Tagen in einem Symlink-Loop. Unraids
`/usr/local/emhttp/mnt` ist ein Symlink nach `/mnt` (mehrere TB Array);
GNU `grep -R` dereferenziert Symlinks, also walking die FCP-Scan-Greps
effektiv das gesamte Array. 3 Cores dauerhaft 100 %, IOWAIT-Peaks 55 %,
USB-Flash unter Dauer-IO, Load 14.6 auf 12 Cores.
Massnahme: `plugin remove fix.common.problems.plg`. Cron, Plugin-Dir
und /tmp-Reste sauber. Load von 14.6 auf 1.08 (1-min) gefallen.
Entscheidung: FCP wird bewusst nicht reinstalliert. Begruendung im
Architektur-Master Sektion 13. Verbleibende Risiken decken Scrutiny,
Monitoring, Posture-Check und Critical-Events-Watcher bereits ab.
Repo-Aenderungen:
- HOMELAB_ARCHITECTURE_MASTER_V2.md Sektion 13: vollstaendiger
Entscheidungs-Log-Eintrag mit Ursache, Massnahme, Begruendung
- AUDIT_2026-05-25_TODO.md "Zuletzt geschlossen": Kurzfassung
Host-Aenderung wurde via SSH durchgefuehrt (read+remove), keine
Compose-/Container-Aenderungen.
Zweite der vier P1-Operator-Aufgaben aus dem DR-Tabletop erledigt.
Was passiert ist:
- SSH-Keypair `dr-readonly-2026-06-03` (ed25519, Passphrase-frei) erzeugt
- Public Key in GitHub Repo Settings -> Deploy Keys ohne Write-Access
hinterlegt (Title `DR Read-Only 2026-06-03`)
- Smoke `git ls-remote git@github.com:michaelkaleschke-spec/homelab-infra.git`
erfolgreich (HEAD `d947c7f` matched origin/master)
- Private Key offline neben die KOMODO_*-Notiz gelegt
- Arbeitsplatz-Kopie auf dem Operator-PC nach USB-Transfer geloescht
EXTERNAL_DEPENDENCIES.md:
- GitHub-Mirror-Zeile von "noch nicht angelegt" auf "offline gesichert"
gezogen, inkl. Deploy-Key-Bezeichnung und Smoke-Bestaetigung
- DR-Workstation-Kit-Tabelle: Quartals-Smoke-Befehl mit konkretem
GIT_SSH_COMMAND-Aufruf dokumentiert
- Review-Zeile 2026-06-03 erweitert
AUDIT_2026-05-25_TODO.md:
- P1-Read-PAT-Eintrag aus offenen Punkten entfernt
- Eintrag unter "Zuletzt geschlossen" mit Wirkung
Zwei P1-Operator-Aufgaben bleiben offen: DR-Workstation-Setup,
Nextcloud-Restore-Test.
DR-Tabletop-Followup: erste der vier P1-Operator-Aufgaben erledigt.
EXTERNAL_DEPENDENCIES.md:
- KOMODO_*-Notiz-Zeile von "noch nicht angelegt" auf "offline gesichert
(Operator-Bestaetigung)" gezogen, mit Hinweis auf die Quelle der Werte
(Self-Stack-.env unter /mnt/user/services/stacks/komodo bzw. die
Drift-Recovery-Kopie vom 2026-05-04)
- DR-Workstation-Kit-Tabelle: Offline-Kopie-Status entsprechend aktualisiert
- Review-Zeile 2026-06-03 mit Bestaetigung ergaenzt
AUDIT_2026-05-25_TODO.md:
- P1-KOMODO_*-Notiz aus den offenen Punkten entfernt
- Eintrag unter "Zuletzt geschlossen" mit Quellenpfad und Wirkung
Drei P1-Operator-Aufgaben bleiben offen: GitHub-Read-PAT,
DR-Workstation-Setup, Nextcloud-Restore-Test.
Kalter Lesetest gegen das Bare-Metal-Szenario aus DR.md Phase 0 bis 5,
mit referenzierten Runbooks (SERVICES_RECOVERY, RESTORE_MATRIX,
SECRETS_MAP, RESTORE_HANDBOOK, EXTERNAL_DEPENDENCIES) und Compose-Ankern
(ops/komodo, traefik).
23 Findings mit Severity, Repo-Datei + Zeile, Fix-Vorschlag pro Punkt.
1x CRITICAL (Unraid-Flash-Restore ohne laufenden Host), 11x HIGH, 8x MED,
3x LOW.
Schwerpunkte:
- Bare-Metal-Operator-Workstation als DR-Voraussetzung nicht dokumentiert
- Henne-Ei: KOMODO_* externe Notiz vs. Vaultwarden-Reihenfolge
- Externe Docker-Netze fehlen in DR.md Phase 4 Stufe 1
- borg-ui-Container als impliziter Borg-Client im Bare-Metal-Bootstrap
- Nextcloud-Restore-Skript ist da, ist aber noch nie real gelaufen (X-1)
Keine produktiven Pfade beruehrt, kein Container gestartet, keine
Skripte ausgefuehrt - reiner Doku-Drill.
Beide UIs haben effektiv Host-/Backup-Zugriff (Borg-Restore-Scope inkl.
/local/secrets, code-server mit Workspace-Mounts). Bisher liefen sie ueber
die catch-all-Regel mit nur one_factor. Files und Scrutiny waren bereits
two_factor; die Liste wird konsistent gezogen.
Wirkung erst nach manuellem Host-Merge (Ausnahme laut docs/WORKFLOW.md):
1. /mnt/user/appdata/authelia/config/configuration.yml mergen
2. docker restart authelia
3. Smoke-Test auf einer der vier 2FA-Domains
4. services/authelia-diff.sh muss exit 0 liefern
Audit-Restliste nachgezogen: Tier-1-Operator-2FA geschlossen, restliche
geparkte Auth-Themen (OIDC, CrowdSec, Nextcloud-2FA) bewusst weiter offen
mit aktualisierter Begruendung.
Traefik-Restore am 2026-06-03 erfolgreich: dynamic/ (2 Files) +
letsencrypt/acme.json (426K) aus Borg, File-Provider-Boot, /ping 200.
Erster Versuch, kein shfs-Problem.
11 von 12 Restore-Tests sind jetzt gruen. Einzig Nextcloud bleibt
blockiert durch Unraids shfs-chmod-Inkompatibilitaet.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Borg-Extract von dynamic/ und letsencrypt/, Traefik startet mit
File-Provider gegen restaurierte Config, /ping Health antwortet.
Bewusst kein docker.sock (wuerde produktive Container discovern),
kein CF-Token (keine DNS-Challenge), keine produktiven Ports.
acme.json-Existenz und -Groesse wird geprueft, TLS-Validitaet nicht.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Mail-Archiver-Restore am 2026-06-03 erfolgreich: Data-Protection-Keys
aus Borg + 645M pg_restore + HTTP 200. Erster Versuch, kein shfs-Problem.
10 von 12 Restore-Tests sind jetzt gruen. Verbleibend: Nextcloud
(blockiert/shfs-chmod) und Traefik (komplex, niedrigere Prio).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Borg-Extract der Data-Protection-Keys + pg_restore des 645M
mailarchiver-Dumps in isoliertes Test-Postgres + Container-Boot +
HTTP-Smoke. Wegwerf-DB-Connection und Auth-Password, kein produktiver
Stack-ENV, kein Authelia-ForwardAuth im Smoke.
Machbarkeit vorab verifiziert: Dump vorhanden, App-Image gepinnt,
Data-Protection-Keys im Borg, .NET-App hat kein shfs-chmod-Problem.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Mealie-Restore-Test am 2026-06-03 erfolgreich: Borg-Data + pg_restore
+ HTTP 200, 3 Rezepte im Test-DB-Check. Erster Versuch, kein
shfs-Problem (Mealie startet als root, kein chmod auf User Shares).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Negativ-Test 2026-06-03: korrupter mealie.dump wurde nicht erkannt,
weil der Docker-Fallback-Pfad nach gescheitertem pg_restore --list
zu return 2 (unchecked) durchfiel statt return 1 (invalid).
Fix: explizites if/else statt &&-Kette, damit fehlgeschlagene
Header-Validierung return 1 liefert und als DUMP_HEADER_INVALID
in den Critical-Zaehler geht.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Produktive Mongo ist 8.0.23, Test-Composes pinnten noch 7.0.32.
Eliminiert die Cross-Version-Warnung beim mongorestore.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Kompletter Restore-Drill fuer den Shared-PostgreSQL-18-Cluster:
globals (Rollen) + 5 per-DB Custom-Format-Dumps (paperless,
mailarchiver, authelia, nextcloud, mealie).
Bekannter mailarchiver-Bootstrap-Rollenkonflikt wird toleriert.
Authelia/Nextcloud/Mealie-Dumps als optional markiert.
Tabellen-Count pro DB als fachlicher Sanity-Check.
Machbarkeit vorab verifiziert: alle Dumps auf Host vorhanden,
pg_restore im postgres:18.4-Image verfuegbar, Postgres auf shfs
bewiesen durch bestehende Tests.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Komodo-Mongo-Daten-Restore am 2026-06-03 erfolgreich: mongorestore
von komodo-mongo.archive.gz in Wegwerf-Mongo, 86904 Dokumente
(inkl. 32 Stack-Definitionen). Damit ist die kanonische Quelle fuer
KOMODO_*-Stack-ENV-Werte im DR-Fall als wiederherstellbar belegt.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Zweiter Lauf scheiterte mit Auth-Failure weil der Container-Name
restoretest-komodo-mongo mit dem alten Bootstrap-Test kollidierte
(stale Datadir auf shfs mit anderen Credentials).
Fix: eigenes Compose mit eigenem Container-Namen
(restoretest-komodo-mongorestore) und eigenem Project-Name, damit
keine Namenskollision mit dem bestehenden Bootstrap-Test entsteht.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>