Files
homelab-infra/ops/maintenance/check-unraid-flash-backup.sh
T

126 lines
3.4 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# check-unraid-flash-backup.sh
#
# Read-only Validierung des Unraid-Flash-Backup-Artefakts
# (`unraid-flash-config.tar.gz`) ohne produktive Extraktion.
#
# Prueft:
# 1. Artefakt, Checksumme und Manifest sind vorhanden
# 2. Artefakt ist frisch genug (Standard: <= 36 h)
# 3. `sha256sum -c` ist OK
# 4. Archiv enthaelt die array-/identitaetsdefinierenden Kern-Configs
#
# Es wird NICHTS extrahiert. `tar -tzf` listet nur Eintragsnamen.
# Das Artefakt enthaelt Host-Konfiguration inkl. SSH-Host-Keys, passwd/shadow
# und Tailscale-State und ist wie Secret-Material zu behandeln. Dieses Skript
# gibt bewusst nur Datei-/Eintragsnamen aus, niemals Inhalte.
#
# Exit-Codes:
# 0 alles OK
# 1 Validierung fehlgeschlagen (fehlende Datei, Checksumme falsch,
# fehlende Kern-Config)
# 2 Artefakt aelter als erlaubt (Frische-Warnung)
DUMPS_DIR="${DUMPS_DIR:-/mnt/user/backups/borg/dumps/latest}"
ARTIFACT="${ARTIFACT:-unraid-flash-config.tar.gz}"
MAX_AGE_HOURS="${MAX_AGE_HOURS:-36}"
# Kern-Configs, die ein brauchbares Flash-Restore mindestens enthalten muss.
CRITICAL_FILES=(
"config/super.dat" # Array-/Disk-Zuordnung
"config/disk.cfg" # Array-Einstellungen
"config/ident.cfg" # Hostname/Identitaet
"config/share.cfg" # Share-Grundeinstellungen
"config/network.cfg" # Netzwerk
"config/docker.cfg" # Docker-Settings
"config/go" # Boot-Script
"config/domain.cfg" # VM/Domain-Settings
)
fail=0
artifact_path="$DUMPS_DIR/$ARTIFACT"
sha_path="$artifact_path.sha256"
manifest_path="$DUMPS_DIR/unraid-flash-config.manifest.txt"
echo "## Unraid Flash Backup Validierung"
echo "Verzeichnis: $DUMPS_DIR"
echo
# 1. Existenz
for f in "$artifact_path" "$sha_path" "$manifest_path"; do
if [ -f "$f" ]; then
echo "OK vorhanden: $(basename "$f")"
else
echo "FEHLER fehlt: $(basename "$f")"
fail=1
fi
done
echo
# Wenn das Artefakt fehlt, hat alles Weitere keinen Sinn.
if [ ! -f "$artifact_path" ]; then
echo "Abbruch: Artefakt nicht vorhanden."
exit 1
fi
# 2. Frische
now_epoch="$(date +%s)"
file_epoch="$(stat -c %Y "$artifact_path")"
age_hours=$(( (now_epoch - file_epoch) / 3600 ))
echo "Alter des Artefakts: ${age_hours} h (Grenze: ${MAX_AGE_HOURS} h)"
stale=0
if [ "$age_hours" -gt "$MAX_AGE_HOURS" ]; then
echo "WARNUNG Artefakt ist aelter als ${MAX_AGE_HOURS} h."
stale=1
else
echo "OK Artefakt ist frisch."
fi
echo
# 3. Checksumme
if [ -f "$sha_path" ]; then
if ( cd "$DUMPS_DIR" && sha256sum -c "$(basename "$sha_path")" ) ; then
echo "OK sha256 stimmt."
else
echo "FEHLER sha256-Pruefung fehlgeschlagen."
fail=1
fi
echo
fi
# 4. Kern-Configs (nur Namen, keine Extraktion)
echo "## Kern-Configs im Archiv"
listing="$(tar -tzf "$artifact_path")"
entry_count="$(printf '%s\n' "$listing" | wc -l | tr -d ' ')"
echo "Eintraege im Archiv: $entry_count"
for cf in "${CRITICAL_FILES[@]}"; do
if printf '%s\n' "$listing" | grep -qxF "$cf"; then
echo "OK $cf"
else
echo "FEHLER $cf fehlt im Archiv"
fail=1
fi
done
echo
# Manifest-Kopf zur Orientierung (enthaelt keine Secret-Werte)
if [ -f "$manifest_path" ]; then
echo "## Manifest"
cat "$manifest_path"
echo
fi
if [ "$fail" -ne 0 ]; then
echo "ERGEBNIS: FEHLGESCHLAGEN"
exit 1
fi
if [ "$stale" -ne 0 ]; then
echo "ERGEBNIS: OK, aber Frische-Warnung"
exit 2
fi
echo "ERGEBNIS: OK"
exit 0