docs(PROJ-66): Backup-Strategie-Spec anlegen; PROJ-65 Deployed-Status nachziehen
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.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
---
|
||||
id: PROJ-65
|
||||
title: Physische Tenant-Trennung im Storage-Layer
|
||||
status: In Review
|
||||
status: Deployed
|
||||
created: 2026-07-04
|
||||
---
|
||||
|
||||
@@ -247,8 +247,113 @@ PROJ-55/56.
|
||||
- `cmd/archivmail/cmd_status.go` (`checkStoragePermissions`)
|
||||
- `docs/GOBD_DSGVO_CHECKLIST.md` (Punkt 15, siehe separater Commit)
|
||||
|
||||
## QA Test Results
|
||||
_To be added by /qa_
|
||||
## QA Test Results (2026-07-04, Testserver 192.168.1.132)
|
||||
|
||||
**Build:** `CGO_ENABLED=0 go build ./cmd/archivmail/` auf 192.168.1.132 erfolgreich
|
||||
(go1.24.4), 0 Fehler/Warnungen. Getestet mit dem frisch gebauten Binary
|
||||
(`/tmp/archivmail-proj65`, nach Test entfernt). Hinweis: der laufende Dienst
|
||||
auf 132 ist noch das alte 0.9.1-Binary ohne PROJ-65 — Produktiv-Deploy +
|
||||
einmaliger Backfill sind Handoff an devops-deploy (siehe unten).
|
||||
|
||||
**Testdaten-Hygiene:** Backfill wurde als QA-Test auf dem echten Test-Store
|
||||
gefahren (51304 Mails), danach die erzeugten `store/tenant_<id>/`-Verzeichnisse
|
||||
und das Test-Binary wieder entfernt, da der laufende Dienst noch das alte Binary
|
||||
ist (sonst inkonsistenter Zustand: Backfill-Links vorhanden, aber neue Mails ohne
|
||||
Link). Kanonische Dateien nach Cleanup intakt verifiziert (Linkzähler zurück auf
|
||||
1, Dateigröße unverändert). Keine echten Passwörter/Configs überschrieben; für
|
||||
den 0755/0750-Test wurde eine temporäre Kopie der Config mit umgebogenem
|
||||
`store_path` in einem `mktemp -d` genutzt, danach gelöscht.
|
||||
|
||||
### Acceptance Criteria — Option B
|
||||
|
||||
| # | Kriterium | Ergebnis |
|
||||
|---|-----------|----------|
|
||||
| B1 | store/ gehört archivmail-User, Modus 0700 | **PASS** — `/var/archivmail/store` = `700 archivmail:archivmail` |
|
||||
| B2 | `archivmail status` prüft Storage-Permissions, warnt bei zu offenen Rechten | **PASS** — neuer Check `Storage-Rechte` erscheint in Text- und JSON-Ausgabe; 0700 → OK, 0755 + 0750 → WARN (nie Hard-Fail, Exit 0), 0700 → OK. Alle drei chmod-Fälle live verifiziert. |
|
||||
| B3 | GoBD-Checkliste Punkt 15 umformuliert | **PASS** — Punkt 15 nennt PROJ-65, Hardlink-Dirs, Backfill, 0700-Überwachung, Defense-in-Depth-Einordnung |
|
||||
| B4 | Messung Cross-Tenant-Dedup-Quote | **PASS/erledigt** — 8 von 51304 Mails mit >1 Tenant-Zuordnung (~0,016 %), bestätigt die Spec-Annahme "<1%" |
|
||||
|
||||
### Acceptance Criteria — Option A
|
||||
|
||||
| # | Kriterium | Ergebnis |
|
||||
|---|-----------|----------|
|
||||
| A1 | Hardlink-Erstellung bei Save (alle Dedup-Zweige) | **PASS (via Backfill verifiziert)** — Backfill legte 50015 Links an (== `COUNT(*) email_refs`), 0 Fehler. Save-Wiring code-reviewed (drei email_refs-INSERT-Stellen). Laufzeit-Verifikation der Save-Pfade steht mit Deploy noch aus (alter Dienst). |
|
||||
| A2 | Delete berücksichtigt alle referenzierenden Tenants | **PASS (Hardlink-Semantik verifiziert)** — Entfernen des Tenant-Links ließ die kanonische Datei intakt (Linkzähler 1, Größe unverändert); `TenantsForMail()` erfasst Primär-Tenant + email_refs vor DB-Delete (code-reviewed). Voller Delete-API-Durchlauf nicht gefahren (kein Löschen echter archivierter Mails / GoBD). |
|
||||
| A3 | Kein Speicherplatz-Mehrverbrauch (gleicher Inode) | **PASS** — Einzel-Tenant-Mail: Inode 32623 in Root + Tenant-Dir identisch, Linkzähler 2. Cross-Tenant-Mail (Tenants 1+3): Inode 33626 in Root + tenant_1 + tenant_3 identisch, Linkzähler 3. |
|
||||
| A4 | Backup-/Restore erhält Hardlinks | **OFFEN** — nicht QA-prüfbar, Handoff an devops-deploy (`rsync -H`/`tar`), bleibt offener AC-Punkt. |
|
||||
| A5 | Root-Pfad-Operationen unverändert kompatibel | **PASS** — `filePath()`/`Load()`/`Delete()` unverändert am Root-Pfad; Tenant-Links rein additiv, kein bestehender Code liest daraus. |
|
||||
| A6 | Backfill-Subcommand idempotent | **PASS** — `migrate-tenant-dirs` in main.go registriert; erster Lauf linked=50015/errors=0, zweiter Lauf linked=0/errors=0 (idempotent). |
|
||||
|
||||
### Tenant-Isolation / Sicherheit
|
||||
- Tenant-Verzeichnisse werden mit **0700** angelegt (`MkdirAll(..., 0o700)`) —
|
||||
live verifiziert (`drwx------ archivmail:archivmail` für tenant_1/2/3). Kein
|
||||
Gruppen-/World-Zugriff, konsistent mit dem Defense-in-Depth-Ziel.
|
||||
- Kein neuer Zugriffspfad in `internal/api/` — Hardlinks sind rein additiv,
|
||||
DB-gestützte `tenantAccessAllowed()`-Kontrolle bleibt maßgeblich. Keine neue
|
||||
IDOR-/Cross-Tenant-Angriffsfläche (kein per-ID-HTTP-Endpunkt hinzugekommen).
|
||||
- `linkTenantDir()` ist best-effort (Warn-Log, kein Fehler-Return) — ein
|
||||
Filesystem-Fehler kann Save/Import nicht zum Scheitern bringen. Korrekt für
|
||||
eine additive Defense-in-Depth-Ebene.
|
||||
|
||||
### Regression
|
||||
Bestehende `archivmail status`-Checks (PostgreSQL, Manticore, Storage,
|
||||
Encryption, Audit-Log, Retention) weiterhin unverändert funktional; der neue
|
||||
`Storage-Rechte`-Check ist additiv und beeinflusst den Exit-Code nicht.
|
||||
|
||||
### Bugs
|
||||
Keine. Keine Blocker, keine Regressionen gefunden.
|
||||
|
||||
### Handoff / offene Punkte (nicht QA-Blocker)
|
||||
- **devops-deploy:** (1) Produktiv-Deploy des PROJ-65-Binaries, (2) danach
|
||||
einmalig `archivmail migrate-tenant-dirs` ausführen, (3) Backup-Tool auf
|
||||
Hardlink-Erhalt prüfen (AC A4, `rsync -H`/`tar`).
|
||||
- Laufzeit-Verifikation der Save-/Delete-Hardlink-Pfade des *laufenden* Dienstes
|
||||
erst nach Deploy sinnvoll (aktuell läuft noch das alte Binary).
|
||||
|
||||
**Gesamtergebnis: 9 von 11 AC PASS, 2 OFFEN (A4 Backup-Handoff, A1/A2
|
||||
Laufzeit-Verifikation nach Deploy) — 0 Bugs. Empfehlung: freigeben für Deploy.**
|
||||
|
||||
## Deployment
|
||||
_To be added by /deploy_
|
||||
|
||||
**2026-07-04, devops-deploy, Produktivserver 192.168.1.131**
|
||||
|
||||
- Commit `a15fa37` gepusht nach `origin/main` (war lokaler HEAD, noch nicht auf
|
||||
Remote).
|
||||
- `bash /opt/archivmail/update.sh` auf 131 ausgeführt: Quellcode aktualisiert,
|
||||
Backend gebaut, Frontend gebaut, Cron-Jobs eingespielt, systemd-Units
|
||||
synchronisiert. Ergebnis: `Backend ✓ läuft`, `Frontend ✓ läuft`.
|
||||
- **Backfill** `archivmail migrate-tenant-dirs --config /etc/archivmail/config.yml`
|
||||
ausgeführt: `linked=0, errors=0`. Kein Bug — auf 131 haben alle 151 Bestandsmails
|
||||
`tenant_id = NULL` und `email_refs` ist leer (0 Zeilen); Produktiv läuft aktuell
|
||||
ohne aktive Mandanten-Zuordnung, anders als der Testserver 132 (dort 51304 Mails
|
||||
mit Tenant-Zuordnung, dort lief der Backfill im QA-Test mit `linked=50015`).
|
||||
Sobald auf 131 Mails einem Tenant zugeordnet werden (`emails.tenant_id` oder
|
||||
`email_refs`), legt `Save()` die Hardlinks automatisch an; ein erneuter
|
||||
`migrate-tenant-dirs`-Lauf ist jederzeit gefahrlos wiederholbar.
|
||||
- **Smoke-Test:**
|
||||
- `archivmail status` zeigt neuen Check `[OK] Storage-Rechte /var/archivmail/store
|
||||
Modus 0700 — nur Owner-Zugriff`.
|
||||
- Backend-Health `GET /api/health` → 200, Frontend `GET /` → 200, beide
|
||||
systemd-Units `active`.
|
||||
- Hardlink-Stichprobe (Inode-Vergleich bestehende Mail Root- vs. Tenant-Pfad)
|
||||
**nicht durchführbar auf 131**, da mangels Tenant-Zuordnung keine
|
||||
`store/tenant_<id>/`-Verzeichnisse angelegt wurden (erwartetes Verhalten,
|
||||
kein Fehlschlag). Die Hardlink-Semantik selbst wurde bereits im QA-Lauf auf
|
||||
132 mit echten Tenant-Daten verifiziert (Inode-Gleichheit Root/Tenant-Pfad,
|
||||
Linkzähler korrekt), siehe QA Test Results oben.
|
||||
- **Backup-Prozess-Check (AC A4):** Auf 131 existiert **kein** dediziertes
|
||||
Backup-Tool/-Skript für `/var/archivmail/store` — weder in `/etc/cron.d/`
|
||||
(nur `archivmail`-Cron mit OCR-Pause, Purge, Reindex-Backlog, Reconciliation,
|
||||
keine Backup-Zeile), noch als systemd-Timer, noch als eigenständiges Skript
|
||||
unter `/opt/archivmail` oder `/usr/local/bin`. Die Hardlink-Erhalt-Frage
|
||||
(`rsync -H` vs. `tar` vs. naives `cp -r`) ist damit aktuell **gegenstandslos,
|
||||
weil es noch keinen Store-Backup-Prozess gibt** — das ist ein eigenständiger,
|
||||
vom PROJ-65-Deploy unabhängiger offener Punkt und bleibt hier als Hinweis
|
||||
stehen, nicht selbst behoben (kein Backup-Skript angelegt).
|
||||
- **Restrisiko/offene Punkte für spätere Sprints:**
|
||||
1. Store-Backup-Prozess für 131 fehlt komplett (nicht PROJ-65-Scope, aber im
|
||||
Zuge dieses Deploys aufgefallen — sollte als eigenes Ticket nachgezogen
|
||||
werden, inkl. Hardlink-Erhalt-Vorgabe für PROJ-65).
|
||||
2. Save-/Delete-Hardlink-Pfade des jetzt laufenden Produktiv-Binaries sind auf
|
||||
131 mangels Tenant-Zuordnung noch nicht mit echten Live-Daten beobachtet
|
||||
worden (nur code-reviewed + auf 132 mit echten Daten verifiziert).
|
||||
|
||||
Reference in New Issue
Block a user