diff --git a/docs/DISASTER_RECOVERY.md b/docs/DISASTER_RECOVERY.md index f731163..34547fc 100644 --- a/docs/DISASTER_RECOVERY.md +++ b/docs/DISASTER_RECOVERY.md @@ -393,7 +393,7 @@ Wenn weder externer Mirror noch lokaler Clone verfuegbar sind, ist `services/git - Unraid-USB-/Flash-Backup pruefen - Borg-Passphrase extern sicher hinterlegen - Komodo Stack-ENV-Werte zentral ausserhalb von Komodo dokumentieren -- Restore-Smoke-Test fuer mindestens einen weiteren kritischen Dienst nach Vaultwarden dokumentieren +- Restore-Smoke-Test fuer mindestens einen weiteren kritischen Dienst nach Vaultwarden und Gitea dokumentieren - `komodo-mongo`-Dump nach Major-Upgrades gezielt kontrollieren --- diff --git a/docs/MIGRATION_LOG.md b/docs/MIGRATION_LOG.md index 7ad87d1..dbd5281 100644 --- a/docs/MIGRATION_LOG.md +++ b/docs/MIGRATION_LOG.md @@ -27,6 +27,15 @@ Dieses Dokument ist nur noch ein historischer Verlauf. Der aktuelle operative Ab - Host-Secret-Datei `/mnt/user/appdata/secrets/borg_repo_passphrase.txt` fuer kuenftige Restore-Tests - Testdaten unter `/mnt/user/backups/restore-lab/vaultwarden/data` wurden nach erfolgreichem Lauf wieder bereinigt. +### 2026-05-07 - Gitea Restore-Test praktisch verifiziert + +- Erster echter Gitea-Mini-Restore gegen das produktive Borg-Repo `hetzner_borg_appdata_critical` erfolgreich durchgefuehrt. +- Restore lief isoliert nach `/mnt/user/backups/restore-lab/gitea`, nicht gegen produktive Pfade. +- Testinstanz `restoretest-gitea` wurde lokal auf `127.0.0.1:13000` und `127.0.0.1:12222` gestartet. +- HTTP 200, HTML-Titel und lokaler SSH-Port wurden erfolgreich bestaetigt. +- Report wurde unter `/mnt/user/backups/restore-reports/gitea-2026-05-07.md` geschrieben. +- Testdaten unter `/mnt/user/backups/restore-lab/gitea/data` wurden nach erfolgreichem Lauf wieder bereinigt. + ### 2026-05-06 - Komodo Webhook Secret getrennt - `KOMODO_WEBHOOK_SECRET` von `KOMODO_SECRET_KEY` getrennt und als eigene Stack-ENV-Variable dokumentiert. diff --git a/docs/RESTORE_MATRIX.md b/docs/RESTORE_MATRIX.md index 3cde72a..a5d769b 100644 --- a/docs/RESTORE_MATRIX.md +++ b/docs/RESTORE_MATRIX.md @@ -32,7 +32,7 @@ Sie ist die fachliche Ergaenzung zu `docs/DISASTER_RECOVERY.md`. | PostgreSQL 17 | Share + Dumps | `/mnt/user/appdata/postgresql17` | `postgresql17-globals.sql`, `postgresql17-mailarchiver.dump`, `postgresql17-paperless.dump`, optional `postgresql17-authelia.dump` | `postgres_password.txt` | `backend_net` | DB startet, Ziel-Datenbanken vorhanden | | Redis | Share / Host | `/mnt/user/appdata/redis` | keine | `redis_password.txt` | `backend_net` | Redis startet, Apps verbinden sich | | Authelia | Borg | `/mnt/user/appdata/authelia/config`, `/mnt/user/appdata/secrets/*authelia*` | Shared PostgreSQL, optional Dump `postgresql17-authelia.dump` | JWT/Session/Storage/Postgres-/SMTP-Secret-Dateien | PostgreSQL 17, Traefik, GMX SMTP | Login-Seite und ForwardAuth funktionieren; SMTP-Notifier startet; aktive Sessions werden nach Restart neu aufgebaut | -| Gitea | Borg / Share | `/mnt/user/services/gitea/data` | SQLite in `/data` | keine separaten Secret-Dateien dokumentiert | Traefik | Web-UI erreichbar, Repo sichtbar, SSH-Port reagiert | +| Gitea | Borg / Share | `/mnt/user/services/gitea/data` | SQLite in `/data` | `borg_repo_passphrase.txt` fuer Restore-Tests | Traefik | Web-UI erreichbar, Repo sichtbar, SSH-Port reagiert; Mini-Restore nach `/mnt/user/backups/restore-lab/gitea` am 2026-05-07 erfolgreich validiert | | Komodo | Borg / Share | `/mnt/user/appdata/komodo/core`, `/mnt/user/appdata/komodo/periphery` | `komodo-mongo.archive.gz` falls verifiziert | `komodo_mongo_password.txt`, `KOMODO_*` Stack ENV | Traefik, Mongo, Gitea | UI erreichbar, Periphery verbunden | | Vaultwarden | Borg / Share | `/mnt/user/appdata/vaultwarden` | dateibasiert | `vaultwarden_admin_token.txt`, `borg_repo_passphrase.txt` fuer Restore-Tests | Traefik | Login-Seite erreichbar, Tresor-Daten sichtbar; Mini-Restore nach `/mnt/user/backups/restore-lab/vaultwarden` am 2026-05-07 erfolgreich validiert | @@ -110,8 +110,8 @@ Die Dump-Erzeugung ist host-seitig ueber `ops/borg-ui/scripts/pre-backup-dumps.s Wenn weitere Restore-Uebungen dokumentiert werden sollen, sind diese Dienste besonders geeignet: -1. `gitea` -2. `paperless-ngx` +1. `paperless-ngx` +2. `gitea` 3. `vaultwarden` Sie liefern hohen Erkenntnisgewinn ohne den kompletten Homelab-Neuaufbau zu brauchen. diff --git a/ops/restore-tests/README.md b/ops/restore-tests/README.md index 5895c42..3ee63e7 100644 --- a/ops/restore-tests/README.md +++ b/ops/restore-tests/README.md @@ -23,7 +23,10 @@ Ziel: - `vaultwarden-restore-test.ps1`: erster Mini-Restore-Ablauf - `vaultwarden-plan.md`: konkreter Vaultwarden-Testplan - `vaultwarden-compose.test.yml`: isolierte Testinstanz fuer Vaultwarden -- spaeter weitere Tests fuer `gitea` und `paperless` +- `gitea-restore-test.ps1`: Gitea-Mini-Restore-Ablauf +- `gitea-plan.md`: konkreter Gitea-Testplan +- `gitea-compose.test.yml`: isolierte Testinstanz fuer Gitea +- spaeter weiterer Test fuer `paperless` ## Automatisierungsmodell @@ -49,11 +52,12 @@ Das ist das bevorzugte Muster fuer weitere dateibasierte Restore-Tests wie `gite ## Status -Aktuell ist hier nur die erste Repo-Vorbereitung angelegt. +Aktuell ist das erste validierte Muster vorhanden. -- noch kein echter Restore-Lauf -- noch keine Host-Pfade beschrieben -- noch keine Container gestartet -- erster V1-Ablauf ohne `ntfy`, mit Bereinigung nach Erfolg +- echter Vaultwarden-Restore am 2026-05-07 erfolgreich verifiziert +- echter Gitea-Restore am 2026-05-07 erfolgreich verifiziert +- Restore-Lab und Report-Pfade auf dem Host angelegt +- V1-Ablauf weiter ohne `ntfy`, mit Bereinigung nach Erfolg +- `paperless` ist der naechste Referenz-Restore Vor dem ersten echten Testlauf muessen Zielpfade, Quellpfade und Bereinigungsschritte bewusst freigegeben werden. diff --git a/ops/restore-tests/gitea-compose.test.yml b/ops/restore-tests/gitea-compose.test.yml new file mode 100644 index 0000000..d43da6a --- /dev/null +++ b/ops/restore-tests/gitea-compose.test.yml @@ -0,0 +1,23 @@ +services: + restoretest-gitea: + image: docker.gitea.com/gitea:1.25.4@sha256:17d18218be2dad1f8ed402a4f906989505c90ab8b66ee9befcecfb5d470133e7 + container_name: restoretest-gitea + restart: "no" + + environment: + USER_UID: "1000" + USER_GID: "1000" + GITEA__server__DOMAIN: 127.0.0.1 + GITEA__server__ROOT_URL: http://127.0.0.1:13000/ + GITEA__database__DB_TYPE: sqlite3 + GITEA__webhook__ALLOWED_HOST_LIST: "*" + + ports: + - "127.0.0.1:13000:3000" + - "127.0.0.1:12222:22" + + volumes: + - /mnt/user/backups/restore-lab/gitea/data:/data + + security_opt: + - no-new-privileges:true diff --git a/ops/restore-tests/gitea-plan.md b/ops/restore-tests/gitea-plan.md new file mode 100644 index 0000000..babd3d0 --- /dev/null +++ b/ops/restore-tests/gitea-plan.md @@ -0,0 +1,59 @@ +# 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/gitea-restore-test.ps1 b/ops/restore-tests/gitea-restore-test.ps1 new file mode 100644 index 0000000..4110054 --- /dev/null +++ b/ops/restore-tests/gitea-restore-test.ps1 @@ -0,0 +1,36 @@ +param( + [string]$BackupSource = "/mnt/user/backups/borg", + [string]$RestoreRoot = "/mnt/user/backups/restore-lab/gitea", + [string]$ReportRoot = "/mnt/user/backups/restore-reports", + [string]$BorgPassphraseFile = "/mnt/user/appdata/secrets/borg_repo_passphrase.txt", + [switch]$WhatIf +) + +$Mode = if ($WhatIf) { "WhatIf" } else { "PlanOnly" } + +Write-Output "Gitea restore test scaffold" +Write-Output "BackupSource: $BackupSource" +Write-Output "RestoreRoot: $RestoreRoot" +Write-Output "ReportRoot: $ReportRoot" +Write-Output "BorgPassphraseFile: $BorgPassphraseFile" +Write-Output "Expected Borg source path inside archive: local/gitea/data" + +if ($WhatIf) { + Write-Output "Mode: WhatIf" +} else { + Write-Output "Mode: PlanOnly" +} + +Write-Output "" +Write-Output "Planned steps:" +Write-Output "1. Prepare restore-lab target under /mnt/user/backups/restore-lab/gitea" +Write-Output "2. Restore Gitea data into an isolated test path" +Write-Output ' Template: borg extract "$BORG_REPO" "::ARCHIVE_NAME" local/gitea/data' +Write-Output ' Passphrase source: $(cat /mnt/user/appdata/secrets/borg_repo_passphrase.txt)' +Write-Output "3. Start container restoretest-gitea against test data only" +Write-Output "4. Run smoke checks against the local web and SSH endpoints" +Write-Output "5. Write markdown report under /mnt/user/backups/restore-reports" +Write-Output "6. Stop test container and clean restore data after success" +Write-Output "" +Write-Output "This script is intentionally a scaffold only." +Write-Output "No restore, no container start, no file write is executed yet." diff --git a/ops/restore-tests/gitea-runbook.md b/ops/restore-tests/gitea-runbook.md new file mode 100644 index 0000000..1a4a23b --- /dev/null +++ b/ops/restore-tests/gitea-runbook.md @@ -0,0 +1,107 @@ +# Gitea Restore Runbook + +## Vorbedingungen + +- Borg-Quelle ist verfuegbar +- Borg-Passphrase-Datei vorhanden: `/mnt/user/appdata/secrets/borg_repo_passphrase.txt` +- Testpfade unter `/mnt/user/backups/restore-lab/` und `/mnt/user/backups/restore-reports/` sind freigegeben + +## Bestaetigter Host-Stand + +- produktive Gitea-Daten liegen unter `/mnt/user/services/gitea/data` +- Borg-Scope fuer Gitea ist `/local/gitea/data` +- `restore-lab` und `restore-reports` sind auf dem Host vorhanden + +## Bestaetigter Teststand + +- echter Mini-Restore am `2026-05-07` erfolgreich gelaufen +- Restore-Ziel war `/mnt/user/backups/restore-lab/gitea/data` +- Testcontainer `restoretest-gitea` lief lokal auf `127.0.0.1:13000` und `127.0.0.1:12222` +- HTTP `200 OK`, HTML-Titel und SSH-Port wurden erfolgreich verifiziert +- Report liegt unter `/mnt/user/backups/restore-reports/gitea-2026-05-07.md` + +## Platzhalter + +- `ARCHIVE_NAME`: Borg-Archiv fuer den Restore-Test +- `REPORT_DATE`: z. B. `2026-05-07` +- `BORG_REPO`: Host-Borg-Repo +- `BORG_PASSPHRASE_FILE`: `/mnt/user/appdata/secrets/borg_repo_passphrase.txt` + +## Ablauf + +1. Testpfade vorbereiten + +```bash +mkdir -p /mnt/user/backups/restore-lab/gitea/data +mkdir -p /mnt/user/backups/restore-reports +rm -rf /mnt/user/backups/restore-lab/gitea/data/* +``` + +2. Gitea-Daten aus Borg in das Restore-Lab extrahieren + +Archiv zuerst pruefen: + +```bash +export BORG_REPO='...' +export BORG_PASSPHRASE="$(cat /mnt/user/appdata/secrets/borg_repo_passphrase.txt)" +borg list "$BORG_REPO" +``` + +Restore in das Testziel: + +```bash +cd /mnt/user/backups/restore-lab/gitea +borg extract "$BORG_REPO" "::ARCHIVE_NAME" local/gitea/data +mv /mnt/user/backups/restore-lab/gitea/local/gitea/data /mnt/user/backups/restore-lab/gitea/data +rmdir /mnt/user/backups/restore-lab/gitea/local/gitea +rmdir /mnt/user/backups/restore-lab/gitea/local +``` + +Wenn das Archiv den Pfad anders ablegt, zuerst mit `borg list "$BORG_REPO" "::ARCHIVE_NAME"` den exakten Eintrag pruefen. + +3. Testcontainer starten + +```bash +docker compose -f /mnt/user/services/homelab/ops/restore-tests/gitea-compose.test.yml up -d +``` + +4. Smoke-Test + +```bash +curl -I http://127.0.0.1:13000 +ssh -p 12222 -o BatchMode=yes -o StrictHostKeyChecking=no git@127.0.0.1 +docker logs restoretest-gitea --tail 50 +``` + +Minimal erfolgreich: + +- HTTP-Antwort kommt +- Login-Seite ist erreichbar +- SSH-Port antwortet +- Restore-Lab enthaelt Gitea-Daten + +5. Testcontainer wieder stoppen + +```bash +docker compose -f /mnt/user/services/homelab/ops/restore-tests/gitea-compose.test.yml down +``` + +6. Report schreiben + +Ziel: + +```text +/mnt/user/backups/restore-reports/gitea-REPORT_DATE.md +``` + +7. Testdaten nach erfolgreichem Lauf bereinigen + +```bash +rm -rf /mnt/user/backups/restore-lab/gitea/data +``` + +## Festgelegte Entscheidungen + +- Testdaten werden nach erfolgreichem Lauf geloescht. +- `ntfy` wird nicht im ersten echten Lauf eingebunden. +- Die Borg-Passphrase wird fuer Restore-Tests aus einer Host-Secret-Datei gelesen, nicht aus Borg-UI-Interna.