Skip to content

Backup Common

Wspólny skrypt dla skryptów backup

Skrypt zawiera funkcje współdzielone przez pozostałe skrypty backup (logowanie, blokady, retencja). Jest potrzebny do ujednolicenia zachowania i ograniczenia powielania logiki.

Założenia konfiguracyjne:

  • Skrypty znajdują się w /srv/config/scripts/.
  • Logi skryptów trafiają na stdout/stderr
  • Pozostałe skrypty backup: backup-db-mariadb.sh, backup-db-postgres.sh, backup-restic.sh

Tworzenie skryptu

Tworzymy plik:

micro /srv/config/scripts/backup-common.sh

W pliku umieszczamy:

backup-db-common.sh
#!/usr/bin/env bash
# /srv/config/scripts/backup-db-common.sh

# 1. Strict Mode (Safety)
set -euo pipefail
IFS=$'\n\t'

# ANSI Colors
GREEN='\033[0;32m'
CYAN='\033[0;36m'
YELLOW='\033[1;33m'
RED='\033[31m'
NC='\033[0m' # No Color

# Globals
CURRENT_BACKUP_DIR=""
LOCK_BASE_DIR="/srv/backups/.locks"

# --- Logging Functions ---
log()      { echo -e "${GREEN}[backup]${NC} $1"; }
header()   { echo -e "\n${CYAN}=== $1 ===${NC}"; }
warn()     { echo -e "${YELLOW}Warning: $1${NC}"; }
error()    { echo -e "${RED}Error: $1${NC}" >&2; }
critical() { echo -e "${RED}CRITICAL ERROR: $1${NC}" >&2; exit 1; }

# --- Security Checks ---
check_root() {
    if [[ "${EUID:-$(id -u)}" -ne 0 ]]; then
        critical "Run as root (required for docker interaction)."
    fi
}

check_config_perms() {
    local config_file="$1"
    if [[ ! -f "$config_file" ]]; then
        critical "Config file missing: $config_file"
    fi

    # Check for symlinks (Safety)
    if [[ -L "$config_file" ]]; then
        critical "Config file is a symlink (unsafe): $config_file"
    fi

    # Strict check: owned by root:root and mode 600
    if [[ "$(stat -c '%U:%G %a' "$config_file")" != "root:root 600" ]]; then
        critical "Unsafe permissions for $config_file (expected root:root 600)"
    fi
}

# --- Improved Lock Mechanism (Dynamic FD + Central Dir) ---
acquire_lock() {
    local lock_name="$1"

    # Ensure lock directory exists
    if [[ ! -d "$LOCK_BASE_DIR" ]]; then
        mkdir -p "$LOCK_BASE_DIR" || critical "Cannot create lock dir: $LOCK_BASE_DIR"
    fi

    local lock_file="${LOCK_BASE_DIR}/${lock_name}.lock"

    # Use dynamic file descriptor allocation {var_name} (Bash 4.1+)
    # This prevents FD collisions (like the static 200 issue)
    exec {lock_fd}>"$lock_file"

    if ! flock -n "$lock_fd"; then
        error "Script is already running. Locked: $lock_file"
        exit 1
    fi
    # Note: lock_fd stays open until script exit
}

# --- Trap Handler ---
backup_exit_handler() {
    local rc=$?
    if [[ $rc -ne 0 ]] && [[ -n "$CURRENT_BACKUP_DIR" ]]; then
        : > "${CURRENT_BACKUP_DIR}/.FAILED"
        error "Backup failed (rc=$rc). Check logs."
    fi
}

# --- Environment Setup ---
setup_backup_env() {
    local out_dir="$1"
    CURRENT_BACKUP_DIR="$out_dir"
    mkdir -p "$out_dir"
    trap backup_exit_handler EXIT
}

# --- Safe Retention (Safety Stop) ---
perform_retention() {
    local target_dir="$1"
    local days="$2"

    header "Retention Policy"

    # Safety Check 1: Empty variable
    if [[ -z "$target_dir" ]]; then
        critical "Retention SAFETY STOP: Target directory variable is empty."
    fi

    # Safety Check 2: Path sanity (Must be under /srv/backups)
    if [[ "$target_dir" != /srv/backups/* ]]; then
        critical "Retention SAFETY STOP: Path '$target_dir' is outside /srv/backups. Aborting delete."
    fi

    # Safety Check 3: Directory exists
    if [[ ! -d "$target_dir" ]]; then
        warn "Retention: Directory $target_dir does not exist. Skipping."
        return
    fi

    log "Cleaning directories older than $days days in: $target_dir"

    # Execution
    find "$target_dir" -maxdepth 1 -mindepth 1 -type d \
        -name '20????????_????' \
        -mtime +"$days" \
        -print -exec rm -rf {} +
}

# --- Finalization ---
finish_backup() {
    local out_dir="$1"
    local fail_count="$2"

    echo "" 
    if [[ "$fail_count" -eq 0 ]]; then
        : > "$out_dir/.SUCCESS"
        log "Completed successfully."
        echo "FINAL_OUT_DIR=$out_dir"
    else
        critical "Backup finished with $fail_count errors."
    fi
}

Nadawanie uprawnień

Uprawnienia 0640 ograniczają zapis wyłącznie do konta root, a odczyt udostępniają tylko rootowi i uprzywilejowanej grupie (np. sudo)

sudo chown root:root /srv/config/scripts/backup-common.sh
sudo chmod 0640 /srv/config/scripts/backup-common.sh