Skip to content

Restore Repo

Odtwarzanie serwera z repozytorium

Skrypt realizuje

Odtworzenie konfiguracji Docker Compose

  • nadpisuje pliki konfiguracyjne (.yml) z repozytorium,
  • nie usuwa ani nie nadpisuje lokalnych plików .env,
  • nie ingeruje w wolumeny danych, logi ani dane runtime.

Odtworzenie i nadpisanie konfiguracji systemowej

  • wybrane pliki w /etc (m.in. ssh, fail2ban, ufw, docker),
  • konfigurację systemd (journald.conf),
  • jednostki systemd (*.service, *.timer).

Przywracanie plików z repozytorium do systemu

  • repozytorium jest traktowane jako źródło prawdy,
  • pliki obecne w repo nadpisują pliki systemowe.

Nadawanie właściwych uprawnień

  • konfiguracje systemowe: root:root + odpowiednie prawa (644, 640),
  • spójne i deterministyczne prawa po restore.

Plik konfiguracyjny

Do działania skryptu oddtwarzania niezbędny jest plik konfiguracyjny sync.env opisany w dokumentacji skryptu synchronizacji.

Plik Konfiguracji

Tworzymy plik konfiguracyjny dla skryptu synchronizacji:

Skrypt odtwarzania serwra z repozytorium

Tworzymy jeden zbiorczy skrypt odpowiedzialny za odtwarzanie:

sudo micro /srv/config/scripts/restore-repo.sh

Treść pliku:

#!/usr/bin/env bash
set -euo pipefail

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

log()      { echo -e "${GREEN}[restore]${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; }

umask 022

# Ensure root execution
if [[ "${EUID:-$(id -u)}" -ne 0 ]]; then
    critical "Run as root (use sudo)."
    exit 1
fi

# ============================================================
# LOCK MECHANISM
# ============================================================
LOCK_FILE="/tmp/restore-repo.lock"
exec 200>"$LOCK_FILE"
flock -n 200 || { error "Script is already running."; exit 1; }

# ============================================================
# CONFIGURATION & VALIDATION
# ============================================================

CONFIG_FILE="/srv/config/git/sync.env"

if [[ ! -f "$CONFIG_FILE" ]]; then
    critical "Config file missing at $CONFIG_FILE"
    exit 1
fi

# Verify config file owner and permissions before loading
if [[ "$(stat -c '%U:%G %a' "$CONFIG_FILE")" != "root:root 600" ]]; then
    critical "Unsafe perms/owner for $CONFIG_FILE (expected root:root 600)"
    exit 1
fi

source "$CONFIG_FILE"

if [[ -z "${REPO_ROOT:-}" ]] || [[ -z "${REPO_USER:-}" ]] || [[ -z "${REPO_GROUP:-}" ]]; then
    critical "REPO_ROOT, REPO_USER or REPO_GROUP variables are not set in sync.env"
    exit 1
fi

if [[ ! -d "$REPO_ROOT" ]]; then
    critical "Repository directory $REPO_ROOT does not exist."
    exit 1
fi

# ============================================================
# SAFETY PROMPT (always manual)
# ============================================================
confirm_execution() {
    echo -e "${RED}WARNING: You are about to OVERWRITE system files from the repository.${NC}"
    echo -e "Source: $REPO_ROOT"
    read -p "Are you sure you want to proceed? (y/N): " -n 1 -r
    echo ""
    if [[ ! $REPLY =~ ^[Yy]$ ]]; then
        echo "Aborted by user."
        exit 1
    fi
}

# ============================================================
# HELPER FUNCTIONS
# ============================================================

restore_file() {
    local repo_path="$REPO_ROOT/$1"
    local sys_path="$2"
    local owner="$3"
    local perms="$4"

    if [[ -f "$repo_path" ]]; then
        mkdir -p "$(dirname "$sys_path")"
        cp -a -- "$repo_path" "$sys_path"
        chown "$owner" "$sys_path"
        chmod "$perms" "$sys_path"

        echo "  + Restored: $sys_path ($owner, $perms)"
    else
        echo -e "  ${YELLOW}! Skipped (missing in repo): $1${NC}"
    fi
}

# ============================================================
# WRAPPERS (mirror sync structure)
# ============================================================

etc_restore() {
    local rel_dir="$1"
    shift

    local repo_base="host/etc"
    local sys_base="/etc"

    if [[ "$rel_dir" != "." ]]; then
        repo_base="host/etc/$rel_dir"
        sys_base="/etc/$rel_dir"
    fi

    for f in "$@"; do
        restore_file "$repo_base/$f" "$sys_base/$f" "root:root" "644"
    done
}

systemd_restore() {
    local rel_dir="$1"
    shift

    local repo_base="host/etc/systemd"
    local sys_base="/etc/systemd"

    if [[ "$rel_dir" != "." ]]; then
        repo_base="host/etc/systemd/$rel_dir"
        sys_base="/etc/systemd/$rel_dir"
    fi

    for f in "$@"; do
        restore_file "$repo_base/$f" "$sys_base/$f" "root:root" "644"
    done
}

units_restore() {
    local rel_dir="$1"
    shift

    local repo_base="host/etc/systemd/system"
    local sys_base="/etc/systemd/system"

    if [[ "$rel_dir" != "." ]]; then
        repo_base="host/etc/systemd/system/$rel_dir"
        sys_base="/etc/systemd/system/$rel_dir"
    fi

    for f in "$@"; do
        restore_file "$repo_base/$f" "$sys_base/$f" "root:root" "644"
    done
}

# ============================================================
# RESTORE MODULES
# ============================================================

restore_compose() {
    header "Restore: Docker Compose"

    local src="$REPO_ROOT/compose/"
    local dst="${SOURCE_COMPOSE:-}"

    if [[ ! -d "$src" ]]; then
        warn "Repo compose directory missing. Skipping."
        return
    fi

    if [[ -z "$dst" ]] || [[ ! -d "$dst" ]]; then
        warn "SOURCE_COMPOSE not found or unset. Skipping."
        return
    fi

    rsync -av \
      --exclude ".git/" \
      "$src" "$dst/"

    chown -R "$REPO_USER:$REPO_GROUP" "$dst"

    log "Compose files updated."
}

restore_etc() {
    header "Restore: Host /etc"

    etc_restore "ssh" sshd_config ssh_config
    etc_restore "fail2ban" jail.local
    etc_restore "ufw" ufw.conf user.rules
    etc_restore "docker" daemon.json
}

restore_systemd() {
    header "Restore: Host /etc/systemd"

    systemd_restore "." journald.conf
}

restore_units() {
    header "Restore: Host /etc/systemd/system"

    units_restore "." restic-backup.service restic-backup.timer
}

restore_host() {
    header "Restore: Host (etc + systemd + units)"
    restore_etc
    restore_systemd
    restore_units
}

# ============================================================
# EXECUTION
# ============================================================

TARGET=${1:-host}

confirm_execution

case "$TARGET" in
    compose)  restore_compose ;;
    etc)      restore_etc ;;
    systemd)  restore_systemd ;;
    units)    restore_units ;;
    host)     restore_host ;;
    all)      restore_compose; restore_host ;;
    *)        error "Usage: $0 {compose|etc|systemd|units|host|all}"; exit 1 ;;
esac

echo ""
log "Completed successfully."

Po zapisaniu nadajemy prawa do uruchamiania:

sudo chmod +x /srv/config/scripts/restore-repo.sh

Uruchamianie odtwarzania

Oddtwarzanie wykonujemy z katalogu skryptów:

cd /srv/config/scripts
sudo ./restore-repo.sh all

Tryby przywracania

Skrypt restore-repo.sh obsługuje następujące tryby pracy:

Tryb compose

Odtwarza wyłącznie definicje Docker Compose z repozytorium (do katalogu roboczego Dockera).

  • nadpisywane są pliki konfiguracyjne stacków (.yml),
  • lokalne pliki .env oraz dane runtime nie są usuwane.
sudo ./restore-repo.sh compose

Tryb etc

Odtwarza wybrane pliki konfiguracyjne systemu z katalogu /etc (np. ssh, fail2ban, ufw, docker).

sudo ./restore-repo.sh etc

Tryb systemd

Odtwarza konfigurację systemd (np. journald.conf).

sudo ./restore-repo.sh systemd

Tryb units

Odtwarza jednostki systemd (np. pliki *.service, *.timer).

sudo ./restore-repo.sh units

Tryb host

Odtwarza pełną konfigurację hosta:

  • /etc
  • /etc/systemd
  • /etc/systemd/system

Jest to tryb domyślny.

sudo ./restore-repo.sh host

Tryb all

Wykonuje pełne odtworzenie konfiguracji:

  • Docker Compose
  • konfigurację hosta (host)
sudo ./restore-repo.sh all