#!/bin/bash # Script de sauvegarde Traefik vers partage Samba via rsync # Auteur: Script généré par Claude # Date: $(date +%Y-%m-%d) set -euo pipefail # ============================================================================= # CONFIGURATION # ============================================================================= # Configuration Traefik TRAEFIK_CONFIG_DIR="/etc/traefik" TRAEFIK_DATA_DIR="/var/lib/traefik" TRAEFIK_DYNAMIC_CONFIG_DIR="/etc/traefik/dynamic" TRAEFIK_ACME_DIR="/var/lib/traefik/acme" TRAEFIK_LOGS_DIR="/var/log/traefik" TRAEFIK_SERVICE="traefik" TRAEFIK_USER="traefik" # Chemins alternatifs pour installation Docker DOCKER_TRAEFIK_VOLUME="traefik-data" DOCKER_COMPOSE_DIR="/opt/traefik" DOCKER_TRAEFIK_CONFIG="/opt/traefik/config" # Configuration du partage Samba SAMBA_SERVER="192.168.77.230" SAMBA_SHARE="/srv/samba/partage/Backups" SAMBA_USER="root" # Configuration SSH SSH_KEY_PATH="$HOME/.ssh/traefik_backup_ed25519" SSH_OPTIONS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR" # Configuration de sauvegarde BACKUP_BASE_DIR="/tmp/traefik_backup_$(date +%Y%m%d_%H%M%S)" REMOTE_BACKUP_DIR="traefik_backups" LOG_FILE="/var/log/traefik_backup.log" RETENTION_DAYS=30 # Options de sauvegarde BACKUP_CONFIG=true # Sauvegarder la configuration BACKUP_CERTIFICATES=true # Sauvegarder les certificats SSL/TLS BACKUP_LOGS=true # Sauvegarder les logs (optionnel) BACKUP_DYNAMIC_CONFIG=true # Sauvegarder la configuration dynamique COMPRESS_LOGS=true # Compresser les logs pour économiser l'espace # ============================================================================= # FONCTIONS UTILITAIRES # ============================================================================= log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" } error_exit() { log "ERREUR: $1" exit 1 } check_root() { if [[ $EUID -ne 0 ]]; then error_exit "Ce script doit être exécuté en tant que root" fi } check_command() { if ! command -v "$1" &> /dev/null; then return 1 fi return 0 } # ============================================================================= # INSTALLATION DES DÉPENDANCES # ============================================================================= install_dependencies() { log "Vérification et installation des dépendances..." # Détecter la distribution if [[ -f /etc/debian_version ]]; then DISTRO="debian" PKG_MANAGER="apt-get" PACKAGES="rsync samba-client cifs-utils openssh-client docker.io docker-compose jq gzip tar" elif [[ -f /etc/redhat-release ]]; then DISTRO="redhat" PKG_MANAGER="yum" PACKAGES="rsync cifs-utils openssh-clients docker docker-compose jq gzip tar" else error_exit "Distribution non supportée" fi log "Distribution détectée: $DISTRO" # Mise à jour du cache des paquets if [[ "$DISTRO" == "debian" ]]; then apt-get update -qq fi # Installation des paquets manquants for package in $PACKAGES; do if [[ "$DISTRO" == "debian" ]]; then if ! dpkg -l | grep -q "^ii $package "; then log "Installation de $package..." apt-get install -y -qq "$package" || log "Impossible d'installer $package (peut-être optionnel)" else log "$package est déjà installé" fi elif [[ "$DISTRO" == "redhat" ]]; then if ! rpm -q "$package" >/dev/null 2>&1; then log "Installation de $package..." yum install -y "$package" || log "Impossible d'installer $package (peut-être optionnel)" else log "$package est déjà installé" fi fi done } # ============================================================================= # GESTION DES CERTIFICATS SSH # ============================================================================= setup_ssh_key() { log "Configuration de la clé SSH ED25519..." if [[ ! -f "$SSH_KEY_PATH" ]]; then log "Création de la clé SSH ED25519..." ssh-keygen -t ed25519 -f "$SSH_KEY_PATH" -N "" -C "traefik-backup-$(hostname)" chmod 600 "$SSH_KEY_PATH" chmod 644 "${SSH_KEY_PATH}.pub" log "Clé SSH créée: $SSH_KEY_PATH" log "Clé publique: ${SSH_KEY_PATH}.pub" echo log "IMPORTANT: Vous devez copier cette clé publique sur le serveur distant:" log "==========================================" cat "${SSH_KEY_PATH}.pub" log "==========================================" echo log "Commande pour copier la clé sur le serveur distant:" log "ssh-copy-id -i $SSH_KEY_PATH ${SAMBA_USER}@${SAMBA_SERVER}" echo read -p "Appuyez sur Entrée une fois la clé configurée sur le serveur distant..." else log "Clé SSH existante trouvée: $SSH_KEY_PATH" fi } test_ssh_connection() { log "Test de la connexion SSH..." if ssh -i "$SSH_KEY_PATH" $SSH_OPTIONS "${SAMBA_USER}@${SAMBA_SERVER}" "echo 'Connexion SSH réussie'" 2>/dev/null; then log "Connexion SSH: OK" else error_exit "Impossible de se connecter via SSH. Vérifiez la configuration de la clé." fi } # ============================================================================= # DÉTECTION DE L'ENVIRONNEMENT # ============================================================================= detect_environment() { log "Détection de l'environnement Traefik..." # Détection de Traefik TRAEFIK_INSTALL_TYPE="none" if systemctl is-enabled "$TRAEFIK_SERVICE" >/dev/null 2>&1; then TRAEFIK_INSTALL_TYPE="systemd" log "Traefik détecté: installation systemd" elif check_command traefik; then TRAEFIK_INSTALL_TYPE="binary" log "Traefik détecté: installation binaire" elif docker ps --format "table {{.Names}}" | grep -q traefik; then TRAEFIK_INSTALL_TYPE="docker" log "Traefik détecté: conteneur Docker" elif [[ -d "$DOCKER_COMPOSE_DIR" && -f "$DOCKER_COMPOSE_DIR/docker-compose.yml" ]]; then TRAEFIK_INSTALL_TYPE="docker-compose" log "Traefik détecté: Docker Compose" else log "ATTENTION: Traefik non détecté" fi # Adaptation des chemins selon l'environnement if [[ "$TRAEFIK_INSTALL_TYPE" == "docker" ]]; then TRAEFIK_DATA_DIR="/var/lib/docker/volumes/${DOCKER_TRAEFIK_VOLUME}/_data" TRAEFIK_CONFIG_DIR="$DOCKER_TRAEFIK_CONFIG" TRAEFIK_ACME_DIR="$TRAEFIK_DATA_DIR" fi # Vérification des répertoires existants if [[ ! -d "$TRAEFIK_CONFIG_DIR" ]]; then log "ATTENTION: Répertoire de configuration non trouvé: $TRAEFIK_CONFIG_DIR" fi } # ============================================================================= # GESTION DES SERVICES # ============================================================================= stop_services() { log "Arrêt des services..." TRAEFIK_WAS_RUNNING=false # Arrêt de Traefik case "$TRAEFIK_INSTALL_TYPE" in "systemd") if systemctl is-active --quiet "$TRAEFIK_SERVICE"; then systemctl stop "$TRAEFIK_SERVICE" TRAEFIK_WAS_RUNNING=true log "Service Traefik arrêté" fi ;; "docker") if docker ps -q -f name=traefik | grep -q .; then docker stop traefik >/dev/null 2>&1 TRAEFIK_WAS_RUNNING=true log "Conteneur Traefik arrêté" fi ;; "docker-compose") if [[ -f "$DOCKER_COMPOSE_DIR/docker-compose.yml" ]]; then cd "$DOCKER_COMPOSE_DIR" docker-compose stop >/dev/null 2>&1 TRAEFIK_WAS_RUNNING=true log "Services Docker Compose arrêtés" fi ;; esac # Attendre un peu pour que les fichiers soient libérés sleep 2 } start_services() { log "Redémarrage des services..." # Redémarrage de Traefik if [[ "$TRAEFIK_WAS_RUNNING" == "true" ]]; then case "$TRAEFIK_INSTALL_TYPE" in "systemd") systemctl start "$TRAEFIK_SERVICE" log "Service Traefik redémarré" ;; "docker") docker start traefik >/dev/null 2>&1 log "Conteneur Traefik redémarré" ;; "docker-compose") if [[ -f "$DOCKER_COMPOSE_DIR/docker-compose.yml" ]]; then cd "$DOCKER_COMPOSE_DIR" docker-compose start >/dev/null 2>&1 log "Services Docker Compose redémarrés" fi ;; esac fi } # ============================================================================= # FONCTIONS DE SAUVEGARDE # ============================================================================= create_backup_structure() { log "Création de la structure de sauvegarde: $BACKUP_BASE_DIR" mkdir -p "$BACKUP_BASE_DIR"/{config,data,certificates,logs,metadata} } backup_traefik_config() { log "Sauvegarde de la configuration Traefik..." # Sauvegarde de la configuration principale if [[ -d "$TRAEFIK_CONFIG_DIR" && "$BACKUP_CONFIG" == "true" ]]; then log "Sauvegarde de la configuration principale..." rsync -av --progress "$TRAEFIK_CONFIG_DIR/" "$BACKUP_BASE_DIR/config/" \ --exclude="*.log" --exclude="*.pid" fi # Sauvegarde de la configuration dynamique if [[ -d "$TRAEFIK_DYNAMIC_CONFIG_DIR" && "$BACKUP_DYNAMIC_CONFIG" == "true" ]]; then log "Sauvegarde de la configuration dynamique..." rsync -av --progress "$TRAEFIK_DYNAMIC_CONFIG_DIR/" "$BACKUP_BASE_DIR/config/dynamic/" fi # Sauvegarde des fichiers Docker Compose si applicable if [[ "$TRAEFIK_INSTALL_TYPE" == "docker-compose" && -d "$DOCKER_COMPOSE_DIR" ]]; then log "Sauvegarde de la configuration Docker Compose..." rsync -av --progress "$DOCKER_COMPOSE_DIR/" "$BACKUP_BASE_DIR/config/docker-compose/" \ --include="*.yml" --include="*.yaml" --include="*.env" --include="*.toml" --exclude="*" fi } backup_traefik_data() { log "Sauvegarde des données Traefik..." # Sauvegarde du répertoire de données if [[ -d "$TRAEFIK_DATA_DIR" ]]; then log "Sauvegarde des données Traefik..." rsync -av --progress "$TRAEFIK_DATA_DIR/" "$BACKUP_BASE_DIR/data/" \ --exclude="*.log" --exclude="*.pid" --exclude="access.log" fi } backup_certificates() { log "Sauvegarde des certificats..." if [[ "$BACKUP_CERTIFICATES" == "true" ]]; then # Sauvegarde des certificats ACME/Let's Encrypt if [[ -d "$TRAEFIK_ACME_DIR" ]]; then log "Sauvegarde des certificats ACME..." find "$TRAEFIK_ACME_DIR" -name "*.json" -o -name "acme.json" -o -name "*.crt" -o -name "*.key" | \ while read -r cert_file; do if [[ -f "$cert_file" ]]; then rel_path="${cert_file#$TRAEFIK_ACME_DIR/}" mkdir -p "$BACKUP_BASE_DIR/certificates/$(dirname "$rel_path")" cp "$cert_file" "$BACKUP_BASE_DIR/certificates/$rel_path" log "Certificat sauvegardé: $rel_path" fi done fi # Recherche de certificats dans d'autres emplacements courants for cert_dir in "/etc/ssl/traefik" "/opt/traefik/certs" "/var/lib/traefik/certificates"; do if [[ -d "$cert_dir" ]]; then log "Sauvegarde des certificats depuis: $cert_dir" rsync -av --progress "$cert_dir/" "$BACKUP_BASE_DIR/certificates/additional/" \ --include="*.crt" --include="*.key" --include="*.pem" --include="*.json" --exclude="*" fi done fi } backup_logs() { log "Sauvegarde des logs..." if [[ "$BACKUP_LOGS" == "true" && -d "$TRAEFIK_LOGS_DIR" ]]; then log "Sauvegarde des logs Traefik..." if [[ "$COMPRESS_LOGS" == "true" ]]; then log "Compression des logs en cours..." tar -czf "$BACKUP_BASE_DIR/logs/traefik_logs.tar.gz" -C "$(dirname "$TRAEFIK_LOGS_DIR")" "$(basename "$TRAEFIK_LOGS_DIR")" else rsync -av --progress "$TRAEFIK_LOGS_DIR/" "$BACKUP_BASE_DIR/logs/" \ --include="*.log" --exclude="*.pid" fi fi # Sauvegarde des logs systemd si applicable if [[ "$TRAEFIK_INSTALL_TYPE" == "systemd" ]]; then log "Export des logs systemd..." journalctl -u "$TRAEFIK_SERVICE" --since "7 days ago" > "$BACKUP_BASE_DIR/logs/systemd.log" 2>/dev/null || true fi } create_metadata() { log "Création des métadonnées de sauvegarde..." # Informations système cat > "$BACKUP_BASE_DIR/metadata/system_info.txt" << EOF Sauvegarde Traefik ================== Date: $(date) Serveur: $(hostname) Version script: 1.0 Utilisateur: $(whoami) Environnement détecté: - Type d'installation: $TRAEFIK_INSTALL_TYPE - Répertoire de configuration: $TRAEFIK_CONFIG_DIR - Répertoire de données: $TRAEFIK_DATA_DIR - Répertoire ACME: $TRAEFIK_ACME_DIR - Répertoire des logs: $TRAEFIK_LOGS_DIR Options de sauvegarde: - Configuration: $BACKUP_CONFIG - Configuration dynamique: $BACKUP_DYNAMIC_CONFIG - Certificats: $BACKUP_CERTIFICATES - Logs: $BACKUP_LOGS - Compression des logs: $COMPRESS_LOGS EOF # Version de Traefik si accessible if check_command traefik; then log "Récupération de la version Traefik..." traefik version > "$BACKUP_BASE_DIR/metadata/traefik_version.txt" 2>/dev/null || \ echo "Version Traefik non disponible" > "$BACKUP_BASE_DIR/metadata/traefik_version.txt" fi # Informations sur les conteneurs Docker si applicable if check_command docker; then log "Sauvegarde des informations Docker..." docker ps -a --format "table {{.Names}}\t{{.Image}}\t{{.Status}}" > "$BACKUP_BASE_DIR/metadata/docker_containers.txt" 2>/dev/null || true docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}" | grep -i traefik > "$BACKUP_BASE_DIR/metadata/docker_images.txt" 2>/dev/null || true fi # Liste des certificats sauvegardés if [[ -d "$BACKUP_BASE_DIR/certificates" ]]; then find "$BACKUP_BASE_DIR/certificates" -type f -name "*.json" -o -name "*.crt" -o -name "*.key" -o -name "*.pem" | \ sed "s|$BACKUP_BASE_DIR/certificates/||" > "$BACKUP_BASE_DIR/metadata/certificates_list.txt" fi # Taille de la sauvegarde BACKUP_SIZE=$(du -sh "$BACKUP_BASE_DIR" | cut -f1) echo "Taille totale de la sauvegarde: $BACKUP_SIZE" >> "$BACKUP_BASE_DIR/metadata/system_info.txt" log "Sauvegarde locale créée dans: $BACKUP_BASE_DIR (Taille: $BACKUP_SIZE)" } sync_to_remote() { log "Synchronisation vers le serveur distant..." # Création du répertoire distant si nécessaire ssh -i "$SSH_KEY_PATH" $SSH_OPTIONS "${SAMBA_USER}@${SAMBA_SERVER}" \ "mkdir -p ${SAMBA_SHARE}/${REMOTE_BACKUP_DIR}/$(date +%Y/%m)" # Synchronisation des fichiers REMOTE_PATH="${SAMBA_USER}@${SAMBA_SERVER}:${SAMBA_SHARE}/${REMOTE_BACKUP_DIR}/$(date +%Y/%m)/traefik_backup_$(date +%Y%m%d_%H%M%S)/" log "Envoi vers: $REMOTE_PATH" rsync -avz --progress --delete \ -e "ssh -i $SSH_KEY_PATH $SSH_OPTIONS" \ "$BACKUP_BASE_DIR/" \ "$REMOTE_PATH" log "Synchronisation terminée" } cleanup_local() { log "Nettoyage des fichiers temporaires..." if [[ -d "$BACKUP_BASE_DIR" ]]; then rm -rf "$BACKUP_BASE_DIR" log "Répertoire temporaire supprimé: $BACKUP_BASE_DIR" fi } cleanup_remote() { log "Nettoyage des anciennes sauvegardes (plus de $RETENTION_DAYS jours)..." ssh -i "$SSH_KEY_PATH" $SSH_OPTIONS "${SAMBA_USER}@${SAMBA_SERVER}" \ "find ${SAMBA_SHARE}/${REMOTE_BACKUP_DIR} -type d -name 'traefik_backup_*' -mtime +${RETENTION_DAYS} -exec rm -rf {} + 2>/dev/null || true" log "Nettoyage des anciennes sauvegardes terminé" } # ============================================================================= # FONCTION PRINCIPALE # ============================================================================= main() { log "=== DÉBUT DE LA SAUVEGARDE TRAEFIK ===" # Vérifications préliminaires check_root # Installation des dépendances install_dependencies # Configuration SSH setup_ssh_key test_ssh_connection # Détection de l'environnement detect_environment # Variables pour suivre l'état des services TRAEFIK_WAS_RUNNING=false # Gestion des erreurs avec nettoyage trap 'cleanup_local; start_services' EXIT ERR # Arrêt des services pour une sauvegarde cohérente stop_services # Création de la structure de sauvegarde create_backup_structure # Sauvegarde de la configuration backup_traefik_config # Sauvegarde des données backup_traefik_data # Sauvegarde des certificats backup_certificates # Sauvegarde des logs backup_logs # Création des métadonnées create_metadata # Redémarrage des services dès que possible start_services # Synchronisation vers le serveur distant sync_to_remote # Nettoyage cleanup_remote cleanup_local log "=== SAUVEGARDE TRAEFIK TERMINÉE AVEC SUCCÈS ===" } # ============================================================================= # POINT D'ENTRÉE # ============================================================================= # Vérification des arguments pour les options avancées while [[ $# -gt 0 ]]; do case $1 in --no-config) BACKUP_CONFIG=false log "Option: Sauvegarde de la configuration désactivée" shift ;; --no-certificates) BACKUP_CERTIFICATES=false log "Option: Sauvegarde des certificats désactivée" shift ;; --no-logs) BACKUP_LOGS=false log "Option: Sauvegarde des logs désactivée" shift ;; --no-dynamic-config) BACKUP_DYNAMIC_CONFIG=false log "Option: Sauvegarde de la configuration dynamique désactivée" shift ;; --no-compression) COMPRESS_LOGS=false log "Option: Compression des logs désactivée" shift ;; --help) echo "Usage: $0 [OPTIONS]" echo "Options:" echo " --no-config Ne pas sauvegarder la configuration" echo " --no-certificates Ne pas sauvegarder les certificats" echo " --no-logs Ne pas sauvegarder les logs" echo " --no-dynamic-config Ne pas sauvegarder la configuration dynamique" echo " --no-compression Ne pas compresser les logs" echo " --help Afficher cette aide" exit 0 ;; *) error_exit "Option inconnue: $1" ;; esac done # Création du fichier de log si nécessaire mkdir -p "$(dirname "$LOG_FILE")" touch "$LOG_FILE" # Lancement du script principal main "$@"