feat(PROJ-66): CLI-Kommandos archivmail backup/restore
Sichert Store (Hardlinks erhalten, PROJ-65-tauglich ohne Abhängigkeit von rsync -H), Keyfile, config.yml und PostgreSQL (pg_dump -Fc) konsistent in ein Zielverzeichnis. Rotation läuft nur nach erfolgreichem Lauf, ein fehlgeschlagener Backup rotiert nie ein gutes altes Backup weg. restore ist bewusst konservativ: bricht bei nicht-leerem Store/Keyfile ohne -force ab, stoppt/startet den Dienst nicht selbst, gibt am Ende die Pflicht-Verifikationsschritte (reconcile, reindex, Stichprobe) aus. Ergänzt die vorhandene PBS+Sync-Infrastruktur um einen App-eigenen, selektiven Restore-Weg. Kein automatischer Cron-Eintrag aktiv (Zielpfad noch offen), nur als Vorlage in deploy/cron.d/archivmail auskommentiert. Kein lokaler go build möglich, QA folgt auf Testserver.
This commit is contained in:
+1
-1
@@ -81,7 +81,7 @@
|
||||
| PROJ-63 | Defensive Tenant-Scope-Härtung der Tenant-Verwaltungs-Endpunkte (FUND-2) | Deployed | [PROJ-63](PROJ-63-harden-tenant-admin-scope.md) | 2026-06-25 |
|
||||
| PROJ-64 | Session-Invalidation bei Passwort-Change + Datei-Permissions-Härtung (Security-Audit) | Deployed | [PROJ-64](PROJ-64-session-invalidation-file-permissions.md) | 2026-07-03 |
|
||||
| PROJ-65 | Physische Tenant-Trennung im Storage-Layer | Deployed | [PROJ-65](PROJ-65-physische-tenant-trennung.md) | 2026-07-04 |
|
||||
| PROJ-66 | Backup-Strategie für Store, Keyfile, PostgreSQL (Produktiv + Teilproduktiv) | Planned | [PROJ-66](PROJ-66-backup-strategie.md) | 2026-07-04 |
|
||||
| PROJ-66 | Backup-Strategie für Store, Keyfile, PostgreSQL (Produktiv + Teilproduktiv) | In Review | [PROJ-66](PROJ-66-backup-strategie.md) | 2026-07-04 |
|
||||
|
||||
<!-- Add features above this line -->
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# PROJ-66: Backup-Strategie für archivmail (Produktiv + Teilproduktiv)
|
||||
|
||||
**Status:** Planned
|
||||
**Status:** In Review
|
||||
**Erstellt:** 2026-07-04
|
||||
|
||||
## Problem / Ausgangslage
|
||||
@@ -451,9 +451,63 @@ 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).
|
||||
|
||||
## Implementation Notes (2026-07-04) — App-eigenes `archivmail backup`/`restore`
|
||||
|
||||
Nutzer-Entscheidung: App-eigene Backup-CLI zusätzlich zur PBS-Sicherung
|
||||
bauen (Phase 2 vorgezogen), statt nur auf Infra-Ebene zu verlassen — gibt
|
||||
einen von PBS unabhängigen, selektiven Restore-Weg (einzelne Tabellen via
|
||||
`pg_restore`, ohne ganzen Container zurückspielen zu müssen).
|
||||
|
||||
### Neue Dateien
|
||||
- `cmd/archivmail/cmd_backup.go`: `archivmail backup -dest <dir> [-config ...] [-keep N]`.
|
||||
Schreibt in `<dest>/<timestamp>/`: `postgres.dump` (`pg_dump -Fc`, shell-out),
|
||||
`store/` (Hardlink-erhaltende Kopie, siehe unten), `keyfile`, `config.yml`,
|
||||
`audit.log` (best-effort). Rotation (`-keep`, Default 7) läuft NUR nach
|
||||
erfolgreichem Durchlauf — ein fehlgeschlagener Lauf lässt den partiellen
|
||||
Ordner stehen und rotiert nichts weg (Lehre aus PROJ-58: ein Job darf beim
|
||||
Scheitern nie gute alte Stände zerstören).
|
||||
- `cmd/archivmail/cmd_restore.go`: `archivmail restore -source <dir> [-force] [-skip-db]`.
|
||||
Bewusst konservativ: bricht ab, wenn `store_path`/Keyfile bereits Inhalt
|
||||
haben, außer `-force` ist gesetzt — ein versehentlicher Restore gegen ein
|
||||
laufendes System soll nicht kommentarlos Produktivdaten überschreiben.
|
||||
Stoppt/startet den Dienst NICHT selbst (Restore ist für einen frischen oder
|
||||
bewusst leergeräumten Zielserver gedacht, kein Live-Overlay). Gibt am Ende
|
||||
die Pflicht-Verifikationsschritte aus dem Runbook aus (`reconcile`,
|
||||
`reindex`, Stichproben-Entschlüsselung).
|
||||
|
||||
### Hardlink-Erhalt ohne externe Tools
|
||||
`copyTreePreservingHardlinks()` (in `cmd_backup.go`, von `cmd_restore.go`
|
||||
mitgenutzt) läuft den Store-Baum ab, merkt sich pro Datei die Inode-Nummer
|
||||
(`syscall.Stat_t.Ino`, Linux) und legt beim zweiten Auftreten derselben Inode
|
||||
einen Hardlink statt einer Kopie an. Das macht die Backup-CLI unabhängig von
|
||||
`rsync -H` (kein zusätzliches Tool-Dependency) und funktioniert identisch für
|
||||
Backup wie Restore — PROJ-65s Tenant-Hardlink-Struktur bleibt dadurch sowohl
|
||||
im Backup-Ziel als auch nach einem Restore verlustfrei erhalten (kein
|
||||
Speicherplatz-Mehrverbrauch).
|
||||
|
||||
### Bewusst nicht gebaut
|
||||
- Kein automatischer Cron-Eintrag aktiv — `deploy/cron.d/archivmail` enthält
|
||||
ihn nur auskommentiert als Vorlage, da `-dest` ein konkretes, vom Host
|
||||
getrenntes Ziel braucht, das noch nicht feststeht (siehe offene Entscheidung
|
||||
oben).
|
||||
- Kein automatisches Stop/Start der Dienste im Restore-Kommando (siehe oben).
|
||||
- Keine Backup-Verschlüsselung im Tool selbst (Abschnitt "Verschlüsselung des
|
||||
Backups selbst" bleibt gültig — Transport/Ziel-Absicherung ist
|
||||
Infrastruktur-Aufgabe, nicht Teil dieses CLI-Kommandos).
|
||||
- Kein Alerting bei Backup-Alter/-Ausfall im Code selbst (AC 7 bleibt offen,
|
||||
bräuchte einen zweiten Cron-Job/Health-Check, der die letzte
|
||||
Backup-Verzeichnis-Zeit prüft — noch nicht gebaut).
|
||||
|
||||
### Offen / Handoff
|
||||
- Kein lokaler `go build` möglich — QA auf Testserver 132 nötig, insbesondere:
|
||||
Backup+Restore-Roundtrip (Backup ziehen, auf leeren Store restoren,
|
||||
`reconcile`+Stichprobe), Hardlink-Erhalt verifizieren (Inode-Vergleich wie
|
||||
bei PROJ-65-QA), Verhalten bei vollem `-dest`-Ziel, `-force`-Schutz wirklich
|
||||
blockierend bei nicht-leerem Store.
|
||||
- `-dest`-Ziel für einen produktiven Cron-Eintrag muss noch vom Nutzer
|
||||
festgelegt werden, bevor die auskommentierte Cron-Zeile aktiviert wird.
|
||||
|
||||
Reference in New Issue
Block a user