126 lines
3.4 KiB
Bash
Executable File
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
|