Zentrales Bash Backup-Skript für mehrere OPNsense Firewalls

Backups 1. März 2026

Hier ist ein umfassendes Bash-Skript, das automatisierte Backups von mehreren OPNsense-Firewalls erstellt und verwaltet:


Hauptskript: opnsense-backup.sh

#!/bin/bash

################################################################################
# OPNsense Centralized Backup Script
# Erstellt automatisierte Backups von mehreren OPNsense Firewalls
# Anforderungen: curl, jq, ssh (optional für lokale Speicherung)
################################################################################

set -euo pipefail

# ============================================================================
# KONFIGURATION
# ============================================================================

# Backup-Verzeichnis
BACKUP_DIR="/var/backups/opnsense"
LOG_FILE="${BACKUP_DIR}/backup.log"
CONFIG_FILE="/etc/opnsense-backup/firewalls.conf"

# Aufbewahrungsdauer (Tage)
RETENTION_DAYS=30

# E-Mail-Benachrichtigungen (optional)
ENABLE_EMAIL=true
EMAIL_RECIPIENT="[email protected]"
EMAIL_FROM="[email protected]"

# Kompression
ENABLE_COMPRESSION=true
COMPRESSION_LEVEL=9

# Remote-Speicher (optional)
ENABLE_REMOTE_STORAGE=false
REMOTE_USER="backup"
REMOTE_HOST="backup.example.com"
REMOTE_PATH="/mnt/backups/opnsense"

# ============================================================================
# FUNKTIONEN
# ============================================================================

log() {
    local level="$1"
    shift
    local message="$@"
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    echo "[${timestamp}] [${level}] ${message}" | tee -a "${LOG_FILE}"
}

log_info() {
    log "INFO" "$@"
}

log_error() {
    log "ERROR" "$@"
}

log_success() {
    log "SUCCESS" "$@"
}

# Verzeichnisse initialisieren
init_directories() {
    mkdir -p "${BACKUP_DIR}"
    mkdir -p "${BACKUP_DIR}/logs"
    mkdir -p "${BACKUP_DIR}/configs"
    chmod 700 "${BACKUP_DIR}"
}

# Konfigurationsdatei validieren
validate_config() {
    if [[ ! -f "${CONFIG_FILE}" ]]; then
        log_error "Konfigurationsdatei nicht gefunden: ${CONFIG_FILE}"
        return 1
    fi
    
    log_info "Konfigurationsdatei validiert"
    return 0
}

# Backup von einer OPNsense-Firewall erstellen
backup_firewall() {
    local name="$1"
    local host="$2"
    local api_key="$3"
    local api_secret="$4"
    local backup_date=$(date '+%Y%m%d_%H%M%S')
    local backup_file="${BACKUP_DIR}/configs/${name}_${backup_date}.xml"
    local backup_file_compressed="${backup_file}.gz"
    
    log_info "Starte Backup für Firewall: ${name} (${host})"
    
    # API-Request zum Erstellen eines Backups
    local response=$(curl -s -X POST \
        -H "X-API-Key: ${api_key}" \
        -H "X-API-Secret: ${api_secret}" \
        "https://${host}/api/core/system/config/download" \
        --insecure 2>&1)
    
    if [[ $? -ne 0 ]]; then
        log_error "Fehler beim Backup von ${name}: API-Anfrage fehlgeschlagen"
        return 1
    fi
    
    # Backup speichern
    echo "${response}" > "${backup_file}"
    
    # Validierung: Prüfe ob XML-Datei gültig ist
    if ! grep -q "<?xml" "${backup_file}"; then
        log_error "Ungültige Backup-Datei für ${name}"
        rm -f "${backup_file}"
        return 1
    fi
    
    # Kompression (optional)
    if [[ "${ENABLE_COMPRESSION}" == "true" ]]; then
        gzip -${COMPRESSION_LEVEL} "${backup_file}"
        backup_file="${backup_file_compressed}"
        log_info "Backup komprimiert: $(ls -lh "${backup_file}" | awk '{print $5}')"
    fi
    
    # Dateigröße und Checksumme
    local file_size=$(stat -f%z "${backup_file}" 2>/dev/null || stat -c%s "${backup_file}")
    local checksum=$(sha256sum "${backup_file}" | awk '{print $1}')
    
    log_success "Backup erstellt für ${name}: ${backup_file} (${file_size} bytes)"
    
    # Metadaten speichern
    echo "${backup_date}|${name}|${host}|${file_size}|${checksum}" >> "${BACKUP_DIR}/backup.index"
    
    # Remote-Speicher (optional)
    if [[ "${ENABLE_REMOTE_STORAGE}" == "true" ]]; then
        backup_remote "${name}" "${backup_file}"
    fi
    
    return 0
}

