#!/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