From 74f4fa91f02e989f7f922610b4a0cbdc2e24eca4 Mon Sep 17 00:00:00 2001 From: Thore Cimbal Date: Fri, 14 Aug 2026 12:00:00 +0000 Subject: [PATCH] =?UTF-8?q?docs(issues):=20#0030=20inventory=20done,=20#00?= =?UTF-8?q?10=20scope=20reduced=20=E2=80=94=20age=20key=20is=20the=20real?= =?UTF-8?q?=20risk?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cluster-side backups are healthier than assumed: three nightly Borg jobs to a Hetzner Storage Box, all completing with plausible volumes and working prune (synapse 199MB/247 files, authentik ~150MB, wikijs 223kB DB-only). No silent failures. Critical finding for #0030: the Borg passphrase and SSH key needed to READ those backups are SOPS-encrypted under a single age key that exists only in the cluster being backed up and on one laptop — no documented cold copy. Losing both makes all three repos permanently unreadable. Cold escrow must precede any restore drill. For #0010 this shrinks the work: the Storage Box + Borg pattern already exists and is proven, so Gitea needs only its own repo there. The disabled cron (no backups since 2026-07-30) remains separately urgent. Co-Authored-By: Claude Opus 4.8 --- STATUS.md | 4 +- ...-09-gitea-backups-off-host-borg-storage.md | 32 +++++++++++- ...estore-ist-nie-geprobt-sicherungen-sind.md | 52 ++++++++++++++++++- 3 files changed, 84 insertions(+), 4 deletions(-) diff --git a/STATUS.md b/STATUS.md index 6b3ece4..3dca28e 100644 --- a/STATUS.md +++ b/STATUS.md @@ -12,7 +12,7 @@ Verteilung: M1 8 · M2 19 · M4 2 · M5 4 | [0004](docs/issues/0004-overmind-02-e1000e-nic-hang-beobachtung-nach.md) | waiting | M1 | low | OVERMIND-02: e1000e-NIC-Hang — Beobachtung nach EEE-Fix + Firmware-Update | | [0008](docs/issues/0008-cfgmon-03-prometheus-remote-write-und-loki.md) | waiting | M1 | medium | CFGMON-03: Prometheus-Remote-Write und Loki öffentlich ohne Auth — Weg A, nachgelagerte Prüfung | | [0009](docs/issues/0009-cfgmon-04-grafana-admin-credentials-aus-env.md) | open | M2 | low | CFGMON-04: Grafana-Admin-Credentials aus .env gelten nicht für die HTTP-API | -| [0010](docs/issues/0010-cfgmon-09-gitea-backups-off-host-borg-storage.md) | open | M1 | medium | CFGMON-09: Gitea-Backups off-host (Borg/Storage Box) — Backup-Cron ist DEAKTIVIERT | +| [0010](docs/issues/0010-cfgmon-09-gitea-backups-off-host-borg-storage.md) | in-progress | M1 | medium | CFGMON-09: Gitea-Backups off-host (Borg/Storage Box) — Backup-Cron ist DEAKTIVIERT | | [0014](docs/issues/0014-cfgmon-14-root-zugang-ueber-die-docker-gruppe.md) | waiting | M2 | low | CFGMON-14: Root-Zugang über die docker-Gruppe umgeht sudo und hinterlässt keine Spur | | [0015](docs/issues/0015-cfgmon-15-token-hygiene-einmal-tokens-der.md) | next | M2 | medium | CFGMON-15: Token-Hygiene — Einmal-Tokens der LABNET-02-Nacht widerrufen | | [0018](docs/issues/0018-doc-01-wiki-rollout-abschliessen-ci-freigaben.md) | open | M2 | low | DOC-01: Wiki-Rollout abschließen — CI-Freigaben, Zeitplan, Dokploy-Stack, wiki.lab | @@ -23,7 +23,7 @@ Verteilung: M1 8 · M2 19 · M4 2 · M5 4 | [0027](docs/issues/0027-audit-01-acht-widersprueche-aus-dem-labnet-02.md) | waiting | M2 | medium | AUDIT-01: Acht Widersprüche aus dem LABNET-02-Nachlauf (Selbst-Audit CFGMON-Session) | | [0028](docs/issues/0028-mirror-01-ein-ausfall-der-push-mirrors-bleibt.md) | open | M2 | low | MIRROR-01: Ein Ausfall der Push-Mirrors bleibt unbemerkt — Produktion friert still ein | | [0029](docs/issues/0029-ui-harmonisieren-gleiche-farben-und-formen.md) | open | M4 | medium | UI harmonisieren: gleiche Farben und Formen über alle Oberflächen | -| [0030](docs/issues/0030-der-restore-ist-nie-geprobt-sicherungen-sind.md) | open | M1 | medium | Der Restore ist nie geprobt — Sicherungen sind bisher eine Vermutung | +| [0030](docs/issues/0030-der-restore-ist-nie-geprobt-sicherungen-sind.md) | in-progress | M1 | medium | Der Restore ist nie geprobt — Sicherungen sind bisher eine Vermutung | | [0031](docs/issues/0031-stillstandspruefung-gitea-token-und-authentik.md) | open | M1 | low | Stillstandsprüfung: GITEA_TOKEN und Authentik-Teil nachziehen | | [0032](docs/issues/0032-gameserver-hat-keinen-push-mirror-und-auf-gitea.md) | open | M2 | medium | gameserver hat keinen Push-Mirror — und auf Gitea liegt ein anderer Stand | | [0033](docs/issues/0033-overmind-01-element-desktop-build-lab-registry.md) | open | M2 | low | OVERMIND-01 — element-desktop-build von rohana in die Lab-Registry umziehen | diff --git a/docs/issues/0010-cfgmon-09-gitea-backups-off-host-borg-storage.md b/docs/issues/0010-cfgmon-09-gitea-backups-off-host-borg-storage.md index 39aa400..4f73582 100644 --- a/docs/issues/0010-cfgmon-09-gitea-backups-off-host-borg-storage.md +++ b/docs/issues/0010-cfgmon-09-gitea-backups-off-host-borg-storage.md @@ -1,7 +1,7 @@ --- type: issue id: "0010" -status: open +status: in-progress created: 2026-08-01 milestone: M1 priority: medium @@ -32,3 +32,33 @@ Quelle: [hosts/cfgmon.md](https://git.lab/axion1337.chat/management/-/blob/main/ --- *Übernommen aus dem Backlogs-Markdown beim Framework-Umbau 2026-08-01 (voller Wortlaut: Git-Historie der Datei).* + +## In Arbeit 2026-08-14 — Muster existiert bereits (Aufwand deutlich kleiner als geplant) + +Bei der Bestandsaufnahme für #0030 zeigte sich: **Borg auf Hetzner Storage Box läuft im +Matrix-Cluster bereits produktiv** — der für dieses Issue geplante Aufbau muss also nicht +erfunden, sondern nur auf Gitea übertragen werden. + +**Vorhandenes Muster (erprobt, nächtlich, verifiziert):** +- Storage Box `u641795@u641795.your-storagebox.de:23`, drei getrennte Repos + (`/./synapse-backup`, `/./authentik-backup`, `/./wikijs-backup`). +- Image `rohana.axion1337.de/sorb/axion-backup:v2`; Retention `borg prune` 7d/4w/6m. +- Credentials (Borg-Passphrase + SSH-Key) als SOPS-Secret, per Env in den Job. + +**Konsequenz für Gitea:** kein neuer Storage-Box-Vertrag nötig — ein zusätzliches Repo +`/./gitea-backup` auf derselben Box (oder ein Sub-Account) genügt. Der Host braucht weiterhin +einen eigenen SSH-Key (CFGMON ist **nicht** der Cluster), dessen Public Key in der Storage Box +hinterlegt wird. Wie im Issue vorgesehen: Dump **unkomprimiert** an Borg geben (`gzip` aus +`backup/gitea-backup.sh` entfernen), sonst greift die Deduplizierung nicht. + +⚠️ **Unverändert akut:** Der nächtliche Cron auf CFGMON ist seit 2026-07-30 auskommentiert — +seit über zwei Wochen läuft **kein** Gitea-Backup, der letzte Stand ist +`/opt/backup/gitea-dump-2026-07-30.tar.gz`. Das ist unabhängig von der Borg-Umstellung sofort +behebbar (Cron wieder einkommentieren) und sollte **nicht** auf das Off-host-Projekt warten. + +⚠️ **Vorrang laut #0030:** Bevor Backups erweitert werden, muss die kalte Kopie des +age-Schlüssels stehen (dort dokumentierte Zirkelabhängigkeit) — sonst wächst nur die Menge +potenziell unlesbarer Sicherungen. + +**Offen (nur mit Host-Zugang, kein SSH zu CFGMON von hier):** Cron reaktivieren; SSH-Key auf +CFGMON erzeugen + in der Storage Box hinterlegen; `gitea-backup.sh` auf Borg umstellen. 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 f13d0e0..9d12fd7 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 @@ -1,7 +1,7 @@ --- type: issue id: "0030" -status: open +status: in-progress created: 2026-08-06 milestone: M1 priority: medium @@ -42,3 +42,53 @@ Der letzte Punkt ist hier besonders relevant: **Der SOPS-age-Schlüssel entschl Ich kenne den Sicherungsaufbau nur aus deiner Beschreibung und einem Screenshot, nicht aus eigener Anschauung. Der erste Schritt ist deshalb Bestandsaufnahme, nicht Bewertung. *Aufgenommen am 2026-08-06 bei einer Bestandsaufnahme der Sicherheitslage.* + +## Schritt 1 erledigt 2026-08-14 — Bestandsaufnahme (Cluster-Seite) + +Wie im Issue gefordert zuerst „das Billigste": in die vorhandenen Sicherungen hineingeschaut, +nichts erweitert. Geprüft wurde die **Matrix-Cluster-Seite** (Hetzner/K3s); die Homelab-Seite +(GitLab auf Overmind → MinIO/DSM) ist von hier nicht erreichbar und weiterhin ungeprüft. + +**Befund: die Sicherungen selbst sind besser als vermutet.** Drei nächtliche CronJobs, alle +`Complete`, keiner suspendiert, alle **off-host** per Borg auf eine Hetzner Storage Box +(`u641795@…your-storagebox.de:23`) — also genau das Muster, das #0010 für Gitea erst plant: + +| Job | Zeit | Ziel-Repo | Volumen (letzter Lauf) | Bewertung | +|---|---|---|---|---| +| `synapse-backup` (matrix) | 03:00 | `/./synapse-backup` | 199,5 MB, 247 Dateien, Dedup-Delta 3,7 MB | plausibel ✅ | +| `authentik-backup` (authentik) | 03:15 | `/./authentik-backup` | ~150 MB gesamt, Delta ~15 MB | plausibel ✅ | +| `wikijs-backup` (matrix) | 03:30 | `/./wikijs-backup` | 223 kB | plausibel ✅ (nur Postgres; die Wiki-**Inhalte** liegen per git-storage in Gitea/git.lab) | + +Retention greift nachweislich (`borg prune`, 7 daily / 4 weekly / 6 monthly, „Deleted data" +in den Logs). Kein stiller Ausfall, keine leeren Archive — die Sicherung ist **keine bloße +Vermutung mehr**, zumindest was Existenz und Inhalt angeht. + +### ⚠️ Kritischer Befund: Zirkelabhängigkeit beim age-Schlüssel + +Genau das vom Issue vorhergesagte Muster („der Dump ist da, aber der Schlüssel lag nur auf dem +Host, der weg ist") liegt real vor: + +1. Zum **Lesen** der Borg-Repos braucht man Borg-Passphrase **und** SSH-Key. +2. Beide liegen in `synapse-backup-secret.yaml` / `authentik-backup-secret.yaml` — **SOPS-verschlüsselt**. +3. Entschlüsselbar ist das nur mit **einem einzigen** age-Schlüssel + (Empfänger `age14l0hw…`, siehe `.sops.yaml`). +4. Dieser private Schlüssel existiert an **genau zwei Orten**, und beide sind „heiß": + - `sops-age`-Secret in `flux-system` — **im Cluster, den das Backup schützen soll** + - `~/.age/keys.txt` auf sorbs Mac — **ein einzelnes Gerät** + +**Folge:** Gehen Cluster und Mac zusammen verloren (Totalschaden Hetzner + Laptop weg/defekt), +sind **alle drei Borg-Repos dauerhaft unlesbar**. Die Sicherungen wären technisch einwandfrei +und trotzdem wertlos. Eine Suche über `docs/` und `hosts/` findet **keine** dokumentierte +Auslagerung (Escrow, Offline-Kopie, Passwort-Manager) des Schlüssels. + +**Billigste wirksame Gegenmaßnahme (vor jedem Restore-Test):** eine **kalte Kopie** des +age-Schlüssels außerhalb von Cluster und Mac anlegen — Passwort-Manager und/oder Ausdruck an +sicherem Ort — und die Fundstelle in `hosts/` dokumentieren (nur *wo*, nie der Wert). Erst danach +lohnt der eigentliche Restore-Durchlauf, sonst probt man einen Ablauf, dessen Voraussetzung +selbst ungesichert ist. + +### Nächste Schritte (unverändert nach Issue-Plan) + +2. Restore-Verfahren schreiben (Reihenfolge, Herkunft von age-Key und kubeconfig). +3. Einmal gegen eine Wegwerf-Umgebung durchspielen. +4. Ergebnis nach `verfahren/` und Wiederholungsrhythmus festlegen.