# Backup zu Remote-Speicher übertragen
backup_remote() {
    local name="$1"
    local backup_file="$2"
    
    log_info "Übertrage Backup für ${name} zu Remote-Speicher"
    
    scp -q "${backup_file}" \
        "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PATH}/" 2>&1
    
    if [[ $? -eq 0 ]]; then
        log_success "Remote-Backup erfolgreich für ${name}"
    else
        log_error "Remote-Backup fehlgeschlagen für ${name}"
        return 1
    fi
}

# Alte Backups löschen (Retention Policy)
cleanup_old_backups() {
    log_info "Bereinige alte Backups (älter als ${RETENTION_DAYS} Tage)"
    
    find "${BACKUP_DIR}/configs" -name "*.xml*" -type f -mtime +${RETENTION_DAYS} -delete
    
    log_success "Bereinigung abgeschlossen"
}

# E-Mail-Benachrichtigung senden
send_email_notification() {
    local subject="$1"
    local body="$2"
    
    if [[ "${ENABLE_EMAIL}" != "true" ]]; then
        return 0
    fi
    
    if ! command -v mail &> /dev/null; then
        log_error "mail-Programm nicht installiert"
        return 1
    fi
    
    echo -e "${body}" | mail -s "${subject}" \
        -r "${EMAIL_FROM}" "${EMAIL_RECIPIENT}"
    
    log_info "E-Mail-Benachrichtigung gesendet"
}

# Backup-Report generieren
generate_report() {
    local report_file="${BACKUP_DIR}/logs/report_$(date '+%Y%m%d_%H%M%S').txt"
    
    {
        echo "======================================"
        echo "OPNsense Backup Report"
        echo "Datum: $(date)"
        echo "======================================"
        echo ""
        echo "Backup-Verzeichnis: ${BACKUP_DIR}"
        echo ""
        echo "--- Backup-Übersicht ---"
        ls -lh "${BACKUP_DIR}/configs" | tail -n +2 || echo "Keine Backups gefunden"
        echo ""
        echo "--- Disk-Nutzung ---"
        du -sh "${BACKUP_DIR}"
        echo ""
        echo "--- Letzte 10 Log-Einträge ---"
        tail -n 10 "${LOG_FILE}"
    } > "${report_file}"
    
    log_info "Report generiert: ${report_file}"
    
    # Optional: Report per E-Mail versenden
    if [[ "${ENABLE_EMAIL}" == "true" ]]; then
        send_email_notification \
            "OPNsense Backup Report - $(date '+%Y-%m-%d')" \
            "$(cat "${report_file}")"
    fi
}

