From 171957aca536725be83304f911c8f0335a4b17d2 Mon Sep 17 00:00:00 2001 From: Johnny Date: Mon, 5 Jan 2026 21:13:23 +0000 Subject: [PATCH] Actualiser system_hardening_optimized.sh --- system_hardening_optimized.sh | 772 ++++++++++++++++++++++++++++------ 1 file changed, 642 insertions(+), 130 deletions(-) diff --git a/system_hardening_optimized.sh b/system_hardening_optimized.sh index d6c3a40..a103168 100644 --- a/system_hardening_optimized.sh +++ b/system_hardening_optimized.sh @@ -45,6 +45,7 @@ readonly DEFAULT_UMASK="027" : "${AUTO_YES:=no}" : "${AUTO_CLEANUP_SSH:=no}" : "${AUTO_CHANGE_ROOT_PWD:=yes}" +: "${AUTO_SKIP_DEBSUMS_CONTAINER:=yes}" TOTAL_STEPS=33 CURRENT_STEP=1 @@ -198,12 +199,141 @@ skip_step() { } detect_container() { - grep -qE "lxc|container" /proc/self/cgroup 2>/dev/null || \ - [[ -f /.dockerenv ]] || [[ -d /dev/lxc ]] + # ----------------------------------------------------------------- + # DÉTECTION DE TOUS TYPES DE CONTENEURS (LXC, Docker, systemd-nspawn, etc.) + # Retourne 0 si dans un conteneur, 1 sinon + # ----------------------------------------------------------------- + + # 1. FICHIERS SPÉCIFIQUES + [[ -f /.dockerenv ]] && { log_message "Conteneur détecté: /.dockerenv" "DETECT"; return 0; } + [[ -d /dev/lxc ]] && { log_message "Conteneur détecté: /dev/lxc" "DETECT"; return 0; } + [[ -f /.lxcpid ]] && { log_message "Conteneur détecté: /.lxcpid" "DETECT"; return 0; } + + # 2. VARIABLES D'ENVIRONNEMENT (utilisation de ${var:-} pour éviter "unbound variable") + [[ -n "${container:-}" ]] && { log_message "Conteneur détecté: variable \$container=$container" "DETECT"; return 0; } + [[ -n "${DOCKER_HOST:-}" ]] && { log_message "Conteneur détecté: variable \$DOCKER_HOST" "DETECT"; return 0; } + [[ -n "${KUBERNETES_SERVICE_HOST:-}" ]] && { log_message "Conteneur détecté: Kubernetes" "DETECT"; return 0; } + + # 3. CGROUPS (compatible v1 et v2) + if [[ -f /proc/1/cgroup ]]; then + # Lecture de /proc/1/cgroup (plus fiable que /proc/self/cgroup) + local cgroup_content + cgroup_content=$(cat /proc/1/cgroup 2>/dev/null || echo "") + + # Modèles de conteneurs dans cgroups + if echo "$cgroup_content" | grep -qE "(docker|lxc|kubepods|containerd|podman|buildkit|systemd-nspawn|libpod)"; then + log_message "Conteneur détecté: cgroups pattern" "DETECT" + return 0 + fi + + # Cgroups v2: format "0::/" + if echo "$cgroup_content" | grep -q "^0::/"; then + # Vérifier si le chemin n'est pas un chemin système standard + local cgroup_path + cgroup_path=$(echo "$cgroup_content" | grep "^0::/" | cut -d: -f3) + + # Les chemins typiques des conteneurs + if [[ "$cgroup_path" == "/" ]] || + [[ "$cgroup_path" == /user.slice* ]] || + [[ "$cgroup_path" == /system.slice* ]]; then + # Pour cgroups v2, vérifier l'isolation des namespaces + if [[ -r /proc/1/ns/pid ]] && [[ -r /proc/self/ns/pid ]]; then + local pid1_inode + local self_inode + pid1_inode=$(stat -Lc '%i' /proc/1/ns/pid 2>/dev/null) + self_inode=$(stat -Lc '%i' /proc/self/ns/pid 2>/dev/null) + + # Si les inodes sont différents, on est dans un namespace PID isolé (conteneur) + if [[ -n "$pid1_inode" ]] && [[ -n "$self_inode" ]] && [[ "$pid1_inode" != "$self_inode" ]]; then + log_message "Conteneur détecté: namespace PID isolé (cgroups v2)" "DETECT" + return 0 + fi + fi + else + # Chemin non standard -> probablement un conteneur + log_message "Conteneur détecté: cgroups v2 path non standard: $cgroup_path" "DETECT" + return 0 + fi + fi + fi + + # 4. SYSTEMD-DETECT-VIRT (outil natif) + if command -v systemd-detect-virt >/dev/null 2>&1; then + local virt + virt=$(systemd-detect-virt 2>/dev/null) + case "$virt" in + "container"|"lxc"|"lxc-libvirt"|"systemd-nspawn"|"docker"|"podman"|"wsl") + log_message "Conteneur détecté: systemd-detect-virt=$virt" "DETECT" + return 0 + ;; + esac + fi + + # 5. VÉRIFICATION DES PROCESSUS ET NAMESPACES + # Vérifier l'isolation des namespaces (méthode avancée) + if [[ -r /proc/1/ns/pid ]] && [[ -r /proc/self/ns/pid ]]; then + local ns_pid1 + local ns_self + ns_pid1=$(readlink /proc/1/ns/pid 2>/dev/null) + ns_self=$(readlink /proc/self/ns/pid 2>/dev/null) + + if [[ -n "$ns_pid1" ]] && [[ -n "$ns_self" ]] && [[ "$ns_pid1" != "$ns_self" ]]; then + log_message "Conteneur détecté: namespace PID différent (PID1: $ns_pid1, Self: $ns_self)" "DETECT" + return 0 + fi + fi + + # 6. VÉRIFICATION DES PROCESSUS DE GESTION DE CONTENEURS + if ps -eo comm 2>/dev/null | grep -qE "^(containerd|dockerd|runc|crun|podman|CRIO)$"; then + # Si un gestionnaire de conteneurs tourne, vérifier si nous sommes dans un conteneur + if [[ "$(unshare --pid --fork --mount-proc readlink /proc/1/exe 2>/dev/null)" != "/sbin/init" ]]; then + log_message "Conteneur détecté: gestionnaire de conteneurs actif" "DETECT" + return 0 + fi + fi + + # 7. VÉRIFICATION DE L'INIT (PID 1) + local init_cmd + init_cmd=$(ps -p 1 -o comm= 2>/dev/null || cat /proc/1/comm 2>/dev/null) + case "$init_cmd" in + "systemd"|"init"|"upstart") + # Init système normal - probablement pas un conteneur + ;; + "containerd-shim"|"runc"|"docker-init"|"tini") + log_message "Conteneur détecté: init spécial: $init_cmd" "DETECT" + return 0 + ;; + esac + + # 8. FICHIERS SPÉCIFIQUES À CERTAINS CONTENEURS + [[ -f /run/.containerenv ]] && { log_message "Conteneur détecté: /run/.containerenv" "DETECT"; return 0; } + [[ -d /run/host ]] && { + # /run/host existe souvent dans les conteneurs systemd-nspawn + if ! mountpoint -q /run/host 2>/dev/null; then + log_message "Conteneur détecté: /run/host présent" "DETECT" + return 0 + fi + } + + # 9. VOLUMES DOCKER SPÉCIFIQUES (optionnel) + if [[ -d /var/lib/docker ]] && mountpoint -q /var/lib/docker 2>/dev/null; then + # /var/lib/docker est monté - pourrait être un daemon Docker + log_message "Docker détecté via /var/lib/docker monté" "DETECT" + # Note: Ceci détecte le daemon Docker, pas forcément qu'on est dans un conteneur + fi + + # Aucune détection positive + log_message "Aucun conteneur détecté" "DETECT" + return 1 } detect_lxc() { - grep -q "lxc" /proc/self/cgroup 2>/dev/null || [[ -d /dev/lxc ]] + # Détection spécifique LXC + [[ -d /dev/lxc ]] && return 0 + [[ -f /.lxcpid ]] && return 0 + grep -q "lxc" /proc/1/cgroup 2>/dev/null && return 0 + [[ "$(systemd-detect-virt 2>/dev/null)" == "lxc" ]] && return 0 + return 1 } backup_file() { @@ -649,6 +779,88 @@ get_ssh_port_to_use() { # FONCTIONS DE DURCISSEMENT (COMPLÈTES) # ============================================================================== +configure_banners() { + local step_name="configure_banners" + if check_step_done "$step_name"; then + skip_step "${STEP_DESCRIPTIONS[$step_name]}" + return 0 + fi + + print_step "Configuration des bannières légales" + + # Bannière pour les connexions locales (login) + local issue_banner=" +╔══════════════════════════════════════════════════════════════════╗ +║ SYSTÈME SÉCURISÉ ║ +║ ║ +║ Accès strictement réservé aux personnes autorisées. ║ +║ Toute tentative d'accès non autorisé est strictement interdite ║ +║ et fera l'objet de poursuites judiciaires. ║ +║ ║ +║ Toutes les activités sur ce système sont surveillées et ║ +║ enregistrées conformément à la réglementation en vigueur. ║ +║ ║ +║ Date et heure: \$(date) ║ +║ Système: \$(hostname) (\$(uname -o) \$(uname -r)) ║ +╚══════════════════════════════════════════════════════════════════╝ +" + + # Bannière pour SSH (issue.net) + local issue_net_banner=" +╔══════════════════════════════════════════════════════════════════╗ +║ SYSTÈME SÉCURISÉ ║ +║ ║ +║ ATTENTION: Accès restreint aux utilisateurs autorisés ║ +║ ║ +║ Vous accédez à un système informatique protégé. ║ +║ Toute utilisation non autorisée est interdite et ║ +║ peut faire l'objet de poursuites. ║ +║ ║ +║ En vous connectant, vous acceptez que votre session ║ +║ soit surveillée et enregistrée. ║ +║ ║ +║ Système: \$(hostname) ║ +║ Adresse IP: \$(who am i | awk '{print \$5}' | sed 's/[()]//g') ║ +╚══════════════════════════════════════════════════════════════════╝ +" + + # Bannière pour motd (Message of the Day) + local motd_banner=" +======================================================================== + SYSTÈME SÉCURISÉ +------------------------------------------------------------------------ +Ce système est configuré pour une haute sécurité. +Toutes les connexions sont tracées et enregistrées. + +Dernière mise à jour sécurité: $(date +%Y-%m-%d) +Connexions actives: $(who | wc -l) +Uptime: $(uptime -p) +======================================================================== +" + + # Appliquer les bannières + print_info "Configuration de /etc/issue (login local)..." + echo "$issue_banner" > /etc/issue + chmod 644 /etc/issue + + print_info "Configuration de /etc/issue.net (SSH)..." + echo "$issue_net_banner" > /etc/issue.net + chmod 644 /etc/issue.net + + print_info "Configuration de /etc/motd (Message du jour)..." + echo "$motd_banner" > /etc/motd + chmod 644 /etc/motd + + # Désactiver les motd dynamiques si existants + if [[ -d /etc/update-motd.d ]]; then + print_info "Désactivation des motd dynamiques..." + chmod -x /etc/update-motd.d/* 2>/dev/null || true + fi + + print_success "Bannières légales configurées" + mark_step_done "$step_name" +} + install_security_tools() { local step_name="install_security_tools" if check_step_done "$step_name"; then @@ -1401,72 +1613,148 @@ harden_ssh() { backup_file "/etc/ssh/sshd_config" local sshd_config="/etc/ssh/sshd_config" - local ssh_port=$(get_ssh_port_to_use) - # Fonction utilitaire pour mettre à jour les paramètres SSH - update_ssh_param() { - local param="$1" - local value="$2" - sed -i "/^#*${param}[[:space:]]/d" "$sshd_config" - echo "$param $value" >> "$sshd_config" - } - - # Configuration de base - update_ssh_param "Port" "$ssh_port" - - # Si c'est un nouveau port et pas en LXC, garder le port 22 temporairement - if [[ "$ssh_port" != "22" ]] && ! detect_lxc; then - update_ssh_param "Port" "22" - update_ssh_param "Port" "$ssh_port" - print_info "Port 22 maintenu temporairement avec port $ssh_port" - fi - - # Authentification - if [[ "$AUTO_DISABLE_ROOT_LOGIN" == "yes" ]]; then - update_ssh_param "PermitRootLogin" "no" - print_info "Connexion root SSH désactivée" + # DÉTERMINER LE PORT SSH À UTILISER + local ssh_port="22" # Par défaut + if detect_lxc; then + print_info "Conteneur LXC détecté - utilisation du port 22" + ssh_port="22" else - update_ssh_param "PermitRootLogin" "prohibit-password" - print_info "Connexion root SSH autorisée uniquement par clé" + ssh_port="$AUTO_SSH_PORT" + # Valider que le port est un nombre entre 1 et 65535 + if ! [[ "$ssh_port" =~ ^[0-9]+$ ]] || [ "$ssh_port" -lt 1 ] || [ "$ssh_port" -gt 65535 ]; then + print_warning "Port SSH invalide ($ssh_port) - utilisation du port 22" + ssh_port="22" + fi fi - # Paramètres de sécurité - update_ssh_param "PasswordAuthentication" "no" - update_ssh_param "PubkeyAuthentication" "yes" - update_ssh_param "PermitEmptyPasswords" "no" - update_ssh_param "X11Forwarding" "no" - update_ssh_param "MaxAuthTries" "3" - update_ssh_param "ClientAliveInterval" "300" - update_ssh_param "ClientAliveCountMax" "2" - update_ssh_param "LoginGraceTime" "60" - update_ssh_param "MaxSessions" "2" - update_ssh_param "TCPKeepAlive" "no" - update_ssh_param "AllowAgentForwarding" "no" - update_ssh_param "AllowTcpForwarding" "no" - update_ssh_param "LogLevel" "VERBOSE" - update_ssh_param "Compression" "no" - update_ssh_param "Protocol" "2" - update_ssh_param "UseDNS" "no" - update_ssh_param "IgnoreRhosts" "yes" - update_ssh_param "HostbasedAuthentication" "no" - update_ssh_param "PrintLastLog" "yes" - update_ssh_param "StrictModes" "yes" + print_info "Configuration SSH avec le port: $ssh_port" - # Algorithmes modernes - cat >> "$sshd_config" << 'EOF' -# Algorithmes de chiffrement modernes -Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr -MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com -KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256 -HostKeyAlgorithms ssh-ed25519,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256,rsa-sha2-256-cert-v01@openssh.com + # CRÉER UNE CONFIGURATION SSH SÉCURISÉE + # D'abord créer un fichier temporaire + local temp_config=$(mktemp) + + # Construire la configuration ligne par ligne pour éviter les problèmes de formatage + { + echo "# ============================================================================" + echo "# Configuration SSH sécurisée - Générée automatiquement $(date)" + echo "# ============================================================================" + echo "" + + # Section Ports + echo "# Ports d'écoute" + if [[ "$ssh_port" != "22" ]] && ! detect_lxc; then + echo "Port 22" + echo "Port $ssh_port" + echo "# Port 22 maintenu temporairement pour la transition" + else + echo "Port $ssh_port" + fi + echo "" + + echo "# Adresse d'écoute" + echo "ListenAddress 0.0.0.0" + echo "ListenAddress ::" + echo "" + + echo "# Authentification" + if [[ "$AUTO_DISABLE_ROOT_LOGIN" == "yes" ]]; then + echo "PermitRootLogin no" + print_info "Connexion root SSH désactivée" + else + echo "PermitRootLogin prohibit-password" + print_info "Connexion root SSH autorisée uniquement par clé" + fi + echo "PasswordAuthentication no" + echo "PubkeyAuthentication yes" + echo "PermitEmptyPasswords no" + echo "MaxAuthTries 3" + echo "LoginGraceTime 60" + echo "ClientAliveInterval 300" + echo "ClientAliveCountMax 2" + echo "MaxSessions 2" + echo "" + + echo "# Sécurité" + echo "Protocol 2" + echo "StrictModes yes" + echo "UseDNS no" + echo "IgnoreRhosts yes" + echo "HostbasedAuthentication no" + echo "PrintLastLog yes" + echo "" + + echo "# Redirections" + echo "X11Forwarding no" + echo "AllowAgentForwarding no" + echo "AllowTcpForwarding no" + echo "TCPKeepAlive no" + echo "" + + echo "# Performances et logs" + echo "Compression no" + echo "LogLevel VERBOSE" + echo "" + + echo "# Bannière" + echo "Banner /etc/issue.net" + echo "" + + echo "# Algorithmes de chiffrement modernes" + echo "Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr" + echo "MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com" + echo "KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256" + echo "HostKeyAlgorithms ssh-ed25519,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256,rsa-sha2-256-cert-v01@openssh.com" + echo "" + echo "# ============================================================================" + } > "$temp_config" + + # VÉRIFIER LE FICHIER TEMPORAIRE + print_info "Vérification du fichier temporaire..." + if ! sshd -t -f "$temp_config" 2>&1; then + print_error "Configuration temporaire invalide - affichage du contenu :" + cat -n "$temp_config" + print_info "Tentative avec configuration minimaliste..." + + # Configuration minimaliste pour LXC + cat > "$temp_config" << EOF +Port 22 +Protocol 2 +PermitRootLogin prohibit-password +PasswordAuthentication no +PubkeyAuthentication yes +PermitEmptyPasswords no +X11Forwarding no +AllowTcpForwarding no +AllowAgentForwarding no +ChallengeResponseAuthentication no +UsePAM yes +PrintLastLog yes +TCPKeepAlive no +ClientAliveInterval 300 +ClientAliveCountMax 2 EOF + + if ! sshd -t -f "$temp_config" 2>&1; then + print_error "Configuration minimaliste aussi invalide" + rm -f "$temp_config" + return 1 + fi + fi - # Bannière - update_ssh_param "Banner" "/etc/issue.net" + # COPIER LA CONFIGURATION + cp "$temp_config" "$sshd_config" + chmod 600 "$sshd_config" + chown root:root "$sshd_config" + rm -f "$temp_config" - # Test et application - print_info "Test de la configuration SSH..." - if sshd -t 2>&1 | tee -a "$LOG_FILE"; then + # AFFICHER LES PREMIÈRES LIGNES POUR VÉRIFICATION + print_info "Premières lignes de la configuration :" + head -n 15 "$sshd_config" | cat -n + + # TEST FINAL ET APPLICATION + print_info "Test final de la configuration SSH..." + if sshd -t -f "$sshd_config" 2>&1 | tee -a "$LOG_FILE"; then print_success "Configuration SSH valide" print_info "Redémarrage du service SSH..." @@ -1476,12 +1764,10 @@ EOF if [[ "$ssh_port" != "22" ]] && ! detect_lxc; then print_warning "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" print_warning "⚠ Port SSH: $ssh_port (22 temporairement actif)" - if [[ "$AUTO_CLEANUP_SSH" == "yes" ]] || auto_confirm; then - print_info "Nettoyage automatique du port 22 programmé" - else - print_warning "Testez: ssh -p $ssh_port $(whoami)@$(hostname -I | awk '{print $1}')" - fi + print_warning "Testez: ssh -p $ssh_port $(whoami)@localhost" print_warning "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + else + print_info "Port SSH: $ssh_port" fi else print_error "Échec redémarrage SSH - restauration..." @@ -1490,7 +1776,9 @@ EOF return 1 fi else - print_error "Configuration SSH invalide" + print_error "Configuration SSH invalide après copie" + print_info "Contenu complet du fichier :" + cat -n "$sshd_config" cp "${BACKUP_DIR}/sshd_config" /etc/ssh/sshd_config return 1 fi @@ -1498,31 +1786,6 @@ EOF mark_step_done "$step_name" } -configure_banners() { - local step_name="configure_banners" - if check_step_done "$step_name"; then - skip_step "${STEP_DESCRIPTIONS[$step_name]}" - return 0 - fi - - print_step "Configuration des bannières légales" - - local banner_text="╔════════════════════════════════════════════════════════════╗ -║ SYSTÈME SÉCURISÉ ║ -║ ║ -║ Accès réservé aux personnes autorisées uniquement. ║ -║ Toute tentative d'accès non autorisée est interdite ║ -║ et sera tracée et poursuivie selon la loi. ║ -║ ║ -║ Les activités sont surveillées et enregistrées. ║ -╚════════════════════════════════════════════════════════════╝" - - echo "$banner_text" | tee /etc/issue /etc/issue.net /etc/motd > /dev/null - - print_success "Bannières légales configurées" - mark_step_done "$step_name" -} - configure_firewall_ports() { local step_name="configure_firewall_ports" if check_step_done "$step_name"; then @@ -1840,51 +2103,131 @@ verify_packages_integrity() { print_step "Vérification de l'intégrité des paquets" print_info "Vérification avec debsums..." - debsums -s 2>&1 | tee -a "$LOG_FILE" || print_warning "Certains paquets ont des erreurs" - print_success "Vérification d'intégrité terminée" - mark_step_done "$step_name" -} - -configure_automatic_updates() { - local step_name="configure_automatic_updates" - if check_step_done "$step_name"; then - skip_step "${STEP_DESCRIPTIONS[$step_name]}" + # Vérifier si debsums est installé + if ! command -v debsums > /dev/null 2>&1; then + print_warning "debsums n'est pas installé - installation..." + DEBIAN_FRONTEND=noninteractive apt-get install -y -qq debsums 2>/dev/null || { + print_error "Impossible d'installer debsums" + mark_step_done "$step_name" + return 0 + } + fi + + # Vérifier si debsums peut s'exécuter + if ! debsums --help > /dev/null 2>&1; then + print_warning "debsums ne fonctionne pas correctement" + mark_step_done "$step_name" return 0 fi - print_step "Configuration des mises à jour automatiques" + if detect_container; then + print_info "Conteneur détecté - filtrage des faux positifs courants..." + + # Liste des fichiers communément modifiés dans les conteneurs (faux positifs) + local ignore_patterns=( + "/lib/systemd/system/container-getty@.service" + "/etc/hosts" + "/etc/resolv.conf" + "/etc/hostname" + "/etc/mtab" + "/etc/localtime" + "/etc/timezone" + "/etc/machine-id" + "/var/lib/dbus/machine-id" + ) + + # Construire l'expression grep pour ignorer ces fichiers + local grep_pattern=$(printf "|%s" "${ignore_patterns[@]}") + grep_pattern=${grep_pattern:1} # Enlever le premier | + + # Exécuter debsums en filtrant les faux positifs + local debsums_output + debsums_output=$(debsums -s 2>&1 || true) + + if [[ -n "$debsums_output" ]]; then + # Filtrer les faux positifs et les erreurs de permission + local filtered_output + filtered_output=$(echo "$debsums_output" | grep -vE "$grep_pattern" | grep -v "Permission denied" || true) + + if [[ -n "$filtered_output" ]]; then + echo "$filtered_output" | tee -a "$LOG_FILE" + + # Compter les vraies erreurs + local real_errors + real_errors=$(echo "$filtered_output" | wc -l) + + if [[ $real_errors -gt 0 ]]; then + print_warning "$real_errors paquet(s) avec des erreurs réelles" + + # Lister les paquets problématiques + echo "" + echo "Paquets concernés:" + echo "$filtered_output" | awk -F: '{print $1}' | sort -u + + # Proposer une réparation en mode interactif + if ! auto_confirm; then + echo "" + read -p "Voulez-vous tenter de réparer les paquets corrompus ? (o/N): " -r repair + if [[ "$repair" =~ ^[Oo]$ ]]; then + print_info "Réparation des paquets..." + local packages_to_reinstall + packages_to_reinstall=$(echo "$filtered_output" | awk -F: '{print $1}' | sort -u | tr '\n' ' ') + if [[ -n "$packages_to_reinstall" ]]; then + apt-get install --reinstall -y $packages_to_reinstall 2>&1 | tee -a "$LOG_FILE" || \ + print_warning "Certains paquets n'ont pas pu être réinstallés" + fi + fi + fi + else + print_success "Aucune erreur réelle détectée (faux positifs filtrés)" + fi + else + print_success "Aucune modification détectée (après filtrage)" + fi + else + print_success "Aucune modification détectée" + fi + else + # Système hôte - vérification complète + local debsums_output + debsums_output=$(debsums -s 2>&1 || true) + + # Filtrer les erreurs de permission + local filtered_output + filtered_output=$(echo "$debsums_output" | grep -v "Permission denied" || true) + + if [[ -n "$filtered_output" ]]; then + echo "$filtered_output" | tee -a "$LOG_FILE" + + print_warning "Certains paquets ont des erreurs" + + # Lister les paquets problématiques + echo "" + echo "Paquets concernés:" + echo "$filtered_output" | awk -F: '{print $1}' | sort -u + + # Proposer une réparation + if ! auto_confirm; then + echo "" + read -p "Voulez-vous tenter de réparer ces paquets ? (o/N): " -r repair + if [[ "$repair" =~ ^[Oo]$ ]]; then + print_info "Réparation des paquets..." + local packages_to_reinstall + packages_to_reinstall=$(echo "$filtered_output" | awk -F: '{print $1}' | sort -u | tr '\n' ' ') + if [[ -n "$packages_to_reinstall" ]]; then + apt-get install --reinstall -y $packages_to_reinstall 2>&1 | tee -a "$LOG_FILE" || \ + print_warning "Certains paquets n'ont pas pu être réinstallés" + fi + fi + fi + else + print_success "Tous les paquets sont intacts" + fi + fi - # Configuration unattended-upgrades - cat > /etc/apt/apt.conf.d/50unattended-upgrades << 'EOF' -Unattended-Upgrade::Allowed-Origins { - "${distro_id}:${distro_codename}"; - "${distro_id}:${distro_codename}-security"; - "${distro_id}ESM:${distro_codename}"; -}; -Unattended-Upgrade::Origins-Pattern { - "origin=Debian,codename=${distro_codename},label=Debian-Security"; -}; -Unattended-Upgrade::AutoFixInterruptedDpkg "true"; -Unattended-Upgrade::MinimalSteps "true"; -Unattended-Upgrade::Remove-Unused-Kernel-Packages "true"; -Unattended-Upgrade::Remove-Unused-Dependencies "true"; -Unattended-Upgrade::Automatic-Reboot "false"; -Unattended-Upgrade::Automatic-Reboot-Time "04:00"; -EOF - - # Configuration périodique - cat > /etc/apt/apt.conf.d/10periodic << 'EOF' -APT::Periodic::Update-Package-Lists "1"; -APT::Periodic::Download-Upgradeable-Packages "1"; -APT::Periodic::AutocleanInterval "7"; -APT::Periodic::Unattended-Upgrade "1"; -EOF - - print_success "Mises à jour automatiques configurées" mark_step_done "$step_name" } - configure_aide_cron() { local step_name="configure_aide_cron" if check_step_done "$step_name"; then @@ -2281,6 +2624,175 @@ cleanup_ssh_port() { fi } +configure_automatic_updates() { + local step_name="configure_automatic_updates" + if check_step_done "$step_name"; then + skip_step "${STEP_DESCRIPTIONS[$step_name]}" + return 0 + fi + + print_step "Configuration des mises à jour automatiques" + + # Vérifier si unattended-upgrades est installé + if ! dpkg -l | grep -q "^ii.*unattended-upgrades"; then + print_info "Installation de unattended-upgrades..." + DEBIAN_FRONTEND=noninteractive apt-get update -qq + DEBIAN_FRONTEND=noninteractive apt-get install -y -qq unattended-upgrades apt-listchanges needrestart 2>/dev/null || { + print_warning "Échec installation complète - continuation avec ce qui est disponible" + } + fi + + # Configuration principale de unattended-upgrades + backup_file "/etc/apt/apt.conf.d/50unattended-upgrades" + + cat > /etc/apt/apt.conf.d/50unattended-upgrades << 'EOF' +// Configuration des mises à jour automatiques sécurisées +Unattended-Upgrade::Allowed-Origins { + "${distro_id}:${distro_codename}"; + "${distro_id}:${distro_codename}-security"; + "${distro_id}:${distro_codename}-updates"; + "${distro_id}ESM:${distro_codename}"; + "${distro_id}ESM-Apps:${distro_codename}"; +}; + +// Options de comportement +Unattended-Upgrade::AutoFixInterruptedDpkg "true"; +Unattended-Upgrade::MinimalSteps "true"; +Unattended-Upgrade::Remove-Unused-Kernel-Packages "true"; +Unattended-Upgrade::Remove-Unused-Dependencies "true"; +Unattended-Upgrade::Remove-New-Unused-Dependencies "true"; +Unattended-Upgrade::Automatic-Reboot "false"; +Unattended-Upgrade::Automatic-Reboot-Time "04:00"; +Unattended-Upgrade::Automatic-Reboot-WithUsers "false"; + +// Nettoyage automatique +Unattended-Upgrade::Remove-Unused-Kernel-Packages "true"; +Unattended-Upgrade::Remove-Unused-Dependencies "true"; + +// Notifications par email (si postfix/sendmail est installé) +Unattended-Upgrade::Mail "root"; +Unattended-Upgrade::MailOnlyOnError "true"; +Unattended-Upgrade::MailReport "on-change"; + +// Téléchargement et installation +Unattended-Upgrade::Download-Upgradeable-Packages "true"; +Unattended-Upgrade::DevRelease "auto"; + +// Mise à jour du GRUB si nécessaire +Unattended-Upgrade::Update-Boot-App "true"; +EOF + + # Configuration périodique pour APT (ne pas essayer de sauvegarder si le fichier n'existe pas) + if [[ -f "/etc/apt/apt.conf.d/10periodic" ]]; then + backup_file "/etc/apt/apt.conf.d/10periodic" + fi + + cat > /etc/apt/apt.conf.d/10periodic << 'EOF' +// Planification des tâches APT automatiques +APT::Periodic::Update-Package-Lists "1"; // Mettre à jour les listes quotidiennement +APT::Periodic::Download-Upgradeable-Packages "1"; // Télécharger les paquets upgradables +APT::Periodic::AutocleanInterval "7"; // Nettoyer le cache tous les 7 jours +APT::Periodic::Unattended-Upgrade "1"; // Exécuter unattended-upgrade +APT::Periodic::Verbose "0"; // Mode silencieux +APT::Periodic::RandomSleep "300"; // Délai aléatoire pour éviter la charge simultanée +EOF + + # Configuration pour apt-listchanges (uniquement si le fichier existe) + if [[ -f "/etc/apt/listchanges.conf" ]]; then + backup_file "/etc/apt/listchanges.conf" + sed -i 's/^frontend=.*/frontend=mail/' /etc/apt/listchanges.conf 2>/dev/null || true + sed -i 's/^which=.*/which=both/' /etc/apt/listchanges.conf 2>/dev/null || true + sed -i 's/^email_address=.*/email_address=root/' /etc/apt/listchanges.conf 2>/dev/null || true + sed -i 's/^confirm=.*/confirm=0/' /etc/apt/listchanges.conf 2>/dev/null || true + else + # Créer une configuration minimale si le fichier n'existe pas + cat > /etc/apt/listchanges.conf << 'EOF' 2>/dev/null || true +[apt] +frontend=mail +email_address=root +confirm=0 +save_seen=/var/lib/apt/listchanges.db +which=both +EOF + fi + + # Configuration pour needrestart (gestion conditionnelle) + if [[ -f "/etc/needrestart/needrestart.conf" ]]; then + backup_file "/etc/needrestart/needrestart.conf" + sed -i 's/^#\$nrconf{restart}.*/\$nrconf{restart} = '"'a'"';/' /etc/needrestart/needrestart.conf 2>/dev/null || true + sed -i 's/^#\$nrconf{override_rc}.*/\$nrconf{override_rc} = '"'0'"';/' /etc/needrestart/needrestart.conf 2>/dev/null || true + else + # Créer le répertoire et le fichier si nécessaire + mkdir -p /etc/needrestart/ 2>/dev/null || true + cat > /etc/needrestart/needrestart.conf << 'EOF' 2>/dev/null || true +# Configuration de needrestart +$nrconf{restart} = 'a'; # Redémarrer automatiquement les services +$nrconf{override_rc} = 0; # Ne pas modifier les scripts rc +$nrconf{html_browser} = ''; # Pas de navigateur HTML +EOF + fi + + # Configuration du timer systemd (optionnel) + if command -v systemctl >/dev/null 2>&1; then + # Créer le répertoire pour les overrides + mkdir -p /etc/systemd/system/apt-daily.timer.d/ 2>/dev/null || true + + # Ne pas essayer de sauvegarder un fichier qui n'existe pas + if [[ -f "/etc/systemd/system/apt-daily.timer" ]]; then + backup_file "/etc/systemd/system/apt-daily.timer" + fi + + # Créer l'override + cat > /etc/systemd/system/apt-daily.timer.d/override.conf << 'EOF' 2>/dev/null || true +[Timer] +OnCalendar= +OnCalendar=*-*-* 3:00 +RandomizedDelaySec=1h +EOF + + systemctl daemon-reload 2>/dev/null || true + fi + + # Activer le service + print_info "Activation du service unattended-upgrades..." + + if systemctl is-enabled unattended-upgrades >/dev/null 2>&1; then + systemctl restart unattended-upgrades 2>&1 | tee -a "$LOG_FILE" || true + print_info "Service unattended-upgrades redémarré" + else + systemctl enable unattended-upgrades 2>&1 | tee -a "$LOG_FILE" || true + systemctl start unattended-upgrades 2>&1 | tee -a "$LOG_FILE" || true + print_info "Service unattended-upgrades activé et démarré" + fi + + # Vérification simple + sleep 2 + + if systemctl is-active unattended-upgrades >/dev/null 2>&1; then + print_success "Service unattended-upgrades actif" + else + print_warning "Service unattended-upgrades inactif (démarrage différé)" + fi + + # Tester la configuration (optionnel) + if command -v unattended-upgrade >/dev/null 2>&1; then + print_info "Test de configuration unattended-upgrades..." + unattended-upgrade --dry-run --debug 2>&1 | grep -i "checking\|result" | head -5 | tee -a "$LOG_FILE" || true + fi + + # Afficher un résumé + echo "" + print_info "Configuration des mises à jour automatiques:" + echo " ✓ Configuration unattended-upgrades appliquée" + echo " ✓ Planification quotidienne configurée" + echo " ✓ Mises à jour de sécurité activées" + echo " ✓ Nettoyage automatique activé" + echo "" + + print_success "Mises à jour automatiques configurées" + mark_step_done "$step_name" +} + # ============================================================================== # FONCTIONS PRINCIPALES # ==============================================================================