diff --git a/host_backup.sh b/host_backup.sh index 15d00c7..c5779ce 100644 --- a/host_backup.sh +++ b/host_backup.sh @@ -2,112 +2,136 @@ # ============================================================================== # Script: host_backup.sh -# Description: This script securely backs up important host files to a -# designated mounted drive using rsync. It includes pre-run checks -# to ensure the source files and destination are valid, and sends -# ntfy notifications for each task. -# Usage: sudo ./hass_backup.sh -# Notes: This script may require sudo/root privileges to access files. +# Description: Securely backs up key host system configuration files (fstab, +# ddclient, crontabs, systemd units, and installed package lists) +# into timestamped directories on a mounted backup drive. Includes +# 30-day retention pruning and ntfy alerts. +# Usage: sudo ./host_backup.sh +# Notes: Requires sudo/root privileges to access system configuration files. # ============================================================================== # ============================================================================== -# Define Variables for Source and Destination +# Define Variables for Source, Destination, and Retention # ============================================================================== -# This makes the script easier to read and modify. BACKUP_MOUNT_POINT="/mnt/5TB-Disk1" -BACKUP_DESTINATION="$BACKUP_MOUNT_POINT/backup" +HOST_BACKUP_BASE="$BACKUP_MOUNT_POINT/backup/server/host" +RETENTION_DAYS=30 -# Host backup directories +# Timestamp and Directory Setup +TIMESTAMP=$(date +"%Y-%m-%d_%H%M%S") +CURRENT_BACKUP_DIR="$HOST_BACKUP_BASE/backup_$TIMESTAMP" +LATEST_TXT_FILE="$HOST_BACKUP_BASE/latest.txt" + +# Host Backup Sources FSTAB_SOURCE="/etc/fstab" DDCLIENT_CONF_SOURCE="/etc/ddclient.conf" DDCLIENT_DEFAULT_SOURCE="/etc/default/ddclient" -FSTAB_DESTINATION="$BACKUP_MOUNT_POINT/backup/server/os" -DDCLIENT_DESTINATION="$BACKUP_MOUNT_POINT/backup/services/ddclient" +SYSTEMD_SOURCE="/etc/systemd/system" # ntfy Notification Configuration -# To use this feature, you must have the 'curl' command installed. -# NTFY_TOPIC is the topic you wish to send notifications to. -# NTFY_SERVER is the URL of your ntfy server. -# NTFY_TOKEN is the authorization token for your ntfy server. NTFY_TOPIC="Server" NTFY_SERVER="https://ntfy.speerfam.net" NTFY_TOKEN="tk_3xo9mejjtgemyowzckqw2souo3mux" # ============================================================================== -# Pre-flight Checks (for the destination drive) +# Pre-flight Checks # ============================================================================== # Check if the mount point directory exists and is a mounted filesystem. -# This is a critical check to ensure we are writing to the intended air-gapped -# drive and not to a local directory if the mount failed. if ! mountpoint -q "$BACKUP_MOUNT_POINT"; then echo "Error: '$BACKUP_MOUNT_POINT' is not a mount point. Please ensure the drive is mounted." >&2 - # Send a failure notification - curl -H "Authorization: Bearer $NTFY_TOKEN" -d "Error: Script failed. Destination '$BACKUP_MOUNT_POINT' is not mounted." "$NTFY_SERVER/$NTFY_TOPIC" > /dev/null 2>&1 + curl -H "Authorization: Bearer $NTFY_TOKEN" -d "Error: Host backup failed. Destination '$BACKUP_MOUNT_POINT' is not mounted." "$NTFY_SERVER/$NTFY_TOPIC" > /dev/null 2>&1 exit 1 fi -# Check if the destination directories exist and create them if they don't. -# This prevents rsync from failing on the first run. -# mkdir -p "$BACKUP_DESTINATION" -# mkdir -p "$PHOTOS_DESTINATION" +# Create target subdirectories for current timestamp run +mkdir -p "$CURRENT_BACKUP_DIR/os" +mkdir -p "$CURRENT_BACKUP_DIR/services/ddclient" +mkdir -p "$CURRENT_BACKUP_DIR/cron" +mkdir -p "$CURRENT_BACKUP_DIR/systemd" +mkdir -p "$CURRENT_BACKUP_DIR/packages" + +echo "Starting host configuration backup ($TIMESTAMP)..." # ============================================================================== -# Task 1: Sync /etc/fstab +# Task 1: Backup System Mounts (/etc/fstab) # ============================================================================== -echo "Starting rsync of '$FSTAB_SOURCE' to '$FSTAB_DESTINATION'..." - -# Check if the source file for fstab exists. -if [ ! -f "$FSTAB_SOURCE" ]; then - echo "Error: Source file '$FSTAB_SOURCE' not found." >&2 - # Send a failure notification - curl -H "Authorization: Bearer $NTFY_TOKEN" -d "Error: Script failed. Source file '$FSTAB_SOURCE' not found." "$NTFY_SERVER/$NTFY_TOPIC" > /dev/null 2>&1 - exit 1 -fi - -rsync -av --progress "$FSTAB_SOURCE" "$FSTAB_DESTINATION" - -# Post-rsync status check for fstab. -if [ $? -eq 0 ]; then - echo "Success: fstab has been successfully copied to '$FSTAB_DESTINATION'." - curl -H "Authorization: Bearer $NTFY_TOKEN" -d "Success: fstab copied to $FSTAB_DESTINATION" "$NTFY_SERVER/$NTFY_TOPIC" > /dev/null 2>&1 +if [ -f "$FSTAB_SOURCE" ]; then + rsync -a "$FSTAB_SOURCE" "$CURRENT_BACKUP_DIR/os/" + echo "Backed up: $FSTAB_SOURCE" else - echo "Error: rsync failed to copy fstab. Please check permissions." >&2 - curl -H "Authorization: Bearer $NTFY_TOKEN" -d "Error: rsync failed to copy fstab." "$NTFY_SERVER/$NTFY_TOPIC" > /dev/null 2>&1 - exit 1 + echo "Warning: Source file '$FSTAB_SOURCE' not found." >&2 fi # ============================================================================== -# Task 2: Sync ddclient configuration +# Task 2: Backup DDNS Configurations (ddclient) # ============================================================================== -echo "Starting rsync of ddclient configuration files to '$DDCLIENT_DESTINATION'..." - -# Check if ddclient.conf exists. -if [ ! -f "$DDCLIENT_CONF_SOURCE" ]; then - echo "Error: Source file '$DDCLIENT_CONF_SOURCE' not found." >&2 - curl -H "Authorization: Bearer $NTFY_TOKEN" -d "Error: Script failed. ddclient.conf source not found." "$NTFY_SERVER/$NTFY_TOPIC" > /dev/null 2>&1 +if [ -f "$DDCLIENT_CONF_SOURCE" ]; then + rsync -a "$DDCLIENT_CONF_SOURCE" "$CURRENT_BACKUP_DIR/services/ddclient/" + echo "Backed up: $DDCLIENT_CONF_SOURCE" else - rsync -av --progress "$DDCLIENT_CONF_SOURCE" "$DDCLIENT_DESTINATION" - if [ $? -eq 0 ]; then - echo "Success: ddclient.conf copied to '$DDCLIENT_DESTINATION'." - curl -H "Authorization: Bearer $NTFY_TOKEN" -d "Success: ddclient.conf copied to $DDCLIENT_DESTINATION" "$NTFY_SERVER/$NTFY_TOPIC" > /dev/null 2>&1 - else - echo "Error: rsync failed to copy ddclient.conf. Please check permissions." >&2 - curl -H "Authorization: Bearer $NTFY_TOKEN" -d "Error: rsync failed to copy ddclient.conf." "$NTFY_SERVER/$NTFY_TOPIC" > /dev/null 2>&1 - fi + echo "Warning: Source file '$DDCLIENT_CONF_SOURCE' not found." >&2 fi -# Check if ddclient default configuration exists. -if [ ! -f "$DDCLIENT_DEFAULT_SOURCE" ]; then - echo "Error: Source file '$DDCLIENT_DEFAULT_SOURCE' not found." >&2 - curl -H "Authorization: Bearer $NTFY_TOKEN" -d "Error: Script failed. ddclient default source not found." "$NTFY_SERVER/$NTFY_TOPIC" > /dev/null 2>&1 +if [ -f "$DDCLIENT_DEFAULT_SOURCE" ]; then + rsync -a "$DDCLIENT_DEFAULT_SOURCE" "$CURRENT_BACKUP_DIR/services/ddclient/" + echo "Backed up: $DDCLIENT_DEFAULT_SOURCE" else - rsync -av --progress "$DDCLIENT_DEFAULT_SOURCE" "$DDCLIENT_DESTINATION" - if [ $? -eq 0 ]; then - echo "Success: ddclient default configuration copied to '$DDCLIENT_DESTINATION'." - curl -H "Authorization: Bearer $NTFY_TOKEN" -d "Success: ddclient default copied to $DDCLIENT_DESTINATION" "$NTFY_SERVER/$NTFY_TOPIC" > /dev/null 2>&1 - else - echo "Error: rsync failed to copy ddclient default configuration. Please check permissions." >&2 - curl -H "Authorization: Bearer $NTFY_TOKEN" -d "Error: rsync failed to copy ddclient default." "$NTFY_SERVER/$NTFY_TOPIC" > /dev/null 2>&1 - fi + echo "Warning: Source file '$DDCLIENT_DEFAULT_SOURCE' not found." >&2 fi + +# ============================================================================== +# Task 3: Export Cron Configurations +# ============================================================================== +# Export active user/root crontab +if crontab -l > /dev/null 2>&1; then + crontab -l > "$CURRENT_BACKUP_DIR/cron/root_crontab.txt" + echo "Exported: Root crontab" +fi + +# Backup system-wide cron configuration files +if [ -f "/etc/crontab" ]; then + rsync -a /etc/crontab "$CURRENT_BACKUP_DIR/cron/crontab_system" +fi + +if [ -d "/etc/cron.d" ]; then + rsync -a /etc/cron.d "$CURRENT_BACKUP_DIR/cron/" + echo "Backed up: /etc/cron.d directory" +fi + +# ============================================================================== +# Task 4: Backup Custom Systemd Units & Services +# ============================================================================== +if [ -d "$SYSTEMD_SOURCE" ]; then + rsync -a "$SYSTEMD_SOURCE/" "$CURRENT_BACKUP_DIR/systemd/" + echo "Backed up: $SYSTEMD_SOURCE" +else + echo "Warning: Systemd directory '$SYSTEMD_SOURCE' not found." >&2 +fi + +# ============================================================================== +# Task 5: Export Installed System Package Recovery Lists +# ============================================================================== +if command -v dpkg > /dev/null 2>&1; then + dpkg --get-selections > "$CURRENT_BACKUP_DIR/packages/dpkg_selections.txt" + echo "Exported: dpkg package selections" +fi + +if command -v apt-mark > /dev/null 2>&1; then + apt-mark showmanual > "$CURRENT_BACKUP_DIR/packages/apt_manual_packages.txt" + echo "Exported: Manually installed apt package list" +fi + +# ============================================================================== +# Task 6: Record Latest Pointer & Prune Old Backups +# ============================================================================== +echo "$CURRENT_BACKUP_DIR" > "$LATEST_TXT_FILE" + +echo "Checking for backups older than $RETENTION_DAYS days..." +find "$HOST_BACKUP_BASE" -maxdepth 1 -type d -name "backup_*" -mtime +$RETENTION_DAYS -exec rm -rf {} + + +# ============================================================================== +# Completion and Notification +# ============================================================================== +echo "Success: Host backup completed at '$CURRENT_BACKUP_DIR'." +curl -H "Authorization: Bearer $NTFY_TOKEN" -d "Success: Host backup completed ($TIMESTAMP). Retaining $RETENTION_DAYS days." "$NTFY_SERVER/$NTFY_TOPIC" > /dev/null 2>&1 \ No newline at end of file