Enterprise-Backup-, Recovery-, Verification-, Security- und Monitoring-Plattform fuer Proxmox VE, Windows, Linux und Dateisysteme. Der Leitsatz, der fast jede Entscheidung erklaert: Ein Backup gilt erst als vertrauenswuerdig, wenn Integritaet geprueft und Wiederherstellbarkeit nachgewiesen wurde. Deshalb steigt ein Wiederherstellungspunkt erst nach einem tatsaechlich durchgefuehrten Restore-Test auf "recoverable", und Unbekanntes geht in keine Bewertung als "gut" ein. Umfang (Phasen 0-23): - Repository Engine: inhaltsadressierte Bloecke, atomares Commit-Protokoll, Katalogaufbau allein aus den Manifesten — ohne Datenbank - Backup Engine: inhaltsabhaengiges Chunking, Deduplizierung trotz Verschluesselung, zstd, AES-256-GCM, Streaming mit Gegendruck - Agenten fuer Windows und Linux mit Auftragsabholung (Pull-Modell) - Proxmox-Provider mit beiden Zugriffswegen auf die Sicherungsarchive - Scheduler, Recovery Engine mit Pruefpunkt, Verification, Unveraenderlichkeit - Weboberflaeche, Kennzahlen, Meldungen, Berichte, Security Center, Ransomware-Heuristik (meldet, handelt nie) - Disaster Recovery, Haertung, Leistungsmessung, Chaos Testing - Eingefrorene Vertraege fuer API, Migrationen, Backup-Format und Repository - Auslieferungspaket fuer linux/amd64, linux/arm64 und windows/amd64 Nicht enthalten und als solches gekennzeichnet: Kapazitaetsprognose, Backup Copy, Changed Block Tracking bei Proxmox, erweiterte Attribute und ACLs. Gebaut, aber nie auf echter Hardware gefahren: der Windows-Dienst, die systemd-Einheit und der verpflichtende Proxmox-Meilenstein — ob eine wiederhergestellte VM startet, ist ungeprueft. Einzelheiten in CHANGELOG.md und docs/release-candidate.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
9.7 KiB
Disaster Recovery (Phase 18)
Vier Szenarien, alle vier real durchgespielt — der Plan (§20) sagt „Implement and test", und ein Notfallverfahren, das nie ausgeführt wurde, ist keines.
Die eine Entscheidung, die alles trägt
Eine Konfigurationssicherung, die nur auf dem Control-Server liegt, ist beim Verlust des Control-Servers wertlos.
Genau das ist Szenario A. Wer die Konfiguration in ein Verzeichnis neben der Datenbank schreibt, hat sie im Ernstfall nicht mehr — Server und Datenbank gehen typischerweise gemeinsam verloren, weil sie auf derselben Maschine stehen.
Der einzige Ort, der den Verlust überlebt, ist das Repository: Es liegt
anderswo, es ist selbstbeschreibend, und ohne es wäre ohnehin alles verloren.
Deshalb schreibt syncova-dr export die Control-Plane-Konfiguration nach
metadata/control-plane/ — neben die Backups.
Was der Sicherungssatz enthält — und was nicht
Enthalten: Repositories, Aufträge samt Quellen, Aufbewahrungsregeln, Wartungsfenster, Konten mit ihren Rollen, Systemeinstellungen.
Nicht enthalten, und zwar mit Absicht:
| Ausgelassen | Warum |
|---|---|
| Passwörter und Passwort-Hashes | Ein Argon2id-Hash ist kein Klartext, aber offline angreifbar — und ein Repository liegt naturgemäß außerhalb der Anlage |
| Zweite Faktoren (TOTP) | Dasselbe |
| Zugangsdaten und Konfiguration der Benachrichtigungswege | In der Konfiguration steht die Webhook-Adresse, und die trägt bei vielen Diensten das Token im Pfad |
| Sitzungen und Agenten-Tokens | Sie sollen nach einem Ausfall ablaufen |
| Der Verschlüsselungsschlüssel der Anlage | Er gehört in die Umgebung des Dienstes, nicht in eine Datei, die verreist |
Bei den Benachrichtigungswegen wurde die härtere Wahl getroffen: Ein einzelnes Feld zu schwärzen hieße, bei jedem neuen Kanaltyp erneut daran zu denken — und einmal denkt niemand daran. Was bleibt, ist ein Merkzettel: Es gab einen Kanal dieses Namens, dieser Art, mit dieser Schwelle. Weniger, als man sich wünscht, und mehr als nichts.
Die Liste der Auslassungen steht im Sicherungssatz selbst, nicht nur hier. Wer nach einem Totalverlust eine Anlage wiederherstellt, hat die Betriebsanleitung nicht dabei; die Datei muss die Anleitung sein.
Nichts läuft von selbst wieder an
Nach dem Einspielen gilt:
- Aufträge kommen angehalten zurück. Nach einem Totalverlust weiß niemand, ob die Quellen noch existieren und ob die wiederhergestellten Daten die richtigen sind. Ein Zeitplan, der um zwei Uhr nachts von selbst anläuft, könnte auf ein halb wiederhergestelltes System schreiben — und der Betreiber erführe davon am nächsten Morgen.
- Repositories kommen als „nicht erreichbar" zurück. Ob die Ablage am alten
Pfad noch da ist, steht nicht fest. Eines, das als
activezurückkäme und fehlte, ließe den nächsten Lauf ins Leere greifen — und die Übersicht meldete alles in Ordnung. - Konten kommen deaktiviert zurück, Benachrichtigungswege abgeschaltet.
last_verified_atundlast_restore_test_atbleiben leer. Dass ein Backup vor dem Ausfall geprüft wurde, sagt nichts über seinen heutigen Zustand.
Das Einspielen läuft in einer Transaktion: alles oder nichts. Ein Abbruch auf halber Strecke hinterließe Aufträge ohne Repositories und Quellen ohne Aufträge — eine Anlage, die arbeitsfähig aussieht und beim ersten Lauf scheitert. Im Nachweis ist genau das eingetreten (siehe Funde), und die Datenbank blieb leer.
Der Weg nach einem Totalverlust
# 1. Ansehen, was da ist — OHNE Datenbank
syncova-dr inspect --repo /pfad/zum/repository
# 2. Schema anlegen
syncova-migrate up
# 3. Konfiguration und Wiederherstellungspunkte einspielen
syncova-dr restore --repo /pfad/zum/repository --catalog
# 4. Ersten Zugang anlegen
syncova-admin create-admin --username <name>
# 5. Repositorypfade prüfen, auf 'aktiv' setzen, Aufträge freigeben
Schritt 1 braucht keine Datenbank — das ist der Zweck. Wer nach einem Ausfall vor einem Repository steht, muss sehen können, was darin ist, bevor er entscheidet, wie er weitermacht.
--catalog baut die Wiederherstellungspunkte aus den Manifesten neu auf,
nicht aus dem vorhandenen Katalog: Der könnte veraltet oder beschädigt sein, und
darauf darf man sich im Ernstfall nicht verlassen. Verbindlich sind die
Manifeste.
Die Kennungen entstehen deterministisch (UUIDv5 aus Repository-Kennung, Art und Backupkennung). Eine zweite Übernahme desselben Repositorys ergibt dieselben Kennungen; mit Zufallswerten entstünden bei jedem Lauf Dubletten, und niemand könnte sagen, welcher Eintrag der richtige ist.
Die vier Szenarien im Nachweis
A und B — Control-Server und Datenbank verloren
DROP DATABASE syncova — null Tabellen. Danach: Schema angelegt, Konfiguration
und Katalog aus dem Repository eingespielt, Administrator neu angelegt,
Repository auf „aktiv" gesetzt.
Dann die Probe: Quellverzeichnis gelöscht, Wiederherstellung über die API angestoßen.
Vorabprüfung: durchführbar, 3 Dateien / 3.500.040 Byte, 0 fehlende Blöcke
Ergebnis: succeeded, 5 Objekte, 3.500.040 Byte
Prüfsummen: ✓ alle stimmen überein
Symlink: erhalten (unterordner/verweis.txt -> ../rechnung.txt)
C — Repository-Katalog verloren
rm -rf indexes/ — der Katalog samt Verzeichnis. Danach syncova-repo list:
Der Katalog wird stillschweigend aus den Manifesten neu gebaut und wieder
abgelegt.
D — Netzunterbrechung
Kein simulierter Netzwerkfehler, sondern der härtere Fall: SIGKILL mitten in einer Wiederherstellung von 1500 Dateien.
Abbruch nach 8 s: 496 von 1500 Dateien geschrieben, Prüfpunkt bei 367
Freigabe: SCHEDULER_LOST (die Anlage gibt den verwaisten Lauf selbst frei)
Fortsetzung: succeeded, 1500 Dateien, 0 übersprungen
Prüfsummen: ✓ alle 1500 stimmen — bitgenau nach Abbruch und Fortsetzung
Der Prüfpunkt hinkt bewusst hinterher (Schreibintervall 5 s): 496 Dateien lagen am Platz, der Prüfpunkt stand bei 367. Die Fortsetzung schreibt die Differenz erneut. Das ist gewollt — die Alternative wäre ein Datenbankschreibvorgang je Datei.
Ein verwaister Lauf wird nicht automatisch fortgesetzt: Ein zweiter Lauf in ein halb gefülltes Ziel kann Daten beschädigen, die der erste bereits am Platz hatte. Er wird als gescheitert vermerkt und wartet auf einen Blick.
Funde
Fünf, alle erst durch das reale Durchspielen sichtbar.
Die Manifest-Statistik war strukturell null. Die Backup Engine schrieb ihre
Blöcke über WriteTransformedChunk unmittelbar am Repository — und damit an der
Zählung der Schreibsession vorbei. Jedes Manifest trug logical_bytes: 0. Alle
Tests blieben grün, weil keiner das Manifest auf seine Kennzahlen ansah.
Aufgefallen ist es erst, als das Repository zum ersten Mal als alleinige Quelle diente: Nach dem Verlust der Datenbank meldete jedes Backup die Größe null. Ein Repository, das seine eigene Größe nicht kennt, ist nicht selbstbeschreibend — und das ist die zentrale Zusage des Produkts. Die Engine schreibt jetzt über die Session; deduplizierte Blöcke werden ebenfalls vermerkt, denn sonst bliebe die Statistik eines Laufs über unveränderte Daten bei null.
Der Katalog-Neuaufbau scheiterte, wenn das Verzeichnis fehlte. „Katalog verloren" heißt nicht immer „Datei gelöscht"; bei einem Teilausfall des Dateisystems fehlt das ganze Verzeichnis. Ausgerechnet der Neuaufbau, der den Verlust beheben soll, brach dann ab — mit einer Meldung über eine temporäre Datei, die nichts über das eigentliche Problem sagte. Die bestehenden Tests löschten stets nur die Katalogdatei.
Eine Fortsetzung meldete einen Teilfehler, obwohl das Ergebnis vollständig war. Die Dateien zwischen Prüfpunkt und tatsächlichem Fortschritt lagen bereits am Platz und wurden übergangen — 85 von 1500. Sie stammen aus dem eigenen abgebrochenen Lauf und werden jetzt ersetzt. Ein Teilfehler, der keiner ist, ist genau die Meldung, die beim nächsten Mal niemand mehr liest. (Der Schutz gegen fremde Daten bleibt: Die Lockerung gilt ausschließlich bei einer Fortsetzung.)
Die beim Absturz geschriebene Datei blieb liegen. Unter ihrem temporären Namen, dauerhaft. Im Nachweis lagen 1501 Dateien in einem Ziel, das 1500 enthalten sollte. Eine Fortsetzung räumt jetzt auf, bevor sie beginnt.
Ein leeres Musterfeld wurde zu NULL. Beim ersten Einspielversuch brach die
Wiederherstellung an der häufigsten aller Quellen ab — der ohne Filter. Die
Transaktion rollte sauber zurück; die Datenbank blieb leer, statt halb gefüllt
zu sein. Danach folgte gleich der zweite Fehler: Die Spalten sind jsonb, nicht
text[].
Bekannte Grenzen
- Die Datenbank selbst wird nicht gesichert. Szenario B geht davon aus, dass
sie verloren ist und aus dem Repository wiederaufgebaut wird. Ein
pg_dumpdaneben ist trotzdem sinnvoll: Er bringt Läufe, Meldungen, Prüfungen und Kennzahlen zurück, die der Sicherungssatz bewusst nicht enthält. - Der Export läuft nicht selbsttätig.
syncova-dr exportist ein Kommando; es gehört in den Zeitplan des Betriebssystems oder hinter jeden Sicherungslauf. Ein Sicherungssatz von vor drei Monaten kennt die Aufträge von heute nicht. - Konten kommen ohne Passwort zurück. Das ist gewollt, bedeutet aber: Bei vielen Konten ist der Wiederaufbau Handarbeit.
- Ein Repository, das der Sicherungssatz nicht kennt, wird abgelehnt. Die Kennung im Repository muss zu der im Satz passen; sonst landeten die Wiederherstellungspunkte unter einer fremden Kennung, und ein späterer Restore suchte sie am falschen Ort.
- Szenario D ist gegen einen Serverabsturz geprüft, nicht gegen eine echte Netzunterbrechung. Für ein lokales Repository sind beide Fälle gleich; bei einem entfernten Ziel käme das Verhalten des Netzwerkstapels hinzu.