#!/bin/bash # GitHub Repository: https://github.com/N0vachr0n0/NoFD # Description: Exercise on troubleshooting a systemd backup service # Course Link: # Fonction pour définir les messages selon la langue set_language_messages() { if [ "$USER_LANG" = "2" ]; then # Messages en anglais GREETING="Hello, Linux expert!" TITLE="=== Exercise: Systemd Backup Service Troubleshooting ===" TITLE_SPACE=" " INSTRUCTIONS="The backup-etc.service, responsible for automatic backups of configuration files in /etc, is no longer running correctly. Your mission is to diagnose and fix the issues to restore the hourly backups triggered by the backup-etc.timer. Additionally, when a backup is performed, the /var/log/backup.log file is updated with the operation details." HINT="Hint: https://github.com/N0vachr0n0/NoFD/blob/main/Hint_SVC_SYSTEMD_EXO.md" CHECKING="=== Checking in progress... ===" PROMPT_VERIFY="Do you want to proceed with the exercise verification? (y/n): " PROMPT_RESET="Reset the challenge? (y/n): " PROMPT_CHANGE_LANG="Change language? (1: Français, 2: English, any key to continue): " PROMPT_CONFIG="Initial configuration is being set up..." MSG_CHECK_OK="✅ Check passed." MSG_CHECK_FAIL="❌ Check failed." MSG_CHECK_WARN="⚠️ Warning (non-blocking check)." MSG_SUCCESS="🎉 Congratulations! All steps are correct." MSG_FLAG="🏁 Your flag: NoFD_{systemd_backup_troubleshooting}" else # Messages en français GREETING="Bonjour, expert Linux !" TITLE="=== Exercice : Dépannage d'un service de sauvegarde systemd ===" TITLE_SPACE=" " INSTRUCTIONS="Le service backup-etc.service, responsable des sauvegardes automatiques des fichiers de configuration dans /etc, ne s'exécute plus correctement. Votre mission est de diagnostiquer et corriger les problèmes pour restaurer les sauvegardes horaires déclenchées par le timer backup-etc.timer. De plus, lorsqu'une sauvegarde est effectuée, le fichier de log /var/log/backup.log est mis à jour avec les détails de l'opération." HINT="Indice: https://github.com/N0vachr0n0/NoFD/blob/main/Hint_SVC_SYSTEMD_EXO.md" CHECKING="=== Vérification en cours... ===" PROMPT_VERIFY="Voulez-vous procéder à la vérification de l'exercice ? (o/n) : " PROMPT_RESET="Réinitialiser le challenge ? (o/n) : " PROMPT_CHANGE_LANG="Changer la langue ? (1: Français, 2: English, autre touche pour continuer) : " PROMPT_CONFIG="La configuration initiale est en cours..." MSG_CHECK_OK="✅ Vérification réussie." MSG_CHECK_FAIL="❌ Vérification échouée." MSG_CHECK_WARN="⚠️ Avertissement (vérification non bloquante)." MSG_SUCCESS="🎉 Félicitations ! Toutes les étapes sont correctes." MSG_FLAG="🏁 Ton flag : NoFD_{systemd_backup_troubleshooting}" fi } # Vérifier si le script est exécuté avec sudo if [ "$EUID" -ne 0 ]; then printf "❌ Erreur : Ce script doit être exécuté avec sudo.\n❌ Error: This script must be run with sudo.\n" exit 1 fi # Demander à l'utilisateur de choisir une langue printf "Choose your language / Choisissez votre langue :\n" printf "1. Français\n" printf "2. English\n" printf "Enter 1 or 2 / Entrez 1 ou 2 : " read USER_LANG # Vérifier si la langue choisie est valide if [ "$USER_LANG" != "1" ] && [ "$USER_LANG" != "2" ]; then printf "❌ Error / Erreur : Invalid choice. Please select 1 for Français or 2 for English.\n" exit 1 fi # Définir les messages selon la langue choisie set_language_messages # Chemin du fichier de log de référence (nommé explicitement pour éviter toute ambiguïté avec un répertoire) VERIF_FILE="/var/tmp/verif_backup.log" # Fichier verrou de configuration (défini tôt : utilisé par le bloc de reset ci-dessous) CONFIG_FILE="/var/tmp/backup_service_config_done" # Demander si l'utilisateur veut réinitialiser le challenge printf "%s" "$PROMPT_RESET" read RESET if [[ "$RESET" = "y" || "$RESET" = "Y" || "$RESET" = "o" || "$RESET" = "O" ]]; then printf "Resetting the challenge...\nRéinitialisation du challenge...\n" # Arrêter et désactiver le timer systemctl stop backup-etc.timer >/dev/null 2>&1 systemctl disable backup-etc.timer >/dev/null 2>&1 # Supprimer tous les fichiers créés if [ -f /var/log/backup.log ]; then chattr -i /var/log/backup.log; fi rm -f /var/backups/backup_etc_*.tar.gz rm -f "$VERIF_FILE" rm -f /var/log/backup.log rm -f /usr/local/bin/backup-etc.sh /etc/systemd/system/backup-etc.service /etc/systemd/system/backup-etc.timer rm -f /var/tmp/backup_service_config_done # Recharger la configuration systemd systemctl daemon-reload >/dev/null 2>&1 printf "Challenge reset complete.\nRéinitialisation terminée.\n" exit 0 fi clear # Configuration initiale if [ ! -f "$CONFIG_FILE" ]; then printf "%s\n" "$PROMPT_CONFIG" sleep 2 # Nettoyer les fichiers résiduels (ne supprime que les archives de l'exercice, pas le répertoire système /var/backups) rm -f /var/backups/backup_etc_*.tar.gz rm -f "$VERIF_FILE" rm -f /var/log/backup.log rm -f /usr/local/bin/backup-etc.sh /etc/systemd/system/backup-etc.service /etc/systemd/system/backup-etc.timer # Créer le script de sauvegarde cat << 'EOF' > /usr/local/bin/backup-etc.sh #!/bin/bash # Directory to backup SOURCE_DIR="/etc" # Backup destination directory BACKUP_DIR="/var/backups" # Log file LOG_FILE="/var/log/backup.log" # Create backup directory if it doesn't exist mkdir -p "$BACKUP_DIR" # Generate filename with timestamp DATE=$(date +"%Y%m%d_%H%M") BACKUP_NAME="backup_etc_$DATE.tar.gz" # Create tar.gz archive tar -czf "$BACKUP_DIR/$BACKUP_NAME" -C "$(dirname "$SOURCE_DIR")" "$(basename "$SOURCE_DIR")" >> /dev/null 2>&1 # Log result if [ $? -eq 0 ]; then echo "$(date) - Backup created: $BACKUP_DIR/$BACKUP_NAME" >> "$LOG_FILE" else echo "$(date) - ERROR: Failed to create backup of $SOURCE_DIR" >> "$LOG_FILE" fi EOF chmod +x /usr/local/bin/backup-etc.sh chown root:root /usr/local/bin/backup-etc.sh # Créer le service systemd cat << 'EOF' > /etc/systemd/system/backup-etc.service [Unit] Description=Backup Service for /etc Directory After=network.target [Service] Type=oneshot ExecStart=/usr/local/bin/backup-etc.sh User=root Environment="PATH=/usr/bin:/usr/sbin:/bin" StandardOutput=journal StandardError=journal SyslogIdentifier=backup-etc [Install] WantedBy=multi-user.target EOF # Créer le timer systemd cat << 'EOF' > /etc/systemd/system/backup-etc.timer [Unit] Description=Run /etc backup every hour [Timer] OnCalendar=hourly AccuracySec=1min Persistent=true Unit=backup-etc.service [Install] WantedBy=timers.target EOF # Activer et démarrer le timer systemctl daemon-reload systemctl enable backup-etc.timer systemctl start backup-etc.timer # Exécuter le service pour générer un log de référence (Type=oneshot : démarrage synchrone, pas besoin de sleep) if ! systemctl start backup-etc.service; then printf "❌ Erreur : impossible de démarrer backup-etc.service lors de l'initialisation.\n" printf "❌ Error: unable to start backup-etc.service during initialization.\n" exit 1 fi # Copier le log de référence if [ -f "/var/log/backup.log" ]; then cp /var/log/backup.log "$VERIF_FILE" fi # Introduire les erreurs chattr +i /var/log/backup.log chmod -x /usr/local/bin/backup-etc.sh sed -i 's|ExecStart=/usr/local/bin/backup-etc.sh|ExecStart=/usr/local/backup-etc.sh|' /etc/systemd/system/backup-etc.service systemctl daemon-reload # Tenter de démarrer le service (devrait échouer, c'est la panne à corriger) systemctl start backup-etc.service >/dev/null 2>&1 || true touch "$CONFIG_FILE" fi # Afficher l'énoncé printf "\n" printf "%s\n" "$TITLE" printf "%s\n" "$TITLE_SPACE" printf "%s\n" "$GREETING" printf "%s\n\n" "$INSTRUCTIONS" printf "%s\n\n" "$HINT" # Donner la possibilité de changer la langue printf "%s" "$PROMPT_CHANGE_LANG" read CHANGE_LANG if [[ "$CHANGE_LANG" = "1" || "$CHANGE_LANG" = "2" ]]; then USER_LANG=$CHANGE_LANG set_language_messages clear printf "\n" printf "%s\n" "$TITLE" printf "%s\n" "$TITLE_SPACE" printf "%s\n" "$GREETING" printf "%s\n\n" "$INSTRUCTIONS" printf "%s\n\n" "$HINT" fi # Demander confirmation avant la vérification printf "%s" "$PROMPT_VERIFY" read CONFIRM if [[ "$CONFIRM" != "y" && "$CONFIRM" != "Y" && "$CONFIRM" != "o" && "$CONFIRM" != "O" ]]; then exit 0 fi printf "\n" printf "%s\n" "$CHECKING" # Vérifications # 1. Vérifier que /var/log/backup.log n'est pas immuable if lsattr /var/log/backup.log 2>/dev/null | awk '{print $1}' | grep -q i; then printf "%s\n" "$MSG_CHECK_FAIL" exit 1 else printf "%s\n" "$MSG_CHECK_OK" fi # 2. Vérifier que backup-etc.service pointe vers /usr/local/bin/backup-etc.sh if grep -qE '^ExecStart=/usr/local/bin/backup-etc\.sh *$' /etc/systemd/system/backup-etc.service; then printf "%s\n" "$MSG_CHECK_OK" else printf "%s\n" "$MSG_CHECK_FAIL" exit 1 fi # 3. Comparer la première ligne du log de référence avec /var/log/backup.log (avertissement non bloquant : # les checks fonctionnels ci-dessous sont la source de vérité sur l'état réel du service) if [ -f "$VERIF_FILE" ] && [ -f "/var/log/backup.log" ]; then VERIF_LINE=$(head -n 1 "$VERIF_FILE") LOG_LINE=$(head -n 1 /var/log/backup.log) if [ "$VERIF_LINE" = "$LOG_LINE" ] && [ -n "$VERIF_LINE" ]; then printf "%s\n" "$MSG_CHECK_OK" else printf "%s\n" "$MSG_CHECK_WARN" fi else printf "%s\n" "$MSG_CHECK_WARN" fi # 4. Vérifier que le script de sauvegarde est exécutable if [ -x /usr/local/bin/backup-etc.sh ]; then printf "%s\n" "$MSG_CHECK_OK" else printf "%s\n" "$MSG_CHECK_FAIL" exit 1 fi # 5. Vérifier que le timer est actif et activé au démarrage if systemctl is-active --quiet backup-etc.timer && systemctl is-enabled --quiet backup-etc.timer; then printf "%s\n" "$MSG_CHECK_OK" else printf "%s\n" "$MSG_CHECK_FAIL" exit 1 fi # 6. Test fonctionnel : le service doit réellement produire une nouvelle ligne de log et une nouvelle archive LOG_LINES_BEFORE=0 if [ -f /var/log/backup.log ]; then LOG_LINES_BEFORE=$(wc -l < /var/log/backup.log) fi ARCHIVES_BEFORE=$(find /var/backups -maxdepth 1 -name 'backup_etc_*.tar.gz' 2>/dev/null | wc -l) systemctl start backup-etc.service >/dev/null 2>&1 LOG_LINES_AFTER=0 if [ -f /var/log/backup.log ]; then LOG_LINES_AFTER=$(wc -l < /var/log/backup.log) fi ARCHIVES_AFTER=$(find /var/backups -maxdepth 1 -name 'backup_etc_*.tar.gz' 2>/dev/null | wc -l) NEW_LOG_LINES="" if [ "$LOG_LINES_AFTER" -gt "$LOG_LINES_BEFORE" ]; then NEW_LOG_LINES=$(tail -n "$((LOG_LINES_AFTER - LOG_LINES_BEFORE))" /var/log/backup.log) fi if [ "$LOG_LINES_AFTER" -gt "$LOG_LINES_BEFORE" ] && printf '%s\n' "$NEW_LOG_LINES" | grep -q "Backup created" && [ "$ARCHIVES_AFTER" -gt "$ARCHIVES_BEFORE" ]; then printf "%s\n" "$MSG_CHECK_OK" else printf "%s\n" "$MSG_CHECK_FAIL" exit 1 fi # Message de fin printf "\n" printf "%s\n" "$MSG_SUCCESS" printf "%s\n" "$MSG_FLAG"