Der Abschnitt "Wohin darf zurueckgeschrieben werden?" im Runbook ist der wichtigste Zusatz: Dass ein Ziel an ProtectSystem=strict scheitert und nicht an den Rechten des Verzeichnisses, sieht man dem Fehler nicht an. Die Tabelle nennt die vier Faelle samt Grund. Die Beispiel-Einheit in der Installationsanleitung fuehrte in denselben Fehler — sie nannte nur das Repository in ReadWritePaths. Eine Anleitung, deren Ergebnis keine Wiederherstellung zulaesst, ist schlimmer als keine. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
343 lines
12 KiB
Markdown
343 lines
12 KiB
Markdown
# Wiederherstellung — Handbuch für den Ernstfall
|
|
|
|
Dieses Dokument ist für den Moment geschrieben, in dem etwas weg ist. Es
|
|
beschreibt **was zu tun ist**, nicht wie es gebaut wurde — das steht in
|
|
[recovery.md](recovery.md) und [disaster-recovery.md](disaster-recovery.md).
|
|
|
|
Vier Lagen, von der harmlosen zur schwersten. Suchen Sie die Ihre und
|
|
überspringen Sie den Rest.
|
|
|
|
| Lage | Abschnitt |
|
|
| --- | --- |
|
|
| Einzelne Dateien oder ein Ordner sind weg | [1](#1-dateien-und-ordner) |
|
|
| Ein Proxmox-Gast ist weg | [2](#2-ein-proxmox-gast) |
|
|
| Der Control-Server samt Datenbank ist weg, das Repository steht | [3](#3-control-server-und-datenbank) |
|
|
| Das Repository ist beschädigt | [4](#4-beschädigtes-repository) |
|
|
|
|
---
|
|
|
|
## 0. Zuerst: nichts überstürzen
|
|
|
|
Drei Fragen, jede kostet eine Minute und kann Stunden sparen.
|
|
|
|
**Gibt es einen brauchbaren Wiederherstellungspunkt?**
|
|
|
|
```bash
|
|
curl -s -H "Authorization: Bearer <token>" \
|
|
'https://<server>/api/v1/backups?classification=recoverable' | jq '.data[0]'
|
|
```
|
|
|
|
`recoverable` bedeutet: Dieses Backup wurde zurückgeschrieben und gegen das
|
|
Original verglichen. `verified` bedeutet nur, dass die Blöcke stimmen —
|
|
ein starkes Indiz, aber kein Nachweis. `unverified` heißt: ungeprüft.
|
|
|
|
**Lässt sich der Punkt überhaupt zurückspielen?**
|
|
|
|
```bash
|
|
curl -X POST https://<server>/api/v1/restores/validate \
|
|
-H "Authorization: Bearer <token>" -H 'Content-Type: application/json' \
|
|
-d '{"backup_id":"<id>","target_type":"filesystem","target_path":"/pfad/zum/ziel"}'
|
|
```
|
|
|
|
Die Vorabprüfung schreibt nichts. Sie prüft, ob **jeder benötigte Block noch
|
|
da ist** — ein Manifest allein belegt nur, dass jemand einmal etwas gesichert
|
|
hat.
|
|
|
|
**Wohin soll es?** Schreiben Sie nach Möglichkeit **neben** das Original, nicht
|
|
darüber. Ein Vergleich ist danach immer noch möglich; ein überschriebenes
|
|
Original nicht mehr.
|
|
|
|
---
|
|
|
|
## 1. Dateien und Ordner
|
|
|
|
### Wohin darf zurückgeschrieben werden?
|
|
|
|
**Die wichtigste Frage, und sie ist nicht offensichtlich.** Der Dienst läuft mit
|
|
`ProtectSystem=strict`: Außerhalb weniger Pfade ist das Dateisystem für ihn
|
|
schreibgeschützt, unabhängig von den Rechten des Verzeichnisses. Ein Ziel
|
|
außerhalb endet mit `mkdir: permission denied` — und zwar erst **nach** der
|
|
Vorabprüfung.
|
|
|
|
| Ort | Ergebnis |
|
|
| --- | --- |
|
|
| `/srv/syncova-restore` | ✓ von `setup.sh` angelegt und eingetragen |
|
|
| weitere aus `--wiederherstellungsziel` | ✓ |
|
|
| `/tmp/…` | ✗ landet im privaten `/tmp` des Dienstes und ist von außen unsichtbar |
|
|
| `/etc`, `/usr`, `/var/lib`, `/root` … | ✗ vom Zielschutz gesperrt |
|
|
| alles andere | ✗ schreibgeschützt durch `ProtectSystem=strict` |
|
|
|
|
Der Ordnerbaum in der Oberfläche beantwortet das direkt: Er meldet je
|
|
Verzeichnis, ob der Dienst dort schreiben darf — **gemessen** durch eine
|
|
Probedatei, nicht aus den Rechtebits abgeleitet.
|
|
|
|
Ein weiteres Ziel nachträglich freigeben:
|
|
|
|
```bash
|
|
sudo systemctl edit syncova-api # ReadWritePaths= ergänzen
|
|
sudo systemctl restart syncova-api
|
|
```
|
|
|
|
### Über die Oberfläche
|
|
|
|
Wiederherstellungspunkte → Punkt wählen → *Wiederherstellen*.
|
|
|
|
1. **Ziel** über den Ordnerbaum wählen. Beschreibbare Orte stehen oben als
|
|
Vorschlag; ein Unterverzeichnis lässt sich anlegen.
|
|
2. **Umfang** über den Backup-Browser wählen — das gesamte Backup, ein Ordner
|
|
oder eine einzelne Datei.
|
|
3. **Vorabprüfung** — sie schreibt nichts und stellt fest, ob jeder benötigte
|
|
Block noch da ist.
|
|
4. **Ausführen.**
|
|
|
|
### Über die API
|
|
|
|
```bash
|
|
curl -X POST https://<server>/api/v1/restores \
|
|
-H "Authorization: Bearer <token>" -H 'Content-Type: application/json' \
|
|
-H "Idempotency-Key: $(uuidgen)" -d '{
|
|
"backup_id": "<id>",
|
|
"target_type": "filesystem",
|
|
"target_path": "/srv/wiederhergestellt",
|
|
"path_prefix": "daten/projekte"
|
|
}'
|
|
```
|
|
|
|
`path_prefix` trifft **einen Ordner oder eine einzelne Datei**: Der Server
|
|
vergleicht auf Gleichheit oder Präfix mit Verzeichnisgrenze — `dokumente`
|
|
trifft dabei nicht `dokumentation`. Ohne ihn kommt alles zurück.
|
|
|
|
Den Inhalt eines Backups durchsehen, ohne etwas zurückzuschreiben:
|
|
|
|
```bash
|
|
curl -s -H "Authorization: Bearer <token>" \
|
|
"https://<server>/api/v1/backups/<id>/contents?path=berichte" | jq '.data.entries'
|
|
```
|
|
|
|
Fortschritt:
|
|
|
|
```bash
|
|
curl -s -H "Authorization: Bearer <token>" \
|
|
https://<server>/api/v1/restores/<id> | jq '.data | {status, files_restored, bytes_restored}'
|
|
```
|
|
|
|
### Überschreiben
|
|
|
|
Drei Hürden, absichtlich:
|
|
|
|
1. `"overwrite_existing": true`
|
|
2. die Berechtigung `restores.overwrite` — sie steckt **nicht** in
|
|
`restores.execute`
|
|
3. `"confirm_overwrite": "/srv/daten"` — der Zielpfad **wörtlich wiederholt**
|
|
|
|
Ein versehentlich gesetztes Kennzeichen in einem Skript reicht damit nicht aus.
|
|
Läuft der Schalter ins Leere, weil am Ziel nichts liegt, entfällt die
|
|
Bestätigung — ein Ritual ohne Anlass gewöhnt das Wegklicken an.
|
|
|
|
### Wenn eine Wiederherstellung abbricht
|
|
|
|
Sie wird **nicht** selbsttätig wiederholt. Ein zweiter Lauf in ein halb
|
|
gefülltes Ziel kann Daten beschädigen, die der erste bereits am Platz hatte.
|
|
Stattdessen fortsetzen:
|
|
|
|
```bash
|
|
curl -X POST https://<server>/api/v1/restores/<id>/resume -H "Authorization: Bearer <token>"
|
|
```
|
|
|
|
Die Fortsetzung räumt zuerst die beim Abbruch angefangene Datei weg und
|
|
beginnt beim Prüfpunkt. Der Prüfpunkt entsteht erst, **nachdem** eine Datei
|
|
vollständig und umbenannt am Platz liegt.
|
|
|
|
### Ohne Control-Server
|
|
|
|
Geht der Server nicht, aber das Repository steht:
|
|
|
|
```bash
|
|
./bin/syncova-repo list --path /srv/syncova-repository
|
|
./bin/syncova-agent restore \
|
|
--repository /srv/syncova-repository \
|
|
--backup <backup-id> \
|
|
--target /srv/wiederhergestellt
|
|
```
|
|
|
|
Dafür wird `SYNCOVA_ENCRYPTION_KEYS` gebraucht — bei einem verschlüsselten
|
|
Repository liegt der Datenschlüssel unter `metadata/data-key.json` und ist mit
|
|
diesem Schlüssel versiegelt.
|
|
|
|
---
|
|
|
|
## 2. Ein Proxmox-Gast
|
|
|
|
> **Ungeprüft auf echter Hardware.** Der Weg läuft gegen einen Nachbau der API
|
|
> vollständig durch, aber **ob eine wiederhergestellte Maschine startet, ist
|
|
> nicht nachgewiesen** (siehe [proxmox.md](proxmox.md)). Rechnen Sie im
|
|
> Ernstfall mit Nacharbeit.
|
|
|
|
```bash
|
|
./bin/syncova-proxmox restore-guest \
|
|
--cluster <verbund-id> \
|
|
--repository /srv/syncova-repository \
|
|
--backup <backup-id> \
|
|
--target-guest qemu/900 \
|
|
--node pve-01
|
|
```
|
|
|
|
**`--target-guest` setzen.** Ohne ihn wird der Ursprungsgast überschrieben —
|
|
und wenn der noch läuft und nur Daten fehlen, vernichtet das genau den Stand,
|
|
den man retten wollte.
|
|
|
|
Der Ablauf: Archiv aus dem Repository lesen (entschlüsseln, entpacken, jeder
|
|
Block gegen seine Kennung geprüft) → über den Zugriffsweg auf einen
|
|
Proxmox-Speicher schreiben → `qmrestore` anstoßen → Archiv wieder abräumen.
|
|
|
|
Danach:
|
|
|
|
- **Der Gast startet nicht von selbst.** `--start` ist der ausdrückliche Weg.
|
|
Eine wiederhergestellte Maschine, die sich mit derselben Adresse ins Netz
|
|
meldet wie das noch laufende Original, richtet mehr Schaden an als der
|
|
Ausfall.
|
|
- **Die Plattenzuordnung wird nicht aus der gesicherten Konfiguration gesetzt.**
|
|
Sie verwiese auf den alten Ort. Das erscheint als Warnung und ist von Hand
|
|
zu prüfen.
|
|
- **Ausgenommene Platten** (`backup=0`) fehlen. Die Ausgabe nennt sie.
|
|
|
|
---
|
|
|
|
## 3. Control-Server und Datenbank
|
|
|
|
Der Fall, für den `syncova-dr` gebaut ist. Voraussetzung: Das Repository steht,
|
|
und Sie haben den **Verschlüsselungsschlüssel**.
|
|
|
|
> Ohne `SYNCOVA_ENCRYPTION_KEYS` sind die abgelegten Geheimnisse verloren. Die
|
|
> Backups selbst bleiben lesbar, sofern der Datenschlüssel des Repositorys mit
|
|
> demselben Schlüssel versiegelt war — ist er das nicht, sind auch sie weg.
|
|
> Deshalb gehört dieser Schlüssel außerhalb der Anlage aufbewahrt.
|
|
|
|
### 3.1 Ansehen, was da ist
|
|
|
|
```bash
|
|
./bin/syncova-dr inspect --repo /srv/syncova-repository
|
|
```
|
|
|
|
Braucht **keine Datenbank**. Nach einem Totalverlust will man zuerst sehen, was
|
|
vorhanden ist.
|
|
|
|
### 3.2 Schema anlegen
|
|
|
|
```bash
|
|
./bin/syncova-migrate up
|
|
```
|
|
|
|
### 3.3 Konfiguration einspielen
|
|
|
|
```bash
|
|
./bin/syncova-dr restore --repo /srv/syncova-repository --catalog
|
|
```
|
|
|
|
`--catalog` bringt zusätzlich die Wiederherstellungspunkte zurück.
|
|
|
|
**Das Einspielen ist eine Transaktion.** Alles oder nichts — eine halb
|
|
wiederhergestellte Anlage sieht arbeitsfähig aus und scheitert beim ersten
|
|
Lauf.
|
|
|
|
### 3.4 Was danach von Hand zu tun ist
|
|
|
|
Nichts läuft von selbst wieder an. Das ist Absicht: Ein Zeitplan, der nachts
|
|
anspringt, könnte auf eine halb wiederhergestellte Anlage schreiben.
|
|
|
|
```text
|
|
Aufträge angehalten → nach Prüfung einschalten
|
|
Repositories nicht erreichbar → nach Prüfung auf aktiv setzen
|
|
Konten deaktiviert → Passwörter neu vergeben
|
|
Benachrichtigungen abgeschaltet → neu einrichten
|
|
last_verified_at leer → Prüfung anstoßen
|
|
```
|
|
|
|
Im Sicherungssatz sind **nicht** enthalten: Passwörter, TOTP-Geheimnisse,
|
|
Zugangsdaten **und Konfiguration** der Benachrichtigungswege (dort steht die
|
|
Webhook-Adresse, und die trägt oft ein Token im Pfad), Betriebstokens der
|
|
Agenten und der Verschlüsselungsschlüssel selbst.
|
|
|
|
```bash
|
|
./bin/syncova-admin create-admin --username admin
|
|
```
|
|
|
|
### 3.5 Vor dem ersten neuen Lauf
|
|
|
|
**Eine Prüfung anstoßen.** Die Wiederherstellbarkeit der übernommenen Punkte
|
|
ist nach dem Einspielen nicht belegt — sie wurde nicht neu nachgewiesen, und
|
|
`last_verified_at` ist deshalb leer.
|
|
|
|
---
|
|
|
|
## 4. Beschädigtes Repository
|
|
|
|
### 4.1 Feststellen, was betroffen ist
|
|
|
|
```bash
|
|
./bin/syncova-repo scan --path /srv/syncova-repository --deep
|
|
```
|
|
|
|
Der vollständige Lauf liest jeden Block und prüft ihn gegen seine Prüfsumme.
|
|
Er braucht **keinen** Schlüssel: Geprüft wird gegen die Prüfsumme der
|
|
abgelegten Form.
|
|
|
|
Der Bericht nennt betroffene Backups namentlich. Ein Backup mit einem einzigen
|
|
beschädigten Block ist **nicht zu 70 % wiederherstellbar, sondern gar nicht** —
|
|
die Einstufung geht deshalb auf 0 %.
|
|
|
|
### 4.2 Katalog verloren
|
|
|
|
```bash
|
|
./bin/syncova-repo rebuild --path /srv/syncova-repository
|
|
```
|
|
|
|
Der Katalog ist nur ein Beschleuniger; verbindlich sind die Manifeste. Er wird
|
|
bei Verlust, Beschädigung oder fremder Repository-Kennung ohnehin still neu
|
|
gebaut.
|
|
|
|
### 4.3 Einzelnes Manifest beschädigt
|
|
|
|
Betroffen ist genau dieses eine Backup. Die Prüfung meldet es, die
|
|
Wiederherstellung bricht ab (**0 Dateien im Ziel**, kein halbes Ergebnis), und
|
|
der Katalog-Neuaufbau schließt es aus.
|
|
|
|
Dass der Katalog es zunächst weiter anzeigt, ist richtig — er ist ein
|
|
Beschleuniger, nicht die Wahrheit.
|
|
|
|
### 4.4 Blöcke fehlen
|
|
|
|
Fehlende Blöcke lassen sich nicht wiederherstellen; sie sind weg. Was bleibt:
|
|
|
|
1. Ein **anderer** Wiederherstellungspunkt derselben Kette. Da Syncova-Manifeste
|
|
vollständig sind, ist jeder Punkt für sich wiederherstellbar — eine
|
|
zerrissene Kette gibt es nicht.
|
|
2. Ein exportierter Container (`syncova-repo export`), sofern vorhanden.
|
|
|
|
### 4.5 Verweigerte Löschung im gehärteten Modus
|
|
|
|
Kein Fehler, sondern der Schutz. Zum Entfernen eines geschützten Backups:
|
|
|
|
```bash
|
|
curl -X DELETE https://<server>/api/v1/backups/<id> -H "Authorization: Bearer <token>"
|
|
```
|
|
|
|
Der Server prüft Aufbewahrungsfrist und Legal Hold. Eine abgewehrte Löschung
|
|
wird protokolliert wie eine erfolgreiche — wer wiederholt gegen den Schutz
|
|
läuft, tut entweder etwas Falsches oder etwas Böses.
|
|
|
|
Eine rekursive Aufhebungsfunktion für den gesamten Schutz gibt es
|
|
**ausdrücklich nicht**.
|
|
|
|
---
|
|
|
|
## Wie oft man das üben sollte
|
|
|
|
Einmal im Quartal, mit einem echten Ziel und einer Stoppuhr. Die gemessene
|
|
Dauer ist die einzige RTO-Angabe, die in einem Bericht etwas wert ist — alles
|
|
andere ist eine Hochrechnung aus Datenmenge und Durchsatz, auf die sich im
|
|
Ernstfall niemand verlassen kann.
|
|
|
|
Solange nicht gemessen wurde, steht in der RTO-Spalte der Berichte „nicht
|
|
gemessen". Das ist die richtige Angabe, aber keine gute.
|