sorb's call: Gitea on rohana is a push mirror of the canonical git.lab, so a nightly gitea dump would back up a copy — effort not justified, cron stays off. Documented the one non-mirror asset for the record: the container registry holds four images the cluster pulls (incl. threadnet-web and the backup image itself), which is rebuild time rather than data loss and is covered by #0022/#0033. For #0030, sorb confirms the age key is also in the password vault, dissolving the circular dependency found earlier. Noted that the vault is now part of the restore path and must lead the procedure. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6.9 KiB
type, id, status, created, milestone, priority, area, gitlab_iid, related
| type | id | status | created | milestone | priority | area | gitlab_iid | related |
|---|---|---|---|---|---|---|---|---|
| issue | 0030 | in-progress | 2026-08-06 | M1 | medium | security | 30 |
Der Restore ist nie geprobt — Sicherungen sind bisher eine Vermutung
Import aus management#30 (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
Es wird gesichert: jeden Sonntag, alle Dienste, drei Versionen vorgehalten, GitLab auf Overmind mit Datenbank und Volumes nach MinIO auf dem DSM. Das ist mehr, als die meisten haben.
Was fehlt, ist der Beweis, dass sich daraus etwas wiederherstellen lässt. Es gibt kein dokumentiertes Verfahren und keinen je durchgespielten Versuch. Eine Suche über docs/ und verfahren/ findet nur Erwähnungen in install.md — keine Anleitung, keine Protokolle.
Warum das der wichtigste der offenen Punkte ist
Eine Sicherung, die nie zurückgespielt wurde, ist eine Vermutung. Die typischen Fehler zeigen sich ausschließlich beim Zurückspielen und nie beim Sichern:
- die Datenbank ist gesichert, aber ohne das Volume mit den Uploads ist sie wertlos
- der Dump ist da, aber der Verschlüsselungsschlüssel lag nur auf dem Host, der weg ist
- es liegen drei Versionen, aber alle drei sind seit Wochen leer, weil ein Pfad umgezogen ist und keiner es gemerkt hat
- niemand weiß, in welcher Reihenfolge die Dienste hochkommen müssen
Der letzte Punkt ist hier besonders relevant: Der SOPS-age-Schlüssel entschlüsselt alle Secrets im Cluster. Wenn der nur an einer Stelle liegt, ist die Frage nicht, ob die Sicherung funktioniert, sondern ob sie überhaupt etwas nützt.
Was zu tun ist
- Zuerst das Billigste: stichprobenartig in die aktuellen Sicherungen hineinschauen. Sind sie plausibel groß? Enthalten sie, was sie sollen? Das findet stille Ausfälle sofort.
- Ein echtes Wiederherstellungsverfahren schreiben — als Ablauf, nicht als Prosa: welcher Dienst zuerst, woher der SOPS-Schlüssel, woher der kubeconfig.
- Einmal wirklich durchspielen, gegen eine Wegwerf-Umgebung, nicht gegen die Produktion. Was dabei fehlt, ist das Ergebnis.
- Ergebnis als Verfahren in
verfahren/ablegen und danach in bekanntem Abstand wiederholen.
⚠️ Bewusst nicht vorschlagen: die Sicherung erweitern, bevor die vorhandene geprüft ist. Mehr zu sichern, ohne zu wissen, ob das Vorhandene trägt, verschiebt das Problem nur.
Grenzen dieses Issues
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:
- Zum Lesen der Borg-Repos braucht man Borg-Passphrase und SSH-Key.
- Beide liegen in
synapse-backup-secret.yaml/authentik-backup-secret.yaml— SOPS-verschlüsselt. - Entschlüsselbar ist das nur mit einem einzigen age-Schlüssel
(Empfänger
age14l0hw…, siehe.sops.yaml). - Dieser private Schlüssel existiert an genau zwei Orten, und beide sind „heiß":
sops-age-Secret influx-system— im Cluster, den das Backup schützen soll~/.age/keys.txtauf 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)
- Restore-Verfahren schreiben (Reihenfolge, Herkunft von age-Key und kubeconfig).
- Einmal gegen eine Wegwerf-Umgebung durchspielen.
- Ergebnis nach
verfahren/und Wiederholungsrhythmus festlegen.
Update 2026-08-14 — age-Schlüssel ist ausgelagert (Befund entschärft)
sorb bestätigt: der private age-Schlüssel liegt zusätzlich im Passwort-Vault, also außerhalb von Cluster und Mac. Damit ist die oben beschriebene Zirkelabhängigkeit aufgelöst — ein gleichzeitiger Verlust von Hetzner-Host und Laptop macht die Borg-Repos nicht mehr unlesbar. Der Befund bleibt hier stehen, weil die Kette (Backup lesen → Borg-Passphrase → SOPS → ein age-Schlüssel) beim Schreiben des Restore-Verfahrens explizit auftauchen muss: der Vault ist Teil des Wiederherstellungswegs, nicht nur Nebensache.
Zu ergänzen beim Verfahren (Schritt 2): Fundort des Schlüssels benennen (nur wo, nie der Wert) und im Restore-Ablauf als ersten Schritt führen — ohne ihn ist kein weiterer Schritt möglich.
Restaufwand nach heutiger Bestandsaufnahme
Schritt 1 (Bestandsaufnahme) ist erledigt und positiv ausgefallen; Schritt 2–4 stehen aus:
Restore-Verfahren schreiben, einmal gegen eine Wegwerf-Umgebung durchspielen, Ergebnis nach
verfahren/ und Wiederholungsrhythmus. Die Homelab-Seite (GitLab auf Overmind → MinIO/DSM)
ist weiterhin ungeprüft — von der Hetzner-Seite aus nicht erreichbar.