# Hauptfunktion
main() {
    log_info "=========================================="
    log_info "OPNsense Backup-Prozess gestartet"
    log_info "=========================================="
    
    init_directories
    validate_config || exit 1
    
    # Backup-Index initialisieren
    touch "${BACKUP_DIR}/backup.index"
    
    local backup_count=0
    local error_count=0
    
    # Jede Firewall aus der Konfiguration sichern
    while IFS='|' read -r name host api_key api_secret; do
        # Kommentare und leere Zeilen ignorieren
        [[ "$name" =~ ^#.*$ ]] && continue
        [[ -z "$name" ]] && continue
        
        if backup_firewall "$name" "$host" "$api_key" "$api_secret"; then
            ((backup_count++))
        else
            ((error_count++))
        fi
    done < "${CONFIG_FILE}"
    
    # Aufräumen
    cleanup_old_backups
    
    # Report generieren
    generate_report
    
    # Zusammenfassung
    log_info "=========================================="
    log_success "Backup-Prozess abgeschlossen"
    log_info "Erfolgreiche Backups: ${backup_count}"
    log_info "Fehler: ${error_count}"
    log_info "=========================================="
    
    # Exit-Code basierend auf Fehlern
    [[ ${error_count} -eq 0 ]] && exit 0 || exit 1
}

# ============================================================================
# SCRIPT-AUSFÜHRUNG
# ============================================================================

main "$@"

Konfigurationsdatei: /etc/opnsense-backup/firewalls.conf

# OPNsense Firewall Backup-Konfiguration
# Format: name|host|api_key|api_secret
# Beispiel:

fw-prod|192.168.1.1|your-api-key-here|your-api-secret-here
fw-backup|192.168.1.2|your-api-key-here|your-api-secret-here
fw-branch|10.0.0.1|your-api-key-here|your-api-secret-here

Cron-Job Setup

# /etc/cron.d/opnsense-backup
# Täglich um 2:00 Uhr morgens ausführen

0 2 * * * root /usr/local/bin/opnsense-backup.sh >> /var/log/opnsense-backup.log 2>&1

# Wöchentliche Verifizierung (Sonntag um 3:00 Uhr)
0 3 * * 0 root /usr/local/bin/opnsense-backup-verify.sh >> /var/log/opnsense-backup-verify.log 2>&1

Verifikations-Skript: opnsense-backup-verify.sh

#!/bin/bash

# Verifiziert die Integrität von Backups

BACKUP_DIR="/var/backups/opnsense"
LOG_FILE="${BACKUP_DIR}/logs/verify.log"

verify_backups() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starte Backup-Verifikation" | tee -a "${LOG_FILE}"
    
    local verified=0
    local failed=0
    
    while IFS='|' read -r backup_date name host file_size checksum; do
        local backup_file=$(find "${BACKUP_DIR}/configs" -name "${name}_${backup_date}*" | head -1)
        
        if [[ -f "${backup_file}" ]]; then
            local actual_checksum=$(sha256sum "${backup_file}" | awk '{print $1}')
            
            if [[ "${actual_checksum}" == "${checksum}" ]]; then
                echo "[OK] ${name} - Checksumme stimmt überein" | tee -a "${LOG_FILE}"
                ((verified++))
            else
                echo "[FEHLER] ${name} - Checksumme stimmt nicht überein" | tee -a "${LOG_FILE}"
                ((failed++))
            fi
        fi
    done < "${BACKUP_DIR}/backup.index"
    
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] Verifikation abgeschlossen - OK: ${verified}, Fehler: ${failed}" | tee -a "${LOG_FILE}"
}

verify_backups

Installation und Setup

# 1. Skripte installieren
sudo install -m 755 opnsense-backup.sh /usr/local/bin/
sudo install -m 755 opnsense-backup-verify.sh /usr/local/bin/

# 2. Konfiguration erstellen
sudo mkdir -p /etc/opnsense-backup
sudo install -m 600 firewalls.conf /etc/opnsense-backup/

# 3. Cron-Job aktivieren
sudo install -m 644 opnsense-backup.cron /etc/cron.d/opnsense-backup

# 4. Backups manuell testen
sudo /usr/local/bin/opnsense-backup.sh

Wichtige Hinweise

API-Credentials: Die API-Keys und Secrets müssen in OPNsense unter System → API erstellt werden. Der Benutzer benötigt Berechtigungen für core:system:config:download.

Sicherheit: Die Konfigurationsdatei sollte Permissions 600 haben und nur vom root-Benutzer lesbar sein.

HTTPS-Validierung: Das Skript nutzt --insecure für selbstsignierte Zertifikate. Für Produktionsumgebungen sollte dies durch ordnungsgemäße Zertifikat-Validierung ersetzt werden.

Remote-Speicher: SSH-Keys sollten für automatisierte Übertragungen konfiguriert sein.

Tags