PROJ-66 (Planned): kein App- oder Cron-Backup für /var/archivmail/store, Keyfile, PostgreSQL auf 131 und 132 vorhanden. Nutzer-Rückmeldung ergänzt: Infra-Ebene sichert bereits per Proxmox Backup Server + Sync auf Zweitserver, das entschärft den Off-Site-Blocker deutlich - verbleibende Kernlücke ist ein nie getesteter Restore, nicht das fehlende Backup-Ziel. Bitwarden- Keyfile-Escrow als zusätzlicher, von PBS unabhängiger Wiederherstellungsweg ergänzt. PROJ-65: Deploy-Agent hatte Status/QA-Ergebnisse bereits lokal geschrieben, aber noch nicht committed.
460 lines
26 KiB
Markdown
460 lines
26 KiB
Markdown
# PROJ-66: Backup-Strategie für archivmail (Produktiv + Teilproduktiv)
|
|
|
|
**Status:** Planned
|
|
**Erstellt:** 2026-07-04
|
|
|
|
## Problem / Ausgangslage
|
|
|
|
**Nachtrag (2026-07-04, nach Nutzer-Rückmeldung):** Auf Infrastruktur-Ebene
|
|
existiert bereits eine Sicherung: Die Container werden per **Snapshot auf
|
|
einen zweiten Server synchronisiert** und zusätzlich **per Proxmox Backup
|
|
Server (PBS) gezogen**. Das ist von innerhalb des Containers nicht sichtbar
|
|
(läuft vom Proxmox-Host aus) — deshalb kam die ursprüngliche Prüfung unten
|
|
("kein Backup-Prozess") zu einem unvollständigen Bild. Das entschärft die
|
|
zuvor als Blocker geführte Frage nach einem Off-Site-Ziel erheblich: PBS +
|
|
Sync auf zweiten Server ist bereits eine physisch getrennte Sicherung.
|
|
|
|
Was das ändert und was offen bleibt, siehe Abschnitt "Bewertung der
|
|
Infra-Ebene" weiter unten. Der ursprüngliche Befund (App-Ebene: kein
|
|
`pg_dump`/rsync/Cron für archivmail selbst) bleibt als Kontext stehen, ist
|
|
aber durch die Infra-Sicherung teilweise bereits abgedeckt.
|
|
|
|
### Verifizierter Ist-Stand (geprüft am 2026-07-04, beide Hosts)
|
|
|
|
Beide Server sind **LXC-Container** (`systemd-detect-virt` → `lxc`), das
|
|
zugrunde liegende Storage ist ein ZFS-Dataset auf dem Proxmox-Host
|
|
(`zfs/data/subvol-1131-disk-0` bzw. `-1132-disk-0`), aber **aus dem Container
|
|
heraus nicht als ZFS ansprechbar** (kein `zfs`/`zpool`-Binary im Container,
|
|
kein Zugriff auf Host-Snapshots). Zff-Snapshots wären nur vom Proxmox-Host aus
|
|
möglich — das ist eine separate Maßnahme außerhalb der Kontrolle des
|
|
archivmail-Deploy-Prozesses und wird hier als Zusatzoption erwähnt, aber nicht
|
|
als alleinige Lösung behandelt (siehe Optionen unten).
|
|
|
|
Geprüft und **nicht vorhanden**:
|
|
- `crontab -l` (root): leer auf beiden Hosts
|
|
- `/etc/cron.d/`: nur `archivmail` (App-eigener Cron, PROJ-56c Purge/Reindex/OCR), `e2scrub_all`, `sysstat` — kein Backup-Eintrag
|
|
- `/etc/cron.{daily,hourly,weekly}`: nur Standard-Debian-Pakete (apt, dpkg, logrotate, man-db) — kein Backup-Skript
|
|
- `systemctl list-timers`: nur Standard-systemd/apt-Timer, kein Backup-Timer
|
|
- LVM: kein `lvs`/`vgs` im Container
|
|
- Borg/Restic/rsnapshot/Bacula: nicht installiert
|
|
- `/var/backups/`: enthält nur dpkg/apt-Housekeeping-Dateien, keine archivmail-Daten
|
|
|
|
Vorhanden, aber **ungenutzt/nicht automatisiert**:
|
|
- `pg_basebackup`, `pg_backupcluster` (Debian-PostgreSQL-Standardtools) — installiert, aber kein Aufruf irgendwo
|
|
- `manticore-backup` — installiert, kein Aufruf irgendwo
|
|
|
|
**Fazit:** Bei Totalausfall eines der beiden Container (Hardware, Filesystem-
|
|
Korruption, versehentliches `rm`, Ransomware, fehlerhaftes Update) sind Store,
|
|
Keyfile und Datenbank **unwiederbringlich verloren**. Das betrifft aktuell
|
|
sowohl 131 (Produktiv) als auch 132 (teilproduktiv, ~51.000 Mails).
|
|
|
|
### Aktuelle Datenmengen (Referenz für Aufwandsschätzung)
|
|
|
|
| Server | Store-Größe | DB | Config |
|
|
|---|---|---|---|
|
|
| 131 | 3,8 MB (frisch, PROJ-65-Migration) | n/a (Verbindungsproblem bei Check — Peer-Auth) | 23 KB |
|
|
| 132 | 6,9 GB (~51.000 Mails) | n/a (nicht separat geprüft) | — |
|
|
|
|
Store-Wachstum ist also unterschiedlich weit fortgeschritten; 132 hat den
|
|
größeren produktiven Datenbestand trotz "Test"-Label.
|
|
|
|
## Bewertung der Infra-Ebene (PBS + Server-Sync)
|
|
|
|
Container-/Snapshot-Backup auf Proxmox-Ebene sichert typischerweise das
|
|
komplette Root-Filesystem des LXC — d.h. `/var/archivmail/store`,
|
|
`/etc/archivmail/keyfile`, `/etc/archivmail/config.yml`, PostgreSQL-Datendir
|
|
und `/var/log/archivmail/audit.log` **gemeinsam als ein Snapshot**. Das löst
|
|
das in "Warum das kritisch ist" beschriebene Kern-Risiko (Store/Keyfile/DB
|
|
müssen zusammen gesichert werden) potenziell bereits vollständig, da alles im
|
|
selben Container liegt und in einem Snapshot landet — vorausgesetzt
|
|
PostgreSQL läuft ebenfalls im selben Container wie Store/Keyfile (zu
|
|
verifizieren, siehe offene Punkte unten).
|
|
|
|
**Was dadurch (voraussichtlich) bereits erfüllt ist:**
|
|
- Physische Trennung (AC 3) — zweiter Server + PBS sind eigene Infrastruktur.
|
|
- Gemeinsame Konsistenz von Store+DB+Keyfile+Config (AC 1) — ein
|
|
Filesystem-Snapshot enthält zwangsläufig alle Dateien vom selben Zeitpunkt.
|
|
- Hardlink-Erhalt (AC 2) — ein Block-/Filesystem-Snapshot (ZFS/PBS) kopiert
|
|
keine Dateien einzeln, sondern sichert die Filesystem-Struktur inklusive
|
|
Inode-Referenzen — Hardlinks aus PROJ-65 bleiben dabei erhalten (anders als
|
|
bei einem naiven `cp -r`, das dieses Ticket ursprünglich als Risiko für ein
|
|
noch zu bauendes App-Backup benannt hatte).
|
|
|
|
**Was weiterhin offen bleibt, auch mit PBS+Sync:**
|
|
1. **PostgreSQL-Crash-Konsistenz:** Ein Snapshot mitten im Betrieb ist für
|
|
PostgreSQL kein sauberes Backup wie `pg_dump`, sondern entspricht einem
|
|
Stromausfall-Szenario — PostgreSQL kommt damit über WAL-Replay beim
|
|
nächsten Start i.d.R. wieder in einen konsistenten Zustand (das ist der
|
|
Normalfall, für den WAL gebaut ist), aber das ist **nicht dasselbe wie
|
|
ein verifizierter, anwendungskonsistenter Dump**. Sollte einmal getestet
|
|
werden (Restore-Test, siehe unten), nicht nur angenommen.
|
|
2. **Restore ist ungetestet** — ein Backup, das nie zurückgespielt wurde,
|
|
ist nicht verifiziert (gilt für PBS-Snapshots genauso wie für ein
|
|
selbstgebautes rsync-Skript). Das ist unabhängig davon, wer das Backup
|
|
zieht, weiterhin ein offener Punkt.
|
|
3. **Retention/DSGVO-Abgleich:** Wie lange hält PBS die Snapshots vor, und
|
|
ist das mit der DSGVO-Löschkonzeption (PROJ-50) abgestimmt? Muss geklärt
|
|
werden, ist aber eine reine Konfigurationsfrage im PBS, kein neuer Code.
|
|
4. **Monitoring/Alerting:** Läuft der PBS-Job zuverlässig durch, und gibt es
|
|
eine sichtbare Warnung bei Ausfall? (Vermutlich ja, PBS hat eigenes
|
|
Job-Monitoring — zu verifizieren, nicht Teil des archivmail-Codes.)
|
|
5. **Keyfile-Einzelkopie (Bitwarden):** Zusätzlich zur PBS-Sicherung soll das
|
|
Keyfile separat in Bitwarden hinterlegt werden (Nutzer-Entscheidung,
|
|
2026-07-04) — als unabhängiger Wiederherstellungsweg, falls PBS/Snapshot-
|
|
Infrastruktur selbst nicht verfügbar ist. Details siehe eigener Abschnitt
|
|
unten.
|
|
|
|
**Auswirkung auf den Rest dieser Spec:** Die unten stehenden Optionen A/B/C
|
|
und der App-eigene rsync+cron-Ansatz sind jetzt **Ergänzung, nicht mehr
|
|
alleinige Lösung** — vor allem für den Fall, dass ein anwendungskonsistenter
|
|
`pg_dump` zusätzlich zur PBS-Snapshot-Sicherung gewünscht wird (schnellerer,
|
|
selektiver Restore einzelner Tabellen, unabhängig von Snapshot-Zeitpunkten).
|
|
Die **kritischste verbleibende Lücke ist nicht mehr "kein Backup-Ziel"**,
|
|
sondern **"Restore nie getestet"** — das bleibt so oder so zu tun, egal
|
|
welche Backup-Quelle (PBS oder App-Skript) im Ernstfall genutzt wird.
|
|
|
|
## Warum das kritisch ist
|
|
|
|
1. **Datenverlust ohne Backup ist bei einem Mail-Archivsystem ein
|
|
Compliance-Vorfall**, nicht nur ein IT-Vorfall — GoBD verlangt
|
|
Verfügbarkeit archivierter Mails über die Aufbewahrungsfrist.
|
|
2. Store, Keyfile und PostgreSQL-Metadaten sind **nur gemeinsam** nutzbar:
|
|
- Store ohne DB-Metadaten: Dateien vorhanden, aber keine Zuordnung zu
|
|
Mail/Tenant/Betreff/Datum → praktisch nicht auffindbar.
|
|
- Keyfile ohne Store: irrelevant, nichts zu entschlüsseln.
|
|
- DB ohne Keyfile: Metadaten vorhanden, aber Mail-Inhalte nicht mehr
|
|
entschlüsselbar → GoBD-Anforderung "Lesbarmachung" verletzt.
|
|
- Ein Backup, das nur eine dieser drei Komponenten sichert, ist wertlos.
|
|
3. **PROJ-65** hat gerade Tenant-Hardlink-Verzeichnisse (`store/tenant_<id>/`)
|
|
eingeführt. Ein Backup-Tool, das Hardlinks nicht erhält (z.B. `cp -r`,
|
|
naives `scp -r`), würde bei jedem Backup den Speicherbedarf im Backup-Ziel
|
|
vervielfachen (pro Tenant eine Vollkopie statt eines Hardlinks) — kein
|
|
Datenverlust, aber Speicherplatz-Explosion und potenziell Backup-Jobs, die
|
|
wegen vollem Zielverzeichnis fehlschlagen.
|
|
|
|
## Backup-Strategie-Optionen
|
|
|
|
### Option A — rsync + cron, konsistenter Snapshot via kurzem Stop (empfohlen als erster Schritt)
|
|
|
|
Ablauf pro Lauf (nachts, Zeitfenster mit wenig Traffic wählen, ggf. an
|
|
PROJ-56-Last-Entzerrung orientieren):
|
|
|
|
1. `systemctl stop archivmail` (Backend stoppen — Store/DB werden während
|
|
des Kopiervorgangs nicht verändert; Frontend kann laufen bleiben, da es
|
|
nur liest, aber optional auch stoppen für saubere Konsistenz)
|
|
2. `pg_dump -Fc archivmail > backup.dump` (Custom-Format, komprimiert,
|
|
erlaubt selektiven Restore einzelner Tabellen)
|
|
3. `rsync -aH --delete /var/archivmail/store/ <ziel>/store/` — **`-H` ist
|
|
Pflicht**, sonst werden Tenant-Hardlinks aus PROJ-65 zu Vollkopien
|
|
aufgelöst
|
|
4. `cp /etc/archivmail/keyfile <ziel>/keyfile` (getrennt verschlüsselt/
|
|
abgesichert, siehe unten)
|
|
5. `cp /etc/archivmail/config.yml <ziel>/config.yml`
|
|
6. `cp /var/log/archivmail/audit.log <ziel>/audit.log.$(date +%F)` (Append-
|
|
Only-Charakter erhalten — Datei nicht überschreiben, sondern datiert
|
|
kopieren, damit historische Audit-Nachweise auch bei Log-Rotation auf dem
|
|
Quellsystem nicht verloren gehen)
|
|
7. `systemctl start archivmail`
|
|
8. Health-Check nach Neustart (Backend ✓ läuft, wie im Standard-Deploy-Workflow)
|
|
9. Manticore-Index: **niedrige Priorität**, aus Store+DB per
|
|
`archivmail reindex` rekonstruierbar — kein Backup nötig, ggf. optional
|
|
`manticore-backup` für schnelleren Recovery-Pfad (Reindex von 51k Mails
|
|
kann je nach OCR-Last dauern)
|
|
|
|
Vorteil: Downtime ist kurz (Stop → Kopieren der geänderten Blöcke via rsync
|
|
inkrementell → Start), einfach zu verstehen, kein neues Tooling nötig außer
|
|
rsync/cron (beide bereits vorhanden). Nachteil: Downtime existiert (wenn auch
|
|
kurz bei inkrementellem rsync nach dem ersten vollen Lauf).
|
|
|
|
### Option B — PITR / kontinuierliche Konsistenz ohne Stop (vollständige Lösung, später)
|
|
|
|
- PostgreSQL: WAL-Archiving + Basisbackup (`pg_basebackup`) für
|
|
Point-in-Time-Recovery statt Downtime beim Dump.
|
|
- Store: Da Store append-only/content-adressiert ist (Dateien werden nach
|
|
Schreiben nie verändert, nur neue hinzugefügt oder per GoBD-Purge gelöscht),
|
|
ist ein Live-rsync ohne Stop grundsätzlich risikoarm — die einzige
|
|
Inkonsistenz-Gefahr ist ein Mail-Datensatz, der in der DB bereits committed
|
|
ist, aber dessen Store-Datei noch nicht vollständig geschrieben wurde (Race
|
|
zwischen Storage-Write und DB-Commit). Das lässt sich durch Reihenfolge
|
|
Store-zuerst-dann-DB im Schreibpfad (falls nicht schon so implementiert —
|
|
bei mailarchiv-architect erfragen) und/oder einen Reconciliation-Check nach
|
|
dem Restore (PROJ-52 `archivmail reconcile`) abfedern statt durch Downtime.
|
|
- Aufwand deutlich höher (WAL-Archiv-Ziel, Retention der WAL-Segmente, mehr
|
|
bewegliche Teile) — als Phase 2 vorschlagen, nicht als ersten Schritt.
|
|
|
|
### Option C — Proxmox-Host-Snapshots (ZFS)
|
|
|
|
Da beide Hosts LXC-Container auf Proxmox mit ZFS-Backing sind, sind
|
|
Host-seitige ZFS-Snapshots oder Proxmox-Backup-Server-Jobs eine sinnvolle
|
|
**Ergänzung** (schnelle Rollback-Möglichkeit bei OS-/Config-Fehlern), aber
|
|
kein Ersatz für anwendungskonsistente Backups: Ein ZFS-Snapshot mitten im
|
|
`pg_dump`-losen Normalbetrieb sichert PostgreSQL nicht transaktionskonsistent
|
|
(außer Postgres nutzt selbst WAL + fsync korrekt, was bei ZFS-Snapshots auf
|
|
Blockebene in der Praxis meist funktioniert, aber nicht ausdrücklich
|
|
validiert wurde). Empfehlung: als Zusatzschicht einplanen, aber **erfordert
|
|
Zugriff auf den Proxmox-Host**, der nicht Teil dieses Deploy-Agenten-Scopes
|
|
ist — separates Ticket/Absprache mit dem Infrastruktur-Verantwortlichen für
|
|
den Proxmox-Host nötig.
|
|
|
|
### Empfehlung
|
|
|
|
**Phase 1 (dieses Ticket, pragmatisch):** Option A — rsync+cron mit kurzem
|
|
Stop, tägliches Backup auf ein Off-Container-Ziel (siehe Off-Site unten).
|
|
Deckt das eigentliche Risiko (Totalverlust ohne jegliches Backup) sofort ab.
|
|
|
|
**Phase 2 (späteres Ticket):** Option B (PITR) wenn Downtime-Fenster zum
|
|
Problem wird (z.B. wegen wachsendem Store auf 132), plus Option C als
|
|
zusätzliche Sicherheitsschicht in Absprache mit Proxmox-Host-Admin.
|
|
|
|
## Backup-Ziel: On-Site vs. Off-Site
|
|
|
|
- **On-Site-only ist nicht ausreichend**: Beide Produktivsysteme (131, 132)
|
|
laufen vermutlich auf derselben Proxmox-Host-Infrastruktur — ein
|
|
Host-Ausfall (Hardware, Ransomware, Fehlkonfiguration) könnte beide Systeme
|
|
UND ein On-Site-Backup-Ziel gleichzeitig treffen, wenn es auf demselben
|
|
Host/Storage liegt.
|
|
- Empfehlung: Backup-Ziel auf physisch getrennter Infrastruktur (zweiter
|
|
Host, NAS außerhalb des Proxmox-Clusters, oder Cloud-Storage mit
|
|
Verschlüsselung — Cloud nur wenn DSGVO-konform vertraglich abgesichert,
|
|
siehe Verschlüsselung unten).
|
|
- Mindestens: 3-2-1-Prinzip anstreben (3 Kopien, 2 verschiedene Medien, 1
|
|
Off-Site) — für den pragmatischen ersten Schritt reicht 2 Kopien (Produktiv
|
|
+ 1 Backup-Ziel off-site) mit klarer Dokumentation, dass das noch nicht
|
|
vollständig 3-2-1 ist.
|
|
|
|
## Verschlüsselung des Backups selbst
|
|
|
|
Das Backup enthält **Keyfile UND verschlüsselte Mails** zusammen — sobald
|
|
beide am selben Ort liegen, ist der Verschlüsselungsschutz des Stores
|
|
faktisch aufgehoben, wenn das Backup-Ziel kompromittiert wird. Anforderungen:
|
|
|
|
1. **Transport:** rsync über SSH (ohnehin Standard) oder explizit
|
|
`rsync -e ssh`, niemals unverschlüsseltes Protokoll (FTP, unverschlüsseltes
|
|
NFS über öffentliches Netz).
|
|
2. **At-Rest am Backup-Ziel:** Wenn das Backup-Ziel als weniger vertrauenswürdig
|
|
gilt als der Produktivserver (z.B. NAS mit mehr Admin-Zugriffsberechtigten,
|
|
Cloud-Storage), muss das Backup-Ziel selbst verschlüsselt sein — entweder
|
|
Ziel-Filesystem-Verschlüsselung (LUKS) oder Backup-Tool mit eingebauter
|
|
Verschlüsselung (z.B. `restic` mit Repository-Passwort — als Phase-2-Option
|
|
evaluieren, da es außerdem Deduplizierung/Versionierung mitbringt).
|
|
3. **Keyfile separat behandeln:** Empfehlung, das Keyfile-Backup zusätzlich
|
|
getrennt vom Store-Backup aufzubewahren (anderer Zugriffspfad/anderes
|
|
Passwort) — erschwert Angreifern, die nur das Backup-Ziel kompromittieren,
|
|
den vollen Zugriff. Mindestens: restriktive Dateirechte (0600, root-only)
|
|
auch im Backup.
|
|
4. Backup-Skript selbst darf Zugangsdaten (SSH-Key fürs Backup-Ziel) nicht im
|
|
Klartext in world-readable Cron-Dateien ablegen (analog zu PROJ-64
|
|
Datei-Permissions-Härtung).
|
|
|
|
### Keyfile-Escrow in Bitwarden (Nutzer-Entscheidung, 2026-07-04)
|
|
|
|
Zusätzlich zur PBS-/Sync-Sicherung wird das Keyfile als unabhängige Kopie in
|
|
Bitwarden hinterlegt — für den Fall, dass die Snapshot-/PBS-Infrastruktur
|
|
selbst nicht verfügbar oder kompromittiert ist.
|
|
|
|
- **Zugriffsbeschränkung:** Eigene Bitwarden-Collection/Ordner, nur für
|
|
Personen mit Restore-Berechtigung sichtbar — nicht die allgemeine
|
|
Organisations-Vault. Das Keyfile entschlüsselt das **gesamte Archiv aller
|
|
Mandanten**, ist der höchstwertige Secret im System.
|
|
- **Format:** Secure Note mit Base64-kodiertem Keyfile-Inhalt (32 Byte roh
|
|
→ base64 fürs Notizfeld), plus Metadaten (welcher Server/welche
|
|
Installation, Erstellungsdatum, wer hat's zuletzt geprüft).
|
|
- **Kein Ersatz für Zugriffskontrolle:** Bitwarden-Zugriff muss genauso
|
|
ernst genommen werden wie root-Zugriff auf den Produktivserver (2FA
|
|
Pflicht, Zugriffsprotokoll wenn Bitwarden das bietet).
|
|
- **Rotation/Prüfung dokumentieren:** Wer Zugriff hat und wann zuletzt
|
|
verifiziert wurde, dass der Bitwarden-Eintrag noch mit dem aktiven Keyfile
|
|
übereinstimmt (relevant falls das Keyfile jemals rotiert wird — aktuell
|
|
nicht vorgesehen, aber für die Zukunft dokumentieren).
|
|
- Kein Blocker für die restlichen ACs — unabhängig von PBS/App-Backup
|
|
umsetzbar, kleiner Zusatzschritt (Bitwarden-Eintrag anlegen, kein Code).
|
|
|
|
## Aufbewahrungsdauer / Rotation
|
|
|
|
- Vorschlag: Tägliche Backups, Rotation nach Generationen-Prinzip:
|
|
- 7 tägliche Backups
|
|
- 4 wöchentliche Backups (z.B. jeweils Sonntag)
|
|
- 6 monatliche Backups
|
|
- Wichtig: Rotation der **Backup-Kopien** ist unabhängig von der
|
|
GoBD-Aufbewahrungsfrist der Mails selbst (PROJ-51 Retention-Kategorien) —
|
|
eine gelöschte/abgelaufene Mail in Produktion kann in einem älteren Backup
|
|
noch enthalten sein. Das ist im Rahmen der GoBD-Löschkonzeption (PROJ-50,
|
|
DSGVO-Löschersuchen) zu dokumentieren: Backups mit personenbezogenen Daten,
|
|
die in Produktion bereits DSGVO-gelöscht wurden, dürfen nicht unbegrenzt
|
|
aufbewahrt werden → Backup-Rotation muss kurz genug sein, dass gelöschte
|
|
Daten nach spätestens N Rotationszyklen auch aus alten Backups verschwinden
|
|
(Abstimmung mit Datenschutzbeauftragtem/Nutzer nötig, kein rein technisches
|
|
Thema).
|
|
|
|
## Restore-Prozess (muss dokumentiert UND getestet sein)
|
|
|
|
Kein Backup ohne verifizierten Restore-Prozess. Vorschlag Runbook-Struktur
|
|
(Stil analog `docs/MIGRATION_RUNBOOK.md`):
|
|
|
|
1. Zielserver vorbereiten (frisches Debian oder vorhandener Server im
|
|
Wartungsmodus, archivmail-Dienste gestoppt).
|
|
2. Store wiederherstellen: `rsync -aH <backup>/store/ /var/archivmail/store/`
|
|
(Hardlinks bleiben erhalten).
|
|
3. Keyfile wiederherstellen: `cp <backup>/keyfile /etc/archivmail/keyfile`,
|
|
Rechte prüfen (0600, root:root).
|
|
4. Config wiederherstellen: `cp <backup>/config.yml /etc/archivmail/config.yml`.
|
|
5. PostgreSQL wiederherstellen: `pg_restore -d archivmail <backup>/backup.dump`
|
|
(auf leere, frisch angelegte DB).
|
|
6. Audit-Log wiederherstellen (informativ, kein Funktionsbestandteil):
|
|
`/var/log/archivmail/audit.log` aus dem letzten Backup-Stand kopieren.
|
|
7. Dienste starten, Health-Check (Backend ✓, Frontend ✓ wie im
|
|
Standard-Deploy-Workflow).
|
|
8. **Verifikation (Pflicht, nicht optional):**
|
|
- `archivmail reconcile` laufen lassen (PROJ-52) — Zählvergleich
|
|
Store-Dateien vs. DB-Einträge muss aufgehen.
|
|
- Stichprobe: 3-5 zufällige Mails über die UI öffnen und lesbar
|
|
entschlüsseln lassen (beweist Keyfile+Store+DB passen zusammen).
|
|
- `archivmail reindex` ausführen, Suche testen (Manticore wird aus 1+3
|
|
rekonstruiert).
|
|
9. Restore-Ergebnis (Mail-Zahl, Zeitstempel, Dauer) dokumentieren — analog zum
|
|
Migrations-Runbook-Prinzip "Zählen, Prüfen, Belegen".
|
|
|
|
**Regelmäßiger Restore-Test:** Ein Backup, das nie zurückgespielt wurde, ist
|
|
nicht verifiziert. Empfehlung: mindestens vierteljährlich einen Restore auf
|
|
einen isolierten Test-Container durchführen (nicht auf 131/132!) und Schritt
|
|
8 durchlaufen.
|
|
|
|
## Monitoring / Alerting für den Backup-Job selbst
|
|
|
|
Größtes Risiko bei Cron-Backups: Job schlägt still fehl, niemand merkt es,
|
|
bis der Ernstfall eintritt (siehe PROJ-58-Lehre: "Cron-Zeilen fehlten
|
|
wochenlang trotz aktivem Code, weil es niemand automatisch prüfte").
|
|
|
|
Anforderungen:
|
|
- Backup-Skript muss Exit-Code aller Teilschritte prüfen (rsync, pg_dump,
|
|
cp) und bei Fehler **nicht** die Rotation der alten (guten) Backups
|
|
anstoßen (sonst wird ein gutes altes Backup durch ein fehlgeschlagenes
|
|
überschrieben).
|
|
- Log-Datei pro Lauf mit Zeitstempel, Ergebnis, Dauer, Größe.
|
|
- Alerting: minimal ein "Backup-Alter"-Check, der z.B. vom bestehenden
|
|
Health-Check-Mechanismus oder Dashboard (PROJ-17-Metriken-Anlehnung)
|
|
mitgeprüft wird — "letztes erfolgreiches Backup älter als 26h" →
|
|
sichtbarer Warnzustand (Dashboard-Badge oder E-Mail/Webhook, je nachdem was
|
|
im System bereits an Alerting existiert; falls nichts existiert, ist das
|
|
ein Teil dieses Tickets: minimal ein Cron-Check-Skript, das eine
|
|
Markerdatei mit Zeitstempel nach erfolgreichem Lauf schreibt und ein
|
|
zweiter Cron-Job/Health-Endpoint das Alter dieser Datei prüft).
|
|
|
|
## User Stories
|
|
|
|
- **Als Betreiber** möchte ich, dass Store, Keyfile und DB-Metadaten täglich
|
|
automatisiert und konsistent gesichert werden, damit ein Server-Totalausfall
|
|
nicht zu unwiederbringlichem Mail-Verlust führt.
|
|
- **Als Betreiber** möchte ich, dass ein fehlgeschlagener Backup-Lauf sichtbar
|
|
wird (nicht erst beim Restore-Versuch auffällt), damit ich rechtzeitig
|
|
eingreifen kann.
|
|
- **Als GoBD-Verantwortlicher** möchte ich, dass ich im Audit-Fall belegen
|
|
kann, dass Backups vorhanden, aktuell und restorebar sind.
|
|
- **Als Datenschutzbeauftragter** möchte ich, dass DSGVO-Löschungen (PROJ-50)
|
|
nicht durch unbegrenzt aufbewahrte Backups unterlaufen werden.
|
|
|
|
## Acceptance Criteria
|
|
|
|
1. Es existiert ein dokumentiertes, automatisiert laufendes Backup (Cron
|
|
oder systemd-Timer) auf 131 UND 132, das Store, Keyfile, PostgreSQL-DB und
|
|
config.yml konsistent zueinander sichert.
|
|
2. Das Backup-Tool erhält nachweislich Hardlinks im Store (Test: Backup-Größe
|
|
nach PROJ-65-Tenant-Hardlinks entspricht nicht der Summe aller
|
|
Tenant-Kopien, sondern der tatsächlichen Store-Größe — Stichprobe via
|
|
`du --apparent-size` vs. `du` bzw. `stat` Link-Count-Check nach Restore).
|
|
3. Backup liegt auf einem vom Produktivsystem physisch getrennten Ziel
|
|
(nicht im selben Proxmox-Host/-Storage wie 131/132, sofern das ohne
|
|
zusätzliche Host-Beschaffung machbar ist — sonst als bekannte Einschränkung
|
|
dokumentieren).
|
|
4. Backup-Daten (insbesondere Keyfile-Kopie und Store-Kopie zusammen) sind am
|
|
Zielort verschlüsselt oder das Ziel ist gleichwertig abgesichert wie der
|
|
Produktivserver.
|
|
5. Rotation/Retention ist implementiert und lässt Backups nicht unbegrenzt
|
|
wachsen; Retention ist kurz genug, um DSGVO-Löschungen nicht dauerhaft zu
|
|
unterlaufen (Zeitraum mit Nutzer/Datenschutzbeauftragtem abgestimmt).
|
|
6. Ein vollständiger Restore wurde mindestens einmal auf einem isolierten
|
|
Testsystem durchgeführt und mit `archivmail reconcile` + Stichproben-
|
|
Entschlüsselung verifiziert; Ergebnis ist dokumentiert.
|
|
7. Es gibt einen sichtbaren Alarm-/Warnmechanismus, wenn der letzte
|
|
erfolgreiche Backup-Lauf älter als ein definiertes Schwellenwert-Fenster
|
|
ist (z.B. 26h bei täglichem Backup).
|
|
8. Ein Backup-Runbook (analog `docs/MIGRATION_RUNBOOK.md`) existiert unter
|
|
`docs/BACKUP_RESTORE_RUNBOOK.md` mit Schritt-für-Schritt-Anleitung für
|
|
Backup, Restore und Verifikation.
|
|
9. `update.sh`-Vollständigkeits-Check: Falls das Backup-Skript/der Cron-Job
|
|
als Datei unter `deploy/` im Repo gepflegt wird, synct `update.sh` diese
|
|
Datei nachweislich mit aus (Lehre aus PROJ-58, siehe Deploy-Vollständigkeits-
|
|
Check in den devops-deploy-Regeln) — sonst existiert das Skript nur lokal
|
|
und geht beim nächsten Server-Neuaufsatz verloren.
|
|
10. **Keyfile-Kopie liegt zugriffsbeschränkt in Bitwarden** (Secure Note,
|
|
eigene Collection, nur Restore-Berechtigte) — unabhängig von PBS/App-Backup.
|
|
11. **PBS-/Sync-Sicherung verifiziert:** Dokumentiert ist, was genau gesichert
|
|
wird (welcher Container/welche Pfade), wie lange PBS die Snapshots vorhält,
|
|
und dass mindestens ein Restore aus einem PBS-Snapshot erfolgreich getestet
|
|
wurde (nicht nur aus einem App-eigenen Backup, falls beide existieren).
|
|
|
|
## Edge Cases
|
|
|
|
- **Server-Totalausfall (131 oder 132):** Restore auf neuem/wiederhergestelltem
|
|
Container muss laut Runbook in angemessener Zeit möglich sein (Ziel-RTO
|
|
definieren, z.B. < 4h für 132 mit 51k Mails — abhängig von Reindex-Dauer).
|
|
- **Backup-Job schlägt fehl, ohne dass es auffällt:** durch Monitoring/
|
|
Alerting (siehe oben) abgedeckt — Testfall: Backup-Skript künstlich zum
|
|
Scheitern bringen (z.B. Zielverzeichnis vollschreiben) und prüfen, ob der
|
|
Alarm tatsächlich auslöst.
|
|
- **Restore-Test schlägt fehl:** Reconcile zeigt Lücken → Ursache muss vor
|
|
Produktivnutzung des Backups geklärt werden (z.B. Backup lief während
|
|
aktiven Schreibvorgangs ohne Stop-Schritt → Konsistenzproblem im
|
|
Backup-Skript selbst beheben).
|
|
- **Backup-Ziel läuft voll:** Rotation muss dies verhindern; zusätzlich
|
|
Disk-Space-Check vor jedem Lauf (analog `df -h`-Diagnose-Befehl im
|
|
devops-deploy-Skill) mit Abbruch + Alarm statt partiellem/korruptem Backup.
|
|
- **PROJ-65-Hardlinks nach Restore:** Nach Restore prüfen, dass
|
|
Tenant-Hardlink-Struktur (`store/tenant_<id>/`) wiederhergestellt ist und
|
|
nicht zu doppeltem Speicherverbrauch auf dem Zielsystem führt.
|
|
- **Gleichzeitiger Schreibzugriff während Backup (Option A mit Stop):** Kurzer
|
|
Downtime-Fenster einplanen und in Betriebszeiten kommunizieren (SMTP-
|
|
Empfang während Stop puffert sich ggf. beim Absender/Relay, sollte mit
|
|
mailarchiv-architect abgeklärt werden, ob eingehende Mails während Backend-
|
|
Stop verloren gehen könnten oder ob es einen Retry-Mechanismus gibt).
|
|
- **132 ist "teilproduktiv":** Muss von Anfang an mit derselben Priorität wie
|
|
131 behandelt werden, nicht nachrangig als "nur Testserver" — bereits im
|
|
Ticket-Titel und in den ACs berücksichtigt (beide Server, nicht nur 131).
|
|
|
|
## Aufwandseinschätzung
|
|
|
|
Durch die vorhandene PBS+Sync-Infrastruktur verschiebt sich der Schwerpunkt
|
|
von "Backup-Ziel schaffen" zu "vorhandenes Backup verifizieren + gezielt
|
|
ergänzen":
|
|
|
|
- **Phase 0 (klein, zuerst):** Bitwarden-Keyfile-Escrow anlegen (kein Code,
|
|
<1h). Dokumentieren, was PBS/Sync konkret sichert und mit welcher
|
|
Retention (Abstimmung mit demjenigen, der PBS betreibt — vermutlich kein
|
|
archivmail-Code-Zugriff nötig, eher ein Gespräch/Screenshot der
|
|
PBS-Job-Konfiguration). Geschätzt **1-2h**.
|
|
- **Phase 1 (Restore-Verifikation, wichtigster verbleibender Schritt):** Ein
|
|
echter Restore-Test aus einem PBS-Snapshot auf einen isolierten
|
|
Test-Container, durchlaufen mit `archivmail reconcile` + Stichproben-
|
|
Entschlüsselung (siehe Restore-Prozess oben). Deckt auf, ob die
|
|
PostgreSQL-Crash-Konsistenz in der Praxis reicht oder ob doch ein
|
|
zusätzlicher `pg_dump` nötig ist. Geschätzt **2-4h** (abhängig davon, wie
|
|
schnell ein Testsystem für den Restore verfügbar ist).
|
|
- **Phase 2 (nur falls Phase 1 Lücken zeigt):** App-eigenes rsync+cron+pg_dump
|
|
(Option A) als Ergänzung, falls PBS-Snapshots allein nicht ausreichen
|
|
(z.B. weil Retention zu kurz für Compliance ist, oder PITR-Granularität
|
|
gebraucht wird). Aufwand wie ursprünglich geschätzt, **4-6h**.
|
|
- **Phase 3 (optional, später):** PITR/WAL-Archiving, restic/borg-Migration —
|
|
nur falls Phase 1/2 einen konkreten Bedarf dafür aufzeigen.
|
|
|
|
**Offene Entscheidung vor Implementierung von Phase 1:** Steht ein isolierter
|
|
Test-Container für den Restore-Test zur Verfügung (nicht 131/132 selbst)?
|
|
|
|
## Nicht Teil dieses Tickets
|
|
|
|
- Implementierung selbst (dieses Ticket liefert nur die Spec, Status
|
|
"Planned" — Implementierung erst nach Freigabe).
|
|
- Konfiguration/Änderung der PBS-Jobs selbst (liegt außerhalb des
|
|
archivmail-Deploy-Scopes, eigener Verantwortungsbereich).
|
|
- Proxmox-Host-seitige ZFS-Snapshot-Konfiguration (separates Thema, anderer
|
|
Verantwortungsbereich/Zugriff).
|