From b3fa132f50edab5930bf6a9492e090bacce5e2eb Mon Sep 17 00:00:00 2001 From: Thore Cimbal Date: Fri, 14 Aug 2026 12:00:00 +0000 Subject: [PATCH] docs: restore drill passed; procedure moves to the notfallhandbuch repo The databases are no longer an assumption: notfall.sh stage 3 restored all three Borg repos into a throwaway postgres inside the pod (synapse 31908 rows, MAS 16085, authentik 325149, wiki 251), isolated from production and repeatable. Procedure and tool now live in git.lab/axion1337.chat/notfallhandbuch so an emergency needs one clone; this repo keeps a pointer. Still open: phase A/B on an empty host, Synapse media, a repeat cadence, and mirroring that new repo off git.lab. Co-Authored-By: Claude Opus 4.8 --- ...estore-ist-nie-geprobt-sicherungen-sind.md | 41 +++++ docs/wiki/deployment/restore.md | 159 ++---------------- 2 files changed, 56 insertions(+), 144 deletions(-) diff --git a/docs/issues/0030-der-restore-ist-nie-geprobt-sicherungen-sind.md b/docs/issues/0030-der-restore-ist-nie-geprobt-sicherungen-sind.md index bac4925..0b6e233 100644 --- a/docs/issues/0030-der-restore-ist-nie-geprobt-sicherungen-sind.md +++ b/docs/issues/0030-der-restore-ist-nie-geprobt-sicherungen-sind.md @@ -139,3 +139,44 @@ Clone lesbar — der age-Key aus dem Vault ist damit tatsächlich der einzige ha **Offen: Schritt 3** (einmal gegen eine Wegwerf-Umgebung durchspielen) und **Schritt 4** (Wiederholungsrhythmus). Das Verfahren ist bis dahin abgeleitet, aber unerprobt. + +## Schritt 3 (teilweise) erledigt 2026-08-14 — Restore-Probe bestanden + +Eigenes Repo **`git.lab/axion1337.chat/notfallhandbuch`** angelegt (Wunsch sorb): Einstiegs- +README, das Verfahren (`restore-matrix.md`) und ein menügeführtes Werkzeug (`notfall.sh`). +Das Verfahren wurde aus `docs/wiki/deployment/` dorthin verschoben — hier steht nur noch ein +Zeiger. Begründung: im Ernstfall will man **einen** Clone, nicht drei Repos absuchen. + +**Der Beweis, den dieses Issue verlangt, ist für die Datenbanken erbracht.** `notfall.sh` +Stufe 3 spielt die Sicherungen in eine **Wegwerf-Postgres im Pod** zurück — isoliert, die +Produktion bleibt unberührt, beliebig wiederholbar: + +| Repo | Datenbank | zurückgespielte Zeilen | +|---|---|---| +| synapse-backup | `synapse` | **31.908** | +| synapse-backup | `matrixauthenticationservice` | **16.085** | +| authentik-backup | `authentik` | **325.149** | +| wikijs-backup | `wiki` | **251** | + +Die Sicherungen sind damit **keine Vermutung mehr** — sie wurden tatsächlich zurückgespielt. + +**Entwurfsentscheidungen des Werkzeugs (bewusst, nicht beiläufig):** +- Arbeit läuft **im Cluster**, nicht auf dem Laptop: `axion-backup:v2` bringt borg + pg-Tools + mit, die Backup-Secrets verlassen den Cluster nicht, lokal genügt `kubectl`. +- `whiptail` wird genutzt **falls vorhanden**, sonst Textmenü — ein Notfallwerkzeug darf nicht + mit „installier erst ein Paket" beginnen (auf dem Mac fehlt whiptail). +- **Bestehenskriterium ist die Zeilenzahl, nicht der Exitcode.** Die erste Fassung meldete bei + fehlgeschlagenem `pg_restore` fälschlich „OK" (Pipe verschluckte den Code) — genau der + stille Ausfall, den dieses Issue beschreibt, nur im Prüfwerkzeug selbst. Behoben und + gegengetestet. + +**Weiterhin offen (Rest von Schritt 3 + Schritt 4):** +- **Phase A/B nie durchgespielt:** Wiederanlauf auf leerem Host (K3s, die zwei Bootstrap- + Secrets, Flux) ist abgeleitet, nicht getestet — das braucht eine Wegwerf-Umgebung. +- **Synapse-Medien** (`media_store` → PVC) nicht erprobt; ohne sie sind Bilder in Räumen tote Links. +- **Wiederholungsrhythmus** festlegen (Stufe 3 ist gefahrlos → z.B. monatlich). +- Homelab-Seite (GitLab/Overmind → MinIO/DSM) weiterhin ungeprüft. + +⚠️ **Empfehlung Spiegelung:** Das Notfallhandbuch liegt bisher nur auf git.lab — also im +Homelab, das im Ernstfall selbst nicht erreichbar sein kann. Ein Push-Mirror nach Gitea +(rohana) wie bei den übrigen Repos wäre hier besonders sinnvoll. diff --git a/docs/wiki/deployment/restore.md b/docs/wiki/deployment/restore.md index 0f10eef..a5103bf 100644 --- a/docs/wiki/deployment/restore.md +++ b/docs/wiki/deployment/restore.md @@ -5,154 +5,25 @@ sources: - "docs/issues/0030-der-restore-ist-nie-geprobt-sicherungen-sind.md" related: - "docs/wiki/deployment/deploy-uebergabe.md" - - "docs/adr/0001-gitlab-kanonisch-push-mirror.md" --- -# Wiederherstellung: Matrix-Plattform (Hetzner/K3s) +# Wiederherstellung — liegt im Notfallhandbuch -Ablauf für den Ernstfall — Totalverlust des Hetzner-Hosts. Kein Fließtext, sondern Reihenfolge: -**was zuerst, woher kommt was, wie prüft man es.** +Das Verfahren und das Werkzeug leben in einem **eigenen Repo**: -> **Warum dieses Dokument in Git liegt und nicht im Wiki:** Wiki.js läuft *auf* dem Cluster. -> Ist der Cluster weg, ist das Wiki weg. Dieses Verfahren muss aus einem Git-Clone (oder von -> git.lab/Gitea) lesbar sein, ohne dass irgendetwas von der Plattform läuft. +**`git.lab/axion1337.chat/notfallhandbuch`** -**Stand:** 2026-08-14, geschrieben aus dem laufenden System heraus (Cluster-Inspektion). -⚠️ **Noch nicht durchgespielt** — siehe „Offene Punkte" am Ende. +| Datei | Inhalt | +|---|---| +| `README.md` | Einstieg: erst Lage klären, dann erst Restore | +| `restore-matrix.md` | Wiederherstellung der Matrix-Plattform (Hetzner/K3s), Phase A–D | +| `notfall.sh` | Menügeführtes Werkzeug: Lage prüfen · Backups prüfen · **Restore-Probe** · Ernstfall | -## 0. Was du in der Hand haben musst, bevor du anfängst +**Warum ein eigenes Repo und nicht hier:** Im Ernstfall will man **einen** Clone, der alles +enthält, und kein Dokument, das tief in einem Management-Repo (oder gar im Wiki, das auf dem +betroffenen Cluster läuft) liegt. Das Notfallhandbuch ist bewusst klein und eigenständig. -| Was | Woher | Ohne das geht | -|---|---|---| -| **age-Schlüssel** (`age.agekey`) | Passwort-Vault (sorb). Zweitkopien: `~/.age/keys.txt` auf sorbs Mac, `sops-age`-Secret im alten Cluster | **gar nichts** — alle Secrets sind damit verschlüsselt | -| **gitops-Repo** | `git.lab/axion1337.chat/axion1337.chat-gitops` (kanonisch), Spiegel auf Gitea/rohana | kein Cluster-Zustand | -| **Zugang Hetzner** | Hetzner Cloud Console (Neuinstallation) | kein Host | -| **Gitea-Zugangsdaten** für Flux | im Repo SOPS-verschlüsselt bzw. Gitea-Token neu erzeugen | Flux kann nicht syncen | -| **Storage-Box-Zugang** | SSH-Key + Borg-Passphrase, beide in `apps/production/synapse-backup-secret.yaml` (SOPS) | keine Daten | - -**Wichtig:** Borg-Passphrase und Storage-Box-SSH-Key holst du **ohne Cluster** aus einem lokalen -Clone — genau dafür ist der age-Schlüssel da: - -```bash -export SOPS_AGE_KEY_FILE=~/.age/keys.txt # aus dem Vault wiederhergestellt -sops -d apps/production/synapse-backup-secret.yaml # Borg-Passphrase + SSH-Key -sops -d apps/authentik/authentik-backup-secret.yaml -``` - -## 1. Wo die Daten liegen - -Hetzner **Storage Box** `u641795@u641795.your-storagebox.de`, **Port 23**, drei getrennte -Borg-Repos. Verschlüsselung `repokey-blake2` → **die Passphrase allein genügt**, es gibt keine -separate Schlüsseldatei zu retten. - -| Repo | Inhalt | Quelle im Betrieb | -|---|---|---| -| `/./synapse-backup` | DBs `synapse` + `matrixauthenticationservice`, **plus** Medien (`media_store`) | `matrix-stack-postgres` | -| `/./authentik-backup` | DB `authentik` | `authentik-postgresql` | -| `/./wikijs-backup` | DB `wiki` | `wikijs-postgres` | - -Aufbewahrung: 7 täglich / 4 wöchentlich / 6 monatlich (`borg prune`, nachweislich aktiv). -Archivname: `-`. Layout im Archiv: -`scratch/dumps/.dump` (Format `pg_dump -Fc` → `pg_restore`) und `media/media_store/…`. - -Zugriff von einer beliebigen Maschine mit Borg: - -```bash -export BORG_REPO='ssh://u641795@u641795.your-storagebox.de:23/./synapse-backup' -export BORG_PASSPHRASE='…' # aus sops -d, s.o. -export BORG_RSH="ssh -i -p 23" -borg list "$BORG_REPO" # Archive auflisten -borg extract "$BORG_REPO::" # ins aktuelle Verzeichnis -``` - -## 2. Reihenfolge der Wiederherstellung - -### Phase A — Fundament - -1. **Host + K3s** neu aufsetzen (siehe `gitops:docs/install.md`, Schritt 1). Kubeconfig sichern. -2. **Zwei Secrets von Hand anlegen** — ohne die kann Flux nichts tun (Henne-Ei, bewusst manuell): - -```bash -kubectl create namespace flux-system - -# a) age-Schlüssel (aus dem Vault) — entschlüsselt alle SOPS-Secrets -kubectl create secret generic sops-age -n flux-system --from-file=age.agekey= - -# b) Git-Zugang für Flux (Keys heißen: username / password) -kubectl create secret generic flux-system -n flux-system \ - --from-literal=username= --from-literal=password= -``` - -3. **Flux installieren** und auf das Repo zeigen lassen (`clusters/matrix`). - -> ⚠️ **Flux zieht aus Gitea (`rohana.axion1337.de`), nicht aus git.lab** — bewusst so, damit der -> Cluster ohne das Homelab baubar ist (siehe `gitops:CLAUDE.md`). **Ist rohana ebenfalls weg**, -> dann zuerst eine erreichbare Git-Quelle herstellen (Repo aus git.lab oder lokalem Clone auf ein -> neues Remote pushen) und `clusters/matrix/flux-system/gotk-sync.yaml` (`url:`) darauf zeigen -> lassen. Sonst hängt der Wiederanlauf an einem Host, der gar nicht Teil des Backups ist. - -### Phase B — Dienste hochziehen (noch leer) - -Flux arbeitet die Kustomizations in dieser Abhängigkeit ab: - -``` -flux-system - └── infra-apps (Namespaces, Cert-Manager, HelmRepositories) — keine Secrets nötig - ├── production-apps (ESS/Synapse/MAS/Wiki.js, entschlüsselt via sops-age) - ├── authentik-apps (entschlüsselt via sops-age) - └── monitoring-apps -``` - -Warten, bis die **Postgres-Instanzen** laufen (`matrix-stack-postgres`, `authentik-postgresql`, -`wikijs-postgres`). Die Anwendungen kommen dabei mit **leeren** Datenbanken hoch — das ist -erwartet. - -### Phase C — Daten zurückspielen - -> ⚠️ **Konsumenten vorher herunterfahren.** Synapse/MAS/Authentik/Wiki.js legen beim Start ein -> leeres Schema an; ein `pg_restore` dagegen kollidiert. Also erst herunterskalieren, dann -> zurückspielen, dann wieder hochfahren. - -```bash -kubectl scale -n matrix deploy --replicas=0 -l app.kubernetes.io/name=synapse -kubectl scale -n matrix deploy --replicas=0 matrix-stack-matrix-authentication-service wikijs -kubectl scale -n authentik deploy --replicas=0 authentik-server authentik-worker -``` - -Pro Datenbank: Archiv extrahieren, Dump einspielen (`pg_restore` in die jeweils **leere** DB; -bei Bedarf `--clean --if-exists`), danach Konsumenten wieder hochskalieren. - -Reihenfolge sinnvoll: **Authentik zuerst** (Anmeldung/OIDC hängt daran), dann Synapse+MAS, -dann Wiki.js. - -**Synapse-Medien** nicht vergessen: `media/media_store/…` aus dem Archiv zurück in das -PVC `matrix-stack-synapse-media`. Ohne die Medien ist die Datenbank zwar konsistent, aber alle -Bilder/Dateien in Räumen sind tote Links. - -### Phase D — Verifikation - -- `flux get kustomizations -A` → alles `Ready=True`. -- Anmeldung über Authentik funktioniert (OIDC-Kette steht). -- Ein bestehender Raum zeigt **Verlauf und Medien**. -- Wiki erreichbar; Inhalte kommen ohnehin zusätzlich aus git-storage (Gitea/git.lab). -- Zertifikate: Traefik/Cert-Manager stellen neu aus (DNS-01 ist umgestellt, kein offener - Port 443 nötig — siehe #0007). -- Die drei Backup-CronJobs laufen wieder (`kubectl get cronjob -A`). - -## 3. Was am Backup **nicht** hängt - -- **Wiki-Inhalte**: liegen zusätzlich als Git-Repo (Wiki.js → Gitea → git.lab kanonisiert). -- **Gesamte Konfiguration**: im gitops-Repo, nicht im Backup (deshalb ist der Cluster - reproduzierbar und nicht nur wiederherstellbar). -- **Gitea/rohana selbst**: Push-Mirror, wird bewusst nicht gesichert (#0010) — Repos aus git.lab - neu befüllen. **Aber:** die Container-Registry dort ist kein Spiegel; die vier eigenen Images - (u.a. `threadnet-web`, `axion-backup`) müssen ggf. neu gebaut werden. - -## 4. Offene Punkte - -- **Nie durchgespielt.** Dieses Verfahren ist aus dem laufenden System abgeleitet, nicht erprobt - (#0030, Schritt 3). Ein Durchlauf gegen eine Wegwerf-Umgebung steht aus — erst danach ist es - belastbar. -- **Homelab-Seite ungeprüft**: GitLab auf Overmind → MinIO/DSM ist nicht Teil dieser Prüfung. -- Nach dem ersten echten Durchlauf: Zeitbedarf und Stolpersteine hier ergänzen und einen - Wiederholungsrhythmus festlegen. +**Erprobter Stand (2026-08-14):** Die Rückspielung der Datenbanken ist mit `notfall.sh` +Stufe 3 nachgewiesen — alle drei Borg-Repos ließen sich in eine Wegwerf-Postgres +zurückspielen (synapse 31.908 Zeilen, MAS 16.085, authentik 325.149, wiki 251). Der +vollständige Wiederanlauf auf leerem Host steht weiterhin aus (#0030).