ops: add restore freshness negative alert test

This commit is contained in:
2026-06-06 13:01:54 +02:00
parent 2628a0c795
commit c33e29016b
4 changed files with 81 additions and 2 deletions
+1 -1
View File
@@ -48,7 +48,7 @@ Bewusst nicht jetzt - mit Review-Trigger.
| Cold-Backup-Rotation | **Bewusst Hetzner-only** (2026-06-05). Keine zweite rotierende Cold-Kopie. Trigger: stark wachsender Datenwert, wiederholte Hetzner-Probleme, geaenderte Praeferenz | `docs/HARDWARE_INVENTORY.md` |
| WAN-Ausfallschutz | **Spaeter evaluieren** (2026-06-05). Mobilfunk-Failover inaktiv; lokale Apps laufen bei WAN-Ausfall weiter. Trigger: haeufigere/laengere DSL-Ausfaelle oder kritischer Remote-Zugang | `docs/NETWORK_INVENTORY.md` |
| Docker Critical Events Watcher | **Aktiviert 2026-06-05:** Unraid User Script `docker-critical-events-at-start` nutzt den Supervisor und steht in `schedule.json` auf `frequency: start`; Watcher manuell gestartet, Status `running`. Optionaler ntfy-Smoke wurde nachts bewusst nicht gesendet und kann spaeter mit `docker-critical-events-supervisor.sh smoke` nachgeholt werden | `docs/SERVICE_CATALOG.md`, `services/posture-check/docker-critical-events.sh`, `services/posture-check/unraid-user-scripts.md` |
| Negativ-Test Backup-Frische | Quartalsweise: bewusst kaputten/fehlenden Dump in Testpfad simulieren, pruefen ob `homelab-alerts` feuert | `docs/AUDIT_2026-05-25_TODO.md` |
| Negativ-Test Backup-Frische | **Automatisiert vorbereitet 2026-06-06:** `ops/restore-tests/negative-freshness-alert-test.sh` simuliert fehlende Dumps nur in einem synthetischen Restore-Lab-Pfad und sendet einen Test-Alert nach `homelab-alerts`. Quartalsweise wiederholen: `ops/restore-tests/run-restore-checks.sh freshness-negative` | `ops/restore-tests/README.md`, `docs/AUDIT_2026-05-25_TODO.md` |
| End-to-end-DR-Drill | Komplett-Bootstrap Phase 1-5 auf Wegwerf-Host; realistisch erst mit zweiter Hardware (siehe auch Extern blockiert) | `docs/AUDIT_2026-05-25_TODO.md`, `docs/DISASTER_RECOVERY.md` |
| Wiederkehrende Restore-Drills | Vaultwarden, Gitea, Authelia, Komodo, Paperless, Immich, Traefik, PostgreSQL, Mongo, Nextcloud, Mealie, Mail-Archiver nach Matrix-Intervallen rotieren | `docs/RESTORE_MATRIX.md`, `docs/RESTORE_HANDBOOK.md` |
| Dedizierter SMB-User `veeam-baerchen` | Optional spaeter, nur wenn Unraid-User-/Share-Rechte bewusst angefasst werden | `ops/windows-reinstall/docs/windows-image-backup-baseline.md` |
+2
View File
@@ -52,6 +52,7 @@ Ziel:
- `check-restore-freshness.ps1`: woechentlicher Frische-Check fuer Dumps und Reports
- `run-restore-checks.ps1`: einfacher Dispatcher fuer Restore-Jobs
- `check-restore-freshness.sh`: hosttauglicher Frische-Check
- `negative-freshness-alert-test.sh`: sicherer Negativtest fuer den Frische-Alarmweg; nutzt synthetische leere Testpfade unter `/mnt/user/backups/restore-lab/freshness-negative`, veraendert keine produktiven Dumps und sendet bei erkanntem Fehler einen Test-Alert nach `homelab-alerts`
- `run-restore-checks.sh`: hosttauglicher Dispatcher
- `common.sh`: gemeinsame Host-Helferfunktionen
- `automation-plan.md`: Host-Job- und Automatisierungsmodell
@@ -100,6 +101,7 @@ Aktuell ist das erste validierte Muster vorhanden.
- Bash-Dispatcher und Bash-Restore-Jobs am 2026-05-07 erfolgreich hostseitig verifiziert
- Restore-Lab und Report-Pfade auf dem Host angelegt
- `ntfy`-Wrapper ist fuer Host-Jobs verfuegbar
- Frische-Negativtest ist als sicherer Host-Job verfuegbar: `ops/restore-tests/run-restore-checks.sh freshness-negative`. Erwartetes Ergebnis: der synthetische leere Dump-Pfad erzeugt Criticals, ein Test-Alert erreicht `homelab-alerts`, produktive Dump-Pfade bleiben unangetastet.
- Nextcloud-Restore-Test: Scaffold existiert, aber **blockiert**. Nextcloud 33 fuehrt zur Laufzeit `chmod()` auf Dateien unter `/var/www/html` aus (`OC_Util.php:486`). Auf Unraids FUSE/shfs User-Shares ist `chmod` strukturell nicht moeglich, was zu permanenter 503 fuehrt. Loesungsoptionen: (a) Restore-Lab auf ein Cache-Drive statt User Share legen, (b) Docker-Volumes statt Bind-Mounts verwenden, (c) tmpfs-Mount fuer html/ + `rsync` der Borg-Daten hinein. Bis dahin ist Nextcloud als Backlog-Item dokumentiert.
- Komodo-Mongo-Daten-Restore am 2026-06-03 erfolgreich: 86904 Dokumente (inkl. 32 Stacks), Report `/mnt/user/backups/restore-reports/komodo-mongo-restore-2026-06-03.md`
- naechste grosse Kandidaten sind Mailarchiver und Mealie; Nextcloud bleibt blockiert (shfs-chmod)
+74
View File
@@ -0,0 +1,74 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
LAB_ROOT="${LAB_ROOT:-/mnt/user/backups/restore-lab/freshness-negative}"
REPORT_ROOT="${REPORT_ROOT:-/mnt/user/backups/restore-reports}"
ALERT_TOPIC="${ALERT_TOPIC:-homelab-alerts}"
INFO_TOPIC="${INFO_TOPIC:-homelab-info}"
SEND_NTFY="${SEND_NTFY:-1}"
stamp="$(date +%F-%H%M%S)"
test_root="$LAB_ROOT/$stamp"
dump_root="$test_root/dumps"
test_report_root="$test_root/reports"
report_file="$REPORT_ROOT/freshness-negative-$stamp.md"
raw_log="$test_root/check-output.md"
mkdir -p "$dump_root" "$test_report_root" "$REPORT_ROOT"
cleanup() {
rm -rf "$test_root"
}
trap cleanup EXIT
set +e
DUMP_ROOT="$dump_root" \
REPORT_ROOT="$test_report_root" \
MAX_DUMP_AGE_HOURS=26 \
MAX_REPORT_AGE_DAYS=45 \
"$SCRIPT_DIR/check-restore-freshness.sh" >"$raw_log" 2>&1
rc=$?
set -e
critical_count="$(awk -F': ' '/^Critical:/ {print $2; exit}' "$raw_log" | tr -d '[:space:]')"
critical_count="${critical_count:-0}"
{
echo "# Restore Freshness Negative Alert Test"
echo
echo "Timestamp: $(date '+%F %T')"
echo "Result: $([ "$rc" -ne 0 ] && [ "$critical_count" -gt 0 ] && echo "ok" || echo "failed")"
echo "Check exit code: $rc"
echo "Critical count: $critical_count"
echo "Synthetic dump root: $dump_root"
echo "Synthetic report root: $test_report_root"
echo "Production dump root touched: no"
echo
echo "## Check Output"
echo
cat "$raw_log"
} >"$report_file"
if [ "$rc" -ne 0 ] && [ "$critical_count" -gt 0 ]; then
if [ "$SEND_NTFY" = "1" ]; then
"$SCRIPT_DIR/send-ntfy.sh" \
"$ALERT_TOPIC" \
"TEST: Restore freshness alert path ok" \
"Negativtest erfolgreich: check-restore-freshness.sh meldete ${critical_count} Criticals gegen synthetischen leeren Testpfad. Produktive Dumps wurden nicht veraendert. Report: $report_file" \
high
fi
echo "Negative freshness alert test ok. Report: $report_file"
exit 0
fi
if [ "$SEND_NTFY" = "1" ]; then
"$SCRIPT_DIR/send-ntfy.sh" \
"$ALERT_TOPIC" \
"TEST FAILED: Restore freshness alert path" \
"Negativtest fehlgeschlagen: erwarteter Critical-Zustand wurde nicht erkannt. Report: $report_file" \
high || true
fi
echo "Negative freshness alert test failed. Report: $report_file" >&2
exit 1
+4 -1
View File
@@ -10,6 +10,9 @@ case "$MODE" in
freshness)
exec "$SCRIPT_DIR/check-restore-freshness.sh"
;;
freshness-negative)
exec "$SCRIPT_DIR/negative-freshness-alert-test.sh"
;;
vaultwarden)
if [ "$WHATIF" = "--what-if" ]; then
exec "$SCRIPT_DIR/vaultwarden-restore-test.sh" --what-if
@@ -95,7 +98,7 @@ case "$MODE" in
exec "$SCRIPT_DIR/shared-pg-cluster-restore-test.sh"
;;
*)
echo "Usage: $0 {freshness|vaultwarden|gitea|paperless|immich|authelia|adguard|redis|nextcloud|komodo-bootstrap|komodo-mongo-restore|traefik|mailarchiver|mealie|shared-pg-cluster} [--what-if]" >&2
echo "Usage: $0 {freshness|freshness-negative|vaultwarden|gitea|paperless|immich|authelia|adguard|redis|nextcloud|komodo-bootstrap|komodo-mongo-restore|traefik|mailarchiver|mealie|shared-pg-cluster} [--what-if]" >&2
exit 1
;;
esac