feat: Add backup and security hardening

This commit is contained in:
2025-11-08 00:28:17 +01:00
parent 8de3f16ee6
commit 96f521a474
23 changed files with 5696 additions and 939 deletions

View File

@@ -0,0 +1,182 @@
#!/bin/sh
# Network-triggered backup daemon
# Monitors for NAS availability and triggers backups with safeguards
set -e
# Config
CONFIG_FILE="/etc/backup.conf"
LOG_DIR="${HOME}/.local/var/log"
LOG_FILE="${LOG_DIR}/backup-monitor.log"
STATE_DIR="${HOME}/.local/var/backup"
LAST_BACKUP_FILE="${STATE_DIR}/last-backup"
LAST_CHECK_FILE="${STATE_DIR}/last-check"
BACKUP_IN_PROGRESS_FILE="${STATE_DIR}/backup-in-progress"
# Ensure directories exist
mkdir -p "$LOG_DIR" "$STATE_DIR"
# Logging
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOG_FILE"
}
# Load config
if [ ! -f "$CONFIG_FILE" ]; then
log "ERROR: Configuration file not found: $CONFIG_FILE"
exit 1
fi
# shellcheck source=/dev/null
. "$CONFIG_FILE"
# Check if backup is already running
is_backup_running() {
# Check if any backup lock files exist
if ls /tmp/backup-*.lock >/dev/null 2>&1; then
return 0 # Backup is running
fi
# Check our own in-progress marker
if [ -f "$BACKUP_IN_PROGRESS_FILE" ]; then
# Check if marker is stale (older than 6 hours)
if [ -n "$(find "$BACKUP_IN_PROGRESS_FILE" -mmin +360 2>/dev/null)" ]; then
log "WARNING: Stale backup-in-progress marker found, removing"
rm -f "$BACKUP_IN_PROGRESS_FILE"
return 1 # Not running
fi
return 0 # Backup is running
fi
return 1 # Not running
}
# Check if cooldown period has passed
check_cooldown() {
if [ ! -f "$LAST_BACKUP_FILE" ]; then
return 0 # No previous backup, proceed
fi
LAST_BACKUP=$(cat "$LAST_BACKUP_FILE")
NOW=$(date +%s)
ELAPSED=$((NOW - LAST_BACKUP))
if [ "$ELAPSED" -lt "$BACKUP_COOLDOWN" ]; then
REMAINING=$((BACKUP_COOLDOWN - ELAPSED))
log "Cooldown active: ${REMAINING}s remaining until next backup allowed"
return 1 # Still in cooldown
fi
return 0 # Cooldown passed
}
# Check if we recently checked (prevent spam checks)
check_rate_limit() {
CHECK_INTERVAL=300 # 5 minutes between checks
if [ ! -f "$LAST_CHECK_FILE" ]; then
date +%s > "$LAST_CHECK_FILE"
return 0 # First check, proceed
fi
LAST_CHECK=$(cat "$LAST_CHECK_FILE")
NOW=$(date +%s)
ELAPSED=$((NOW - LAST_CHECK))
if [ "$ELAPSED" -lt "$CHECK_INTERVAL" ]; then
return 1 # Too soon since last check
fi
date +%s > "$LAST_CHECK_FILE"
return 0 # Can check now
}
# Check if NAS is available
check_nas_available() {
# Quick ping check
if ! ping -c 1 -W 2 "$NAS_HOST" >/dev/null 2>&1; then
return 1
fi
# SSH check
if ! ssh -p "$NAS_PORT" -o ConnectTimeout=5 -o BatchMode=yes \
"${NAS_USER}@${NAS_HOST}" "echo test" >/dev/null 2>&1; then
return 1
fi
return 0
}
# Trigger backup
trigger_backup() {
BACKUP_TYPE="${1:-incremental}"
log "NAS detected, triggering $BACKUP_TYPE backup..."
# Mark backup in progress
touch "$BACKUP_IN_PROGRESS_FILE"
# Run backup in background
(
if /usr/local/bin/backup-"$BACKUP_TYPE" >> "$LOG_FILE" 2>&1; then
log "Backup completed successfully"
else
log "ERROR: Backup failed"
fi
# Remove in-progress marker
rm -f "$BACKUP_IN_PROGRESS_FILE"
) &
# Don't wait for backup to complete
log "Backup started in background (PID: $!)"
}
# Main monitoring loop
main() {
log "=========================================="
log "Backup monitor started"
log "Monitoring for NAS: ${NAS_HOST}"
log "Check interval: 5 minutes"
log "Backup cooldown: ${BACKUP_COOLDOWN}s ($(( BACKUP_COOLDOWN / 3600 ))h)"
log "=========================================="
while true; do
# Rate limit checks (every 5 minutes)
if ! check_rate_limit; then
sleep 60
continue
fi
# Skip if backup already running
if is_backup_running; then
log "Backup already in progress, skipping check"
sleep 300 # Sleep 5 minutes
continue
fi
# Check if NAS is available
if check_nas_available; then
log "NAS is available at ${NAS_HOST}"
# Check cooldown before triggering
if check_cooldown; then
log "Cooldown passed, backup allowed"
trigger_backup "incremental"
else
log "Cooldown active, skipping backup"
fi
else
log "NAS not available (offline or not connected)"
fi
# Sleep before next check (5 minutes)
sleep 300
done
}
# Handle signals
trap 'log "Received shutdown signal, exiting..."; exit 0' TERM INT
# Start monitoring
main