Sync Repo
Konfiguracja synchronizacji repozytorium
Skrypt synchronizacji repozytorium realizuje:
- synchronizację definicji stacków Docker Compose (bez danych i sekretów),
- synchronizację wybranych plików konfiguracyjnych z katalogu
/etc, - synchronizację konfiguracji systemd z katalogu
/etc/systemd, - synchronizację własnych jednostek i override’ów systemd z katalogu
/etc/systemd/system, - blokadę równoległego uruchomienia skryptu (mechanizm lock),
- walidację konfiguracji i wymaganych zmiennych środowiskowych,
- korektę uprawnień w repozytorium po zakończeniu synchronizacji.
Plik konfiguracyjny
Tworzymy plik konfiguracyjny dla skryptu synchronizacji:
sudo micro /srv/config/git/sync.env
Treść pliku:
REPO_ROOT="/srv/config/apps-server"
REPO_USER="fulcro"
REPO_GROUP="fulcro"
SOURCE_COMPOSE="/srv/docker/compose"
SOURCE_SCRIPTS_BACKUP="/srv/backups/scripts"
SOURCE_SCRIPTS_CONFIG="/srv/config/scripts"
Zabezpieczamy plik:
sudo chown root:root /srv/config/git/sync.env
sudo chmod 600 /srv/config/git/sync.env
Skrypt synchronizacji repozytorium
Tworzymy jeden zbiorczy skrypt odpowiedzialny za synchronizację:
sudo micro /srv/config/scripts/sync-repo.sh
#!/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}[sync]${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 (sync.env is root:root 600)
if [[ "${EUID:-$(id -u)}" -ne 0 ]]; then
critical "Run as root (use sudo)."
exit 1
fi
# ============================================================
# LOCK MECHANISM (Prevent parallel execution)
# ============================================================
LOCK_FILE="/tmp/sync-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 "Target directory $REPO_ROOT does not exist."
exit 1
fi
if [[ "$REPO_ROOT" == "/" ]]; then
critical "SAFETY STOP: REPO_ROOT is set to /"
exit 1
fi
if [[ "$REPO_ROOT" != /srv/* ]]; then
critical "SAFETY STOP: REPO_ROOT must be under /srv (got: $REPO_ROOT)"
exit 1
fi
# ============================================================
# HELPER FUNCTIONS
# ============================================================
copy_files() {
local src_dir="$1"
local dst_rel_path="$2"
shift 2
local files=("$@")
local full_dst="$REPO_ROOT/$dst_rel_path"
mkdir -p "$full_dst"
for f in "${files[@]}"; do
if [[ -f "$src_dir/$f" ]]; then
cp -a -- "$src_dir/$f" "$full_dst/$f"
echo " + Copied: $dst_rel_path/$f"
else
echo -e " ${YELLOW}! Skipped (missing): $src_dir/$f${NC}"
fi
done
}
fix_permissions() {
header "Fixing Permissions"
log "Setting ownership to $REPO_USER:$REPO_GROUP in $REPO_ROOT..."
chown -R "$REPO_USER:$REPO_GROUP" "$REPO_ROOT"
log "Done."
}
# ============================================================
# SYNC MODULES
# ============================================================
sync_compose() {
header "Sync: Docker Compose"
local dst="$REPO_ROOT/compose/"
mkdir -p "$dst"
if [[ ! -d "${SOURCE_COMPOSE:-}" ]]; then
warn "SOURCE_COMPOSE not found or unset. Skipping."
return
fi
rsync -a --delete \
--exclude ".git/" \
--exclude "**/.env" \
--exclude "**/data/" \
--exclude "**/volumes/" \
--exclude "**/logs/" \
--exclude "**/backups/" \
--exclude "**/*.db" \
--exclude "**/*.sqlite*" \
"$SOURCE_COMPOSE/" "$dst"
log "Rsync completed."
}
# Wrapper: /etc/<dir> -> host/etc/<dir>
etc_copy() {
local rel_dir="$1"
shift 1
local src_dir="/etc"
local dst_rel="host/etc"
if [[ "$rel_dir" != "." ]]; then
src_dir="/etc/$rel_dir"
dst_rel="host/etc/$rel_dir"
fi
copy_files "$src_dir" "$dst_rel" "$@"
}
# Wrapper: /etc/systemd/<dir> -> host/etc/systemd/<dir>
systemd_copy() {
local rel_dir="$1"
shift 1
local src_dir="/etc/systemd"
local dst_rel="host/etc/systemd"
if [[ "$rel_dir" != "." ]]; then
src_dir="/etc/systemd/$rel_dir"
dst_rel="host/etc/systemd/$rel_dir"
fi
copy_files "$src_dir" "$dst_rel" "$@"
}
# Wrapper: /etc/systemd/system/<dir> -> host/etc/systemd/system/<dir>
units_copy() {
local rel_dir="$1"
shift 1
local src_dir="/etc/systemd/system"
local dst_rel="host/etc/systemd/system"
if [[ "$rel_dir" != "." ]]; then
src_dir="/etc/systemd/system/$rel_dir"
dst_rel="host/etc/systemd/system/$rel_dir"
fi
copy_files "$src_dir" "$dst_rel" "$@"
}
sync_etc() {
header "Sync: Host /etc"
etc_copy "ssh" sshd_config ssh_config
etc_copy "fail2ban" jail.local
etc_copy "ufw" ufw.conf user.rules
etc_copy "docker" daemon.json
}
sync_systemd() {
header "Sync: Host /etc/systemd"
systemd_copy "." journald.conf
}
sync_units() {
header "Sync: Host /etc/systemd/system"
units_copy "." restic-backup.service restic-backup.timer
}
sync_host() {
header "Sync: Host (etc + systemd + units)"
sync_etc
sync_systemd
sync_units
}
# ============================================================
# EXECUTION
# ============================================================
TARGET=${1:-host}
case "$TARGET" in
compose) sync_compose; fix_permissions ;;
etc) sync_etc; fix_permissions ;;
systemd) sync_systemd; fix_permissions ;;
units) sync_units; fix_permissions ;;
host) sync_host; fix_permissions ;;
all) sync_compose; sync_host; fix_permissions ;;
*) 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/sync-repo.sh
Uruchamianie synchronizacji
Synchronizację wykonujemy z katalogu skryptów:
cd /srv/config/scripts
sudo ./sync-repo.sh all
Tryby synchronizacji
Skrypt sync-repo.sh obsługuje następujące tryby pracy:
Tryb compose
Synchronizuje wyłącznie definicje Docker Compose (z katalogu źródłowego do repozytorium).
- kopiowane są pliki konfiguracyjne stacków,
- pliki
.env, dane runtime i wolumeny są pomijane.
sudo ./sync-repo.sh compose
Tryb etc
Synchronizuje wybrane pliki konfiguracyjne systemu z /etc
(np. ssh, fail2ban, ufw, docker).
sudo ./sync-repo.sh etc
Tryb systemd
Synchronizuje konfigurację systemd
(np. journald.conf).
sudo ./sync-repo.sh systemd
Tryb units
Synchronizuje jednostki systemd
(np. *.service, *.timer).
sudo ./sync-repo.sh units
Tryb host
Synchronizuje całą konfigurację hosta:
/etc/etc/systemd/etc/systemd/system
Jest to tryb domyślny.
sudo ./sync-repo.sh host
Tryb all
Wykonuje pełną synchronizację:
- Docker Compose
- konfigurację hosta (
host)
sudo ./sync-repo.sh all