#!/bin/bash ################################################################################ # Script: system_hardening_optimized.sh # Version: 6.0 # Date: $(date +%Y-%m-%d) # Author: Security Team # Description: Système de durcissement sécurité pour Debian/Ubuntu LTS # License: GPLv3 ################################################################################ set -euo pipefail # ============================================================================== # CONFIGURATION # ============================================================================== readonly LOG_FILE="/var/log/system_hardening.log" readonly STATUS_FILE="/var/log/hardening_status.log" readonly BACKUP_DIR="/root/backup_hardening_$(date +%Y%m%d_%H%M%S)" readonly SECURITY_REPORT="/var/log/security_report_$(date +%Y%m%d).log" readonly NEW_SSH_PORT=22022 TOTAL_STEPS=30 CURRENT_STEP=1 # ============================================================================== # COULEURS # ============================================================================== readonly RED='\033[0;31m' readonly GREEN='\033[0;32m' readonly YELLOW='\033[1;33m' readonly CYAN='\033[0;36m' readonly BLUE='\033[0;34m' readonly MAGENTA='\033[0;35m' readonly NC='\033[0m' # ============================================================================== # FONCTIONS UTILITAIRES # ============================================================================== log_message() { local message="$1" local level="${2:-INFO}" echo -e "[$(date +'%Y-%m-%d %H:%M:%S')] [$level] $message" | tee -a "$LOG_FILE" } print_step() { local step_title="$1" echo -e "\n${YELLOW}╔══════════════════════════════════════════════════════════════╗${NC}" echo -e "${YELLOW} ÉTAPE ${CURRENT_STEP}/${TOTAL_STEPS}: $step_title${NC}" echo -e "${YELLOW}╚══════════════════════════════════════════════════════════════╝${NC}" log_message "Début étape $CURRENT_STEP: $step_title" "STEP" CURRENT_STEP=$((CURRENT_STEP + 1)) } print_success() { echo -e "${GREEN}✓${NC} $1" log_message "$1" "SUCCESS" } print_warning() { echo -e "${YELLOW}⚠${NC} $1" log_message "$1" "WARNING" } print_error() { echo -e "${RED}✗${NC} $1" >&2 log_message "$1" "ERROR" } print_info() { echo -e "${BLUE}ℹ${NC} $1" log_message "$1" "INFO" } check_step_done() { grep -q "^${1}$" "$STATUS_FILE" 2>/dev/null } mark_step_done() { echo "$1" >> "$STATUS_FILE" log_message "Étape '$1' marquée comme terminée" "STATUS" } skip_step() { print_info "Étape déjà effectuée : $1" CURRENT_STEP=$((CURRENT_STEP + 1)) } detect_container() { grep -qE "lxc|container" /proc/self/cgroup 2>/dev/null || \ [[ -f /.dockerenv ]] || [[ -d /dev/lxc ]] } backup_file() { local file_path="$1" [[ -f "$file_path" ]] && { mkdir -p "$BACKUP_DIR" cp "$file_path" "${BACKUP_DIR}/" log_message "Sauvegarde de $file_path" "BACKUP" } } add_unique_line() { local line="$1" local file="$2" grep -qF "$line" "$file" 2>/dev/null || { echo "$line" >> "$file" log_message "Ajout ligne: '$line' -> $file" "CONFIG" } } update_config_value() { local file="$1" local key="$2" local value="$3" backup_file "$file" if grep -q "^${key}" "$file"; then sed -i "s/^${key}.*/${key} ${value}/" "$file" else echo "${key} ${value}" >> "$file" fi } # ============================================================================== # FONCTIONS DE DURCISSEMENT # ============================================================================== # ÉTAPE 1: Mise à jour système et installation outils install_security_tools() { local step_name="install_security_tools" check_step_done "$step_name" && { skip_step "$step_name"; return 0; } print_step "Mise à jour système et installation outils de sécurité" print_info "Mise à jour des dépôts..." DEBIAN_FRONTEND=noninteractive apt-get update -qq print_info "Mise à jour du système..." DEBIAN_FRONTEND=noninteractive apt-get upgrade -y -qq local packages="lynis aide aide-common fail2ban ufw libpam-pwquality apt-listchanges \ apt-listbugs needrestart clamav clamav-daemon chrony chkrootkit \ libpam-tmpdir debsums unattended-upgrades" detect_container || packages+=" acct" print_info "Installation des paquets de sécurité..." DEBIAN_FRONTEND=noninteractive apt-get install -y -qq $packages print_success "Système mis à jour et outils installés" mark_step_done "$step_name" } # ÉTAPE 2: Configuration Process Accounting configure_process_accounting() { local step_name="configure_process_accounting" check_step_done "$step_name" && { skip_step "$step_name"; return 0; } print_step "Configuration du Process Accounting" if detect_container; then print_warning "Conteneur détecté - Process Accounting désactivé" for service in acct.service psacct.service; do systemctl list-unit-files | grep -q "^${service}" && { systemctl stop "$service" 2>/dev/null || true systemctl disable "$service" 2>/dev/null || true systemctl mask "$service" 2>/dev/null || true } done else systemctl enable acct.service 2>/dev/null && \ systemctl start acct.service 2>/dev/null && \ print_success "Process Accounting activé" || \ print_warning "Process Accounting non disponible" fi mark_step_done "$step_name" } # ÉTAPE 3: Durcissement sysctl configure_sysctl_security() { local step_name="configure_sysctl_security" check_step_done "$step_name" && { skip_step "$step_name"; return 0; } print_step "Durcissement des paramètres noyau (sysctl)" cat > /etc/sysctl.d/99-security-hardening.conf << 'EOF' # Sécurité réseau IPv4 net.ipv4.conf.all.accept_source_route = 0 net.ipv4.conf.all.accept_redirects = 0 net.ipv4.conf.all.send_redirects = 0 net.ipv4.conf.all.log_martians = 1 net.ipv4.conf.all.rp_filter = 1 net.ipv4.conf.default.accept_source_route = 0 net.ipv4.conf.default.accept_redirects = 0 net.ipv4.conf.default.log_martians = 1 net.ipv4.icmp_echo_ignore_broadcasts = 1 net.ipv4.icmp_ignore_bogus_error_responses = 1 net.ipv4.tcp_syncookies = 1 # Sécurité réseau IPv6 net.ipv6.conf.all.accept_redirects = 0 net.ipv6.conf.all.accept_source_route = 0 net.ipv6.conf.default.accept_redirects = 0 net.ipv6.conf.default.accept_source_route = 0 # Sécurité noyau kernel.randomize_va_space = 2 kernel.kptr_restrict = 2 kernel.dmesg_restrict = 1 kernel.yama.ptrace_scope = 1 kernel.unprivileged_bpf_disabled = 1 # Sécurité système fs.suid_dumpable = 0 fs.protected_fifos = 2 fs.protected_regular = 2 fs.protected_symlinks = 1 fs.protected_hardlinks = 1 EOF sysctl -p /etc/sysctl.d/99-security-hardening.conf 2>/dev/null || \ print_warning "Certains paramètres sysctl ignorés" print_success "Paramètres sysctl appliqués" mark_step_done "$step_name" } # ÉTAPE 4: Configuration permissions logs configure_log_permissions() { local step_name="configure_log_permissions" check_step_done "$step_name" && { skip_step "$step_name"; return 0; } print_step "Configuration des permissions des fichiers de log" find /var/log -type f -exec chmod 640 {} \; 2>/dev/null || true [[ -f /var/log/auth.log ]] && chmod 600 /var/log/auth.log [[ -f /var/log/syslog ]] && chmod 600 /var/log/syslog chown root:adm /var/log/ 2>/dev/null || true chmod 750 /var/log/ 2>/dev/null || true print_success "Permissions des logs configurées" mark_step_done "$step_name" } # ÉTAPE 5: Politique mots de passe PAM configure_pam_password_policy() { local step_name="configure_pam_password_policy" check_step_done "$step_name" && { skip_step "$step_name"; return 0; } print_step "Configuration de la politique de mots de passe PAM" backup_file "/etc/security/pwquality.conf" cat >> /etc/security/pwquality.conf << 'EOF' # Configuration renforcée minlen = 14 minclass = 3 maxrepeat = 3 maxsequence = 3 dcredit = -1 ucredit = -1 lcredit = -1 ocredit = -1 difok = 3 EOF backup_file "/etc/pam.d/common-password" # Ajouter remember et sha512 sed -i 's/pam_unix.so.*/& remember=5 sha512 rounds=500000/' /etc/pam.d/common-password # Ajouter pwquality si absent grep -q "pam_pwquality.so" /etc/pam.d/common-password || \ sed -i '1i password requisite pam_pwquality.so retry=3' /etc/pam.d/common-password print_success "Politique PAM configurée" mark_step_done "$step_name" } # ÉTAPE 6: Configuration login.defs configure_login_defs() { local step_name="configure_login_defs" check_step_done "$step_name" && { skip_step "$step_name"; return 0; } print_step "Configuration des paramètres de connexion (login.defs)" backup_file "/etc/login.defs" update_config_value "/etc/login.defs" "PASS_MAX_DAYS" "90" update_config_value "/etc/login.defs" "PASS_MIN_DAYS" "7" update_config_value "/etc/login.defs" "PASS_WARN_AGE" "7" update_config_value "/etc/login.defs" "LOGIN_RETRIES" "3" update_config_value "/etc/login.defs" "LOGIN_TIMEOUT" "60" update_config_value "/etc/login.defs" "UMASK" "027" update_config_value "/etc/login.defs" "ENCRYPT_METHOD" "SHA512" update_config_value "/etc/login.defs" "SHA_CRYPT_MIN_ROUNDS" "500000" update_config_value "/etc/login.defs" "SHA_CRYPT_MAX_ROUNDS" "1000000" print_success "Configuration login.defs appliquée" mark_step_done "$step_name" } # ÉTAPE 7: Configuration umask global configure_umask() { local step_name="configure_umask" check_step_done "$step_name" && { skip_step "$step_name"; return 0; } print_step "Configuration de l'umask par défaut" for file in /etc/profile /etc/bash.bashrc; do backup_file "$file" sed -i '/^umask/d' "$file" add_unique_line "umask 027" "$file" done print_success "Umask configuré à 027" mark_step_done "$step_name" } # ÉTAPE 8: Configuration AIDE SHA512 configure_aide_sha512() { local step_name="configure_aide_sha512" check_step_done "$step_name" && { skip_step "$step_name"; return 0; } print_step "Configuration AIDE pour SHA512" backup_file "/etc/aide/aide.conf" grep -q "ALLXTRAHASHES" /etc/aide/aide.conf && \ sed -i 's/ALLXTRAHASHES.*=.*/ALLXTRAHASHES = sha512/' /etc/aide/aide.conf || \ echo "ALLXTRAHASHES = sha512" >> /etc/aide/aide.conf print_success "AIDE configuré pour SHA512" mark_step_done "$step_name" } # ÉTAPE 9: Initialisation AIDE initialize_aide_db() { local step_name="initialize_aide_db" check_step_done "$step_name" && { skip_step "$step_name"; return 0; } print_step "Initialisation de la base de données AIDE" print_info "Création de la base de données de référence (peut prendre plusieurs minutes)..." if command -v aideinit > /dev/null; then aideinit && print_success "Base de données AIDE initialisée via aideinit" || { print_error "Échec aideinit" return 1 } else aide --init --config /etc/aide/aide.conf && { [[ -f /var/lib/aide/aide.db.new ]] && { mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db print_success "Base de données AIDE initialisée" } || { print_error "Fichier de base de données non trouvé" return 1 } } || { print_error "Échec initialisation AIDE" return 1 } fi mark_step_done "$step_name" } # ÉTAPE 10: Configuration ClamAV configure_clamav() { local step_name="configure_clamav" check_step_done "$step_name" && { skip_step "$step_name"; return 0; } print_step "Configuration de ClamAV" print_info "Mise à jour de la base de données ClamAV..." systemctl stop clamav-freshclam 2>/dev/null || true freshclam || print_warning "Échec mise à jour ClamAV (normal si déjà récent)" systemctl enable clamav-freshclam systemctl start clamav-freshclam systemctl enable clamav-daemon 2>/dev/null || true systemctl start clamav-daemon 2>/dev/null || true print_success "ClamAV configuré" mark_step_done "$step_name" } # ÉTAPE 11: Configuration Chrony configure_chrony() { local step_name="configure_chrony" check_step_done "$step_name" && { skip_step "$step_name"; return 0; } print_step "Configuration de la synchronisation horaire (Chrony)" # Vérifier si on est dans un conteneur if detect_container; then print_warning "Conteneur détecté - synchronisation horaire gérée par l'hôte" print_info "La configuration de Chrony est ignorée en environnement conteneurisé" # Désactiver/masquer chrony s'il est présent if systemctl list-unit-files | grep -q "^chrony.service"; then systemctl stop chrony 2>/dev/null || true systemctl disable chrony 2>/dev/null || true systemctl mask chrony 2>/dev/null || true print_info "Service Chrony désactivé (non nécessaire en conteneur)" fi mark_step_done "$step_name" return 0 fi # Configuration pour système physique/VM backup_file "/etc/chrony/chrony.conf" # Configurer le fuseau horaire timedatectl set-timezone Europe/Paris || { print_warning "Impossible de définir le fuseau horaire" } cat > /etc/chrony/chrony.conf << 'EOF' # Serveurs NTP pool 2.debian.pool.ntp.org iburst server 0.fr.pool.ntp.org iburst server 1.fr.pool.ntp.org iburst # Fichiers de drift et log driftfile /var/lib/chrony/chrony.drift logdir /var/log/chrony # Restrictions makestep 1.0 3 rtcsync EOF # Activer et démarrer le service if systemctl enable chrony 2>/dev/null && systemctl restart chrony 2>/dev/null; then print_success "Chrony configuré et démarré" else print_warning "Erreur lors du démarrage de Chrony - service peut-être non disponible" fi mark_step_done "$step_name" } # ÉTAPE 12: Durcissement SSH harden_ssh() { local step_name="harden_ssh" check_step_done "$step_name" && { skip_step "$step_name"; return 0; } print_step "Durcissement du service SSH" backup_file "/etc/ssh/sshd_config" local sshd_config="/etc/ssh/sshd_config" # Fonction helper pour mettre à jour une valeur update_ssh_param() { local param="$1" local value="$2" local file="$3" # Supprimer toutes les occurrences (commentées ou non) sed -i "/^#*${param}/d" "$file" # Ajouter la nouvelle valeur echo "${param} ${value}" >> "$file" } # Configuration des ports sed -i '/^Port /d' "$sshd_config" sed -i '/^#Port /d' "$sshd_config" echo "Port 22" >> "$sshd_config" echo "Port $NEW_SSH_PORT" >> "$sshd_config" # Paramètres de sécurité avec la fonction helper update_ssh_param "PermitRootLogin" "yes" "$sshd_config" update_ssh_param "PasswordAuthentication" "no" "$sshd_config" update_ssh_param "PubkeyAuthentication" "yes" "$sshd_config" update_ssh_param "PermitEmptyPasswords" "no" "$sshd_config" update_ssh_param "X11Forwarding" "no" "$sshd_config" update_ssh_param "MaxAuthTries" "3" "$sshd_config" update_ssh_param "ClientAliveInterval" "300" "$sshd_config" update_ssh_param "ClientAliveCountMax" "2" "$sshd_config" update_ssh_param "LoginGraceTime" "60" "$sshd_config" update_ssh_param "MaxSessions" "2" "$sshd_config" update_ssh_param "TCPKeepAlive" "no" "$sshd_config" update_ssh_param "AllowAgentForwarding" "no" "$sshd_config" update_ssh_param "AllowTcpForwarding" "no" "$sshd_config" update_ssh_param "LogLevel" "VERBOSE" "$sshd_config" update_ssh_param "Compression" "no" "$sshd_config" update_ssh_param "Protocol" "2" "$sshd_config" # Algorithmes sécurisés (s'assurer qu'ils n'existent pas déjà) sed -i '/^Ciphers /d' "$sshd_config" sed -i '/^MACs /d' "$sshd_config" sed -i '/^KexAlgorithms /d' "$sshd_config" cat >> "$sshd_config" << 'EOF' # Algorithmes cryptographiques sécurisés 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,hmac-sha2-512,hmac-sha2-256 KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256 EOF # Bannière update_ssh_param "Banner" "/etc/issue.net" "$sshd_config" # Test de la configuration avant application print_info "Test de la configuration SSH..." if sshd -t 2>&1 | tee -a "$LOG_FILE"; then print_success "Configuration SSH valide" # Redémarrer le service print_info "Redémarrage du service SSH..." if systemctl restart sshd 2>/dev/null || systemctl restart ssh 2>/dev/null; then print_success "SSH durci avec succès" # Afficher les ports configurés local ports=$(grep "^Port" "$sshd_config" | awk '{print $2}' | tr '\n' ' ') print_info "Ports SSH actifs: $ports" print_warning "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" print_warning "⚠ TESTEZ IMMÉDIATEMENT dans un NOUVEAU terminal:" print_warning " ssh -p $NEW_SSH_PORT $(whoami)@$(hostname -I | awk '{print $1}')" print_warning " NE FERMEZ PAS cette session avant validation!" print_warning "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" else print_error "Échec redémarrage SSH" print_error "Restauration de la configuration..." cp "${BACKUP_DIR}/sshd_config" /etc/ssh/sshd_config systemctl restart sshd 2>/dev/null || systemctl restart ssh 2>/dev/null return 1 fi else print_error "Configuration SSH invalide - vérifiez les logs" print_error "Restauration de la sauvegarde..." cp "${BACKUP_DIR}/sshd_config" /etc/ssh/sshd_config return 1 fi mark_step_done "$step_name" } # ÉTAPE 13: Configuration bannières configure_banners() { local step_name="configure_banners" check_step_done "$step_name" && { skip_step "$step_name"; return 0; } 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" } # ÉTAPE 14: Configuration UFW configure_ufw_firewall() { local step_name="configure_ufw_firewall" check_step_done "$step_name" && { skip_step "$step_name"; return 0; } print_step "Configuration du pare-feu UFW" # Vérifier si on est dans un conteneur if detect_container; then print_warning "Conteneur détecté - pare-feu géré par l'hôte Proxmox" print_info "UFW ne peut pas être configuré dans un conteneur LXC" print_info "Les règles de pare-feu doivent être configurées au niveau de l'hôte" # Désactiver UFW s'il est actif if systemctl is-active --quiet ufw 2>/dev/null; then ufw --force disable 2>/dev/null || true systemctl stop ufw 2>/dev/null || true systemctl disable ufw 2>/dev/null || true systemctl mask ufw 2>/dev/null || true print_info "Service UFW désactivé (non nécessaire en conteneur)" fi print_success "Configuration pare-feu ignorée (environnement conteneurisé)" mark_step_done "$step_name" return 0 fi # Configuration pour système physique/VM backup_file "/etc/ufw/user.rules" backup_file "/etc/ufw/user6.rules" print_info "Réinitialisation des règles UFW..." ufw --force reset > /dev/null 2>&1 || { print_error "Impossible de réinitialiser UFW" mark_step_done "$step_name" return 1 } print_info "Configuration des politiques par défaut..." ufw default deny incoming ufw default allow outgoing # Ports SSH print_info "Autorisation des ports SSH..." ufw allow 22/tcp comment 'SSH temporaire' || print_warning "Erreur ajout règle SSH:22" ufw allow "${NEW_SSH_PORT}/tcp" comment 'SSH sécurisé' || print_warning "Erreur ajout règle SSH:${NEW_SSH_PORT}" # Limiter ICMP print_info "Configuration limitation ICMP..." if [[ -f /etc/ufw/before.rules ]]; then backup_file "/etc/ufw/before.rules" # Vérifier si la règle n'existe pas déjà if ! grep -q "Limiter pings" /etc/ufw/before.rules; then cat >> /etc/ufw/before.rules << 'EOF' # Limiter pings -A ufw-before-input -p icmp --icmp-type echo-request -m limit --limit 3/second --limit-burst 5 -j ACCEPT -A ufw-before-input -p icmp --icmp-type echo-request -j DROP EOF fi fi # Activer UFW print_info "Activation du pare-feu..." if echo "y" | ufw --force enable > /dev/null 2>&1; then print_success "Pare-feu UFW configuré et activé" else # Fallback if ufw enable <<< 'y' > /dev/null 2>&1; then print_success "Pare-feu UFW configuré et activé (fallback)" else print_warning "Échec activation UFW - continuer sans..." fi fi mark_step_done "$step_name" } # ÉTAPE 15: Configuration Fail2ban configure_fail2ban() { local step_name="configure_fail2ban" check_step_done "$step_name" && { skip_step "$step_name"; return 0; } print_step "Configuration de Fail2ban" backup_file "/etc/fail2ban/jail.conf" # Configuration adaptée selon l'environnement if detect_container; then print_warning "Conteneur détecté - configuration Fail2ban limitée" cat > /etc/fail2ban/jail.local << EOF [DEFAULT] bantime = 1h findtime = 10m maxretry = 3 ignoreip = 127.0.0.1/8 ::1 backend = systemd # Mode conteneur - pas d'action iptables banaction = %(banaction_allports)s banaction_allports = iptables-multiport [sshd] enabled = true port = 22,${NEW_SSH_PORT} filter = sshd logpath = /var/log/auth.log maxretry = 5 bantime = 1h # Action désactivée en conteneur (pas d'accès iptables) action = %(action_)s EOF print_info "Fail2ban configuré en mode conteneur (logging seul, sans bannissement iptables)" else # Configuration complète pour système physique/VM cat > /etc/fail2ban/jail.local << EOF [DEFAULT] bantime = 1h findtime = 10m maxretry = 3 ignoreip = 127.0.0.1/8 ::1 backend = systemd [sshd] enabled = true port = 22,${NEW_SSH_PORT} filter = sshd logpath = /var/log/auth.log maxretry = 5 bantime = 1h [apache-auth] enabled = true port = http,https logpath = /var/log/apache*/*error.log maxretry = 3 EOF print_info "Fail2ban configuré en mode complet avec bannissement iptables" fi # Activer et démarrer le service systemctl enable fail2ban 2>&1 | tee -a "$LOG_FILE" || true if systemctl restart fail2ban 2>&1 | tee -a "$LOG_FILE"; then print_success "Fail2ban configuré et démarré" # Vérifier le statut sleep 2 if systemctl is-active --quiet fail2ban; then print_info "Statut Fail2ban: $(systemctl is-active fail2ban)" else print_warning "Fail2ban démarré mais statut incertain" fi else print_warning "Problème au démarrage de Fail2ban - vérifiez les logs: journalctl -xeu fail2ban" fi mark_step_done "$step_name" } # ÉTAPE 16: Suppression paquets inutiles remove_unneeded_packages() { local step_name="remove_unneeded_packages" check_step_done "$step_name" && { skip_step "$step_name"; return 0; } print_step "Suppression des paquets inutiles" local packages_to_remove=(telnet rsh-client rsh-server netcat-openbsd netcat-traditional nis talk talkd) for package in "${packages_to_remove[@]}"; do dpkg -l | grep -q "^ii.*${package}" && { DEBIAN_FRONTEND=noninteractive apt-get purge -y -qq "$package" 2>/dev/null print_info "Paquet $package supprimé" } done DEBIAN_FRONTEND=noninteractive apt-get autoremove -y -qq print_success "Paquets inutiles supprimés" mark_step_done "$step_name" } # ÉTAPE 17: Restriction permissions restrict_file_permissions() { local step_name="restrict_file_permissions" check_step_done "$step_name" && { skip_step "$step_name"; return 0; } print_step "Restriction des permissions des fichiers critiques" chmod 644 /etc/passwd 2>/dev/null || true chmod 600 /etc/shadow 2>/dev/null || true chmod 644 /etc/group 2>/dev/null || true chmod 600 /etc/gshadow 2>/dev/null || true chmod 600 /etc/sudoers 2>/dev/null || true chmod 750 /etc/sudoers.d 2>/dev/null || true chmod 600 /boot/grub/grub.cfg 2>/dev/null || true chmod 644 /etc/ssh/ssh_config 2>/dev/null || true chmod 600 /etc/ssh/sshd_config 2>/dev/null || true print_success "Permissions des fichiers critiques restreintes" mark_step_done "$step_name" } # ÉTAPE 18: Désactivation modules noyau disable_risky_kernel_modules() { local step_name="disable_risky_kernel_modules" check_step_done "$step_name" && { skip_step "$step_name"; return 0; } print_step "Désactivation des modules noyau risqués" cat > /etc/modprobe.d/hardening-blacklist.conf << 'EOF' # Protocoles réseau non utilisés blacklist dccp install dccp /bin/true blacklist sctp install sctp /bin/true blacklist rds install rds /bin/true blacklist tipc install tipc /bin/true # Systèmes de fichiers non utilisés blacklist cramfs install cramfs /bin/true blacklist freevxfs install freevxfs /bin/true blacklist jffs2 install jffs2 /bin/true blacklist hfs install hfs /bin/true blacklist hfsplus install hfsplus /bin/true blacklist squashfs install squashfs /bin/true blacklist udf install udf /bin/true # FireWire (DMA attack) blacklist firewire-core install firewire-core /bin/true # Thunderbolt (DMA attack) blacklist thunderbolt install thunderbolt /bin/true EOF detect_container && \ print_warning "Conteneur - modules gérés par l'hôte" || \ print_info "Modules blacklistés" print_success "Modules noyau risqués désactivés" mark_step_done "$step_name" } # ÉTAPE 19: Configuration limites sécurité configure_security_limits() { local step_name="configure_security_limits" check_step_done "$step_name" && { skip_step "$step_name"; return 0; } print_step "Configuration des limites de sécurité" backup_file "/etc/security/limits.conf" cat >> /etc/security/limits.conf << 'EOF' # Désactiver core dumps * hard core 0 # Limiter les processus * soft nproc 512 * hard nproc 1024 # Limiter les fichiers ouverts * soft nofile 65536 * hard nofile 65536 EOF print_success "Limites de sécurité configurées" mark_step_done "$step_name" } # ÉTAPE 20: Vérification intégrité paquets verify_packages_integrity() { local step_name="verify_packages_integrity" check_step_done "$step_name" && { skip_step "$step_name"; return 0; } print_step "Vérification de l'intégrité des paquets" print_info "Vérification avec debsums (peut prendre du temps)..." 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" } # ÉTAPE 21: Configuration mises à jour auto configure_automatic_updates() { local step_name="configure_automatic_updates" check_step_done "$step_name" && { skip_step "$step_name"; return 0; } print_step "Configuration des mises à jour automatiques" cat > /etc/apt/apt.conf.d/50unattended-upgrades << 'EOF' Unattended-Upgrade::Allowed-Origins { "${distro_id}:${distro_codename}-security"; "${distro_id}:${distro_codename}-updates"; }; 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"; Unattended-Upgrade::Mail "root"; Unattended-Upgrade::MailReport "on-change"; EOF 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" } # ÉTAPE 22: Configuration tâche AIDE cron configure_aide_cron() { local step_name="configure_aide_cron" check_step_done "$step_name" && { skip_step "$step_name"; return 0; } print_step "Configuration des vérifications AIDE planifiées" cat > /etc/cron.weekly/aide-check << 'EOF' #!/bin/bash LOGFILE="/var/log/aide-check-$(date +%Y%m%d).log" if command -v aide > /dev/null; then echo "=== Vérification AIDE $(date) ===" > "$LOGFILE" if /usr/bin/aide --check >> "$LOGFILE" 2>&1; then echo "Vérification réussie" >> "$LOGFILE" if grep -q "added\|changed\|removed" "$LOGFILE"; then mail -s "[AIDE] Changements détectés sur $(hostname)" root < "$LOGFILE" fi else echo "Échec vérification" >> "$LOGFILE" mail -s "[AIDE] ALERTE - Échec sur $(hostname)" root < "$LOGFILE" fi echo "=== Fin vérification $(date) ===" >> "$LOGFILE" fi EOF chmod 750 /etc/cron.weekly/aide-check print_success "Vérification AIDE hebdomadaire configurée" mark_step_done "$step_name" } # ÉTAPE 23: Durcissement bannière SMTP harden_smtp_banner() { local step_name="harden_smtp_banner" check_step_done "$step_name" && { skip_step "$step_name"; return 0; } print_step "Durcissement bannière SMTP" if dpkg -l | grep -q "^ii.*postfix"; then backup_file "/etc/postfix/main.cf" postconf -e "smtpd_banner = \$myhostname ESMTP" postconf -e "disable_vrfy_command = yes" systemctl reload postfix 2>/dev/null || true print_success "Bannière Postfix durcie" elif dpkg -l | grep -q "^ii.*exim4"; then backup_file "/etc/exim4/exim4.conf.template" sed -i 's/^smtp_banner.*/smtp_banner = "${primary_hostname} ESMTP"/' \ /etc/exim4/exim4.conf.template 2>/dev/null || true update-exim4.conf 2>/dev/null || true print_success "Bannière Exim4 durcie" else print_info "Aucun serveur SMTP détecté" fi mark_step_done "$step_name" } # ÉTAPE 24: Durcissement services systemd harden_systemd_services() { local step_name="harden_systemd_services" check_step_done "$step_name" && { skip_step "$step_name"; return 0; } print_step "Durcissement services systemd" command -v systemd-analyze > /dev/null || { print_warning "systemd-analyze non disponible" mark_step_done "$step_name" return 0 } # Vérifier si on est dans un conteneur if detect_container; then print_warning "Conteneur détecté - durcissement systemd limité" print_info "Les namespaces systemd ne sont pas supportés dans les conteneurs LXC" print_info "Le durcissement systemd est ignoré pour éviter les erreurs de démarrage" # Nettoyer les éventuelles configurations précédentes qui pourraient causer des problèmes local services=(ssh sshd fail2ban chrony) for service in "${services[@]}"; do local unit="${service}.service" local override_dir="/etc/systemd/system/${unit}.d" if [[ -d "$override_dir" ]]; then print_info "Nettoyage des overrides systemd pour $unit" rm -rf "$override_dir" fi done systemctl daemon-reload print_success "Configuration systemd nettoyée pour compatibilité conteneur" mark_step_done "$step_name" return 0 fi # Configuration pour système physique/VM local services=(ssh sshd fail2ban chrony) local hardened=0 for service in "${services[@]}"; do local unit="${service}.service" # Vérifier si le service existe et est actif ou enabled systemctl list-unit-files | grep -q "^${unit}" || continue if ! systemctl is-enabled "$unit" > /dev/null 2>&1 && \ ! systemctl is-active "$unit" > /dev/null 2>&1; then print_info "Service $unit non actif - ignoré" continue fi mkdir -p "/etc/systemd/system/${unit}.d" # Configuration de sécurité adaptée selon le service if [[ "$service" == "ssh" || "$service" == "sshd" ]]; then # Configuration plus permissive pour SSH cat > "/etc/systemd/system/${unit}.d/security.conf" << EOF [Service] NoNewPrivileges=yes PrivateTmp=yes ProtectSystem=full ProtectHome=read-only ProtectKernelTunables=yes ProtectKernelModules=yes ProtectControlGroups=yes RestrictRealtime=yes EOF else # Configuration standard pour les autres services cat > "/etc/systemd/system/${unit}.d/security.conf" << EOF [Service] NoNewPrivileges=yes PrivateTmp=yes ProtectSystem=strict ProtectHome=yes ReadWritePaths=/var/log /var/lib/${service} /run/${service} ProtectKernelTunables=yes ProtectKernelModules=yes ProtectControlGroups=yes RestrictRealtime=yes RestrictSUIDSGID=yes EOF fi hardened=$((hardened + 1)) print_info "Service $unit durci" done # Recharger la configuration systemd systemctl daemon-reload # Vérifier que les services critiques démarrent toujours print_info "Vérification des services critiques..." for service in ssh sshd; do local unit="${service}.service" if systemctl list-unit-files | grep -q "^${unit}"; then if systemctl is-active --quiet "$unit" 2>/dev/null; then systemctl restart "$unit" 2>&1 | tee -a "$LOG_FILE" || { print_error "Échec redémarrage $unit - restauration configuration" rm -rf "/etc/systemd/system/${unit}.d" systemctl daemon-reload systemctl restart "$unit" } fi fi done print_success "$hardened service(s) systemd durci(s)" mark_step_done "$step_name" } # ÉTAPE 25: Configuration PAM avancée configure_advanced_pam() { local step_name="configure_advanced_pam" check_step_done "$step_name" && { skip_step "$step_name"; return 0; } print_step "Configuration PAM avancée" backup_file "/etc/pam.d/common-password" # S'assurer sha512 et rounds grep -q "pam_unix.so" /etc/pam.d/common-password && { grep -q "sha512" /etc/pam.d/common-password || \ sed -i 's/pam_unix.so.*/& sha512/' /etc/pam.d/common-password grep -q "rounds=" /etc/pam.d/common-password || \ sed -i 's/pam_unix.so.*/& rounds=500000/' /etc/pam.d/common-password } # Appliquer expiration mots de passe print_info "Application expiration mots de passe aux utilisateurs..." while IFS=: read -r user _ uid _ _ _ shell; do [[ "$uid" -ge 1000 || "$user" == "root" ]] && \ [[ -n "$shell" ]] && \ [[ "$shell" != *"nologin"* ]] && \ [[ "$shell" != *"false"* ]] && { chage -M 90 "$user" 2>/dev/null || true chage -d 0 "$user" 2>/dev/null || true print_info " → $user: Expiration 90 jours configurée" } done < /etc/passwd print_success "Configuration PAM avancée appliquée" mark_step_done "$step_name" } # ÉTAPE 26: Vérification partitions check_partition_layout() { local step_name="check_partition_layout" check_step_done "$step_name" && { skip_step "$step_name"; return 0; } print_step "Vérification disposition partitions" local partitions=(/home /tmp /var /var/log /var/log/audit) local warnings=() for partition in "${partitions[@]}"; do mount | grep -q " on ${partition} " && { local device=$(mount | grep " on ${partition} " | awk '{print $1}') print_info " ✓ $partition: Partition séparée ($device)" } || { warnings+=("$partition") print_warning " ⚠ $partition: Non monté séparément" } done [[ ${#warnings[@]} -eq 0 ]] && \ print_success "Toutes les partitions critiques séparées" || { print_warning "${#warnings[@]} partition(s) non séparée(s)" detect_container && \ print_info "Conteneur: partitions gérées par l'hôte" } mark_step_done "$step_name" } # ÉTAPE 27: Vérification fichiers noyau check_vmlinuz() { local step_name="check_vmlinuz" check_step_done "$step_name" && { skip_step "$step_name"; return 0; } print_step "Vérification fichiers noyau" local files=(/vmlinuz /boot/vmlinuz "/boot/vmlinuz-$(uname -r)" "/boot/initrd.img-$(uname -r)") local found=0 for kfile in "${files[@]}"; do [[ -f "$kfile" ]] && { found=$((found + 1)) print_info " ✓ $kfile ($(du -h "$kfile" | cut -f1))" } done [[ $found -eq 0 ]] && { print_warning "Aucun fichier noyau trouvé" detect_container && \ print_info "Conteneur: noyau géré par l'hôte" || { print_error "Système physique: fichiers noyau manquants!" apt-get install --reinstall "linux-image-$(uname -r)" 2>/dev/null || \ print_warning "Échec réinstallation noyau" } } || print_success "$found fichier(s) noyau trouvé(s)" mark_step_done "$step_name" } # ÉTAPE 28: Exécution chkrootkit run_chkrootkit() { local step_name="run_chkrootkit" check_step_done "$step_name" && { skip_step "$step_name"; return 0; } print_step "Exécution de chkrootkit" print_info "Scan rootkit en cours..." chkrootkit > "$BACKUP_DIR/chkrootkit_report.log" 2>&1 || \ print_warning "Chkrootkit a détecté des avertissements (voir $BACKUP_DIR/chkrootkit_report.log)" print_success "Scan chkrootkit terminé" mark_step_done "$step_name" } # ÉTAPE 29: Nettoyage port SSH 22 temporaire prepare_ssh_cleanup() { local step_name="prepare_ssh_cleanup" check_step_done "$step_name" && { skip_step "$step_name"; return 0; } print_step "Préparation nettoyage port SSH 22" print_warning "Port SSH 22 toujours actif (temporaire)" print_info "Après avoir testé SSH sur le port $NEW_SSH_PORT:" print_info "Exécutez: $0 --cleanup-ssh" mark_step_done "$step_name" } # ÉTAPE 30: Audit Lynis final run_lynis_audit() { local step_name="run_lynis_audit" check_step_done "$step_name" && { skip_step "$step_name"; return 0; } print_step "Exécution de l'audit Lynis" print_info "Audit système complet en cours (peut prendre plusieurs minutes)..." # Exécuter Lynis et sauvegarder le rapport lynis audit system --quick --no-colors > "$SECURITY_REPORT" 2>&1 # Extraire le score de durcissement local score=$(grep -i "Hardening index" "$SECURITY_REPORT" | grep -oP '\d+' | head -1) local max_score=100 # Score maximum standard Lynis # Si on ne trouve pas le score dans le rapport, utiliser une valeur par défaut [[ -z "$score" ]] && score=0 # Sauvegarder le score pour le résumé echo "$score" > "/tmp/lynis_score.txt" echo "$max_score" > "/tmp/lynis_max_score.txt" # Calculer le pourcentage pour la barre de progression local percentage=$((score * 100 / max_score)) local bar_length=50 # Longueur de la barre en caractères local filled_length=$((percentage * bar_length / 100)) local empty_length=$((bar_length - filled_length)) # Créer la barre de progression local bar="[" for ((i=0; i/dev/null || echo "0") local suggestions=$(grep -c "Suggestion" "$SECURITY_REPORT" 2>/dev/null || echo "0") local tests=$(grep -c "Test" "$SECURITY_REPORT" 2>/dev/null || echo "0") echo " • Tests effectués: $tests" echo " • Avertissements: $warnings" echo " • Suggestions: $suggestions" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" # Afficher les principales suggestions si score < 80 if [[ $score -lt 80 ]]; then echo "" print_info "Top 5 des suggestions prioritaires:" echo "┌──────────────────────────────────────────────────────┐" grep "Suggestion" "$SECURITY_REPORT" | head -5 | nl -w1 -s'. ' | while read -r line; do echo "│ ${line}" done echo "└──────────────────────────────────────────────────────┘" fi # Afficher les warnings critiques echo "" if [[ $warnings -gt 0 ]]; then print_warning "⚠ Avertissements critiques détectés:" echo "┌──────────────────────────────────────────────────────┐" grep "Warning" "$SECURITY_REPORT" | head -3 | nl -w1 -s'. ' | while read -r line; do echo "│ ${line}" done echo "└──────────────────────────────────────────────────────┘" fi echo "" print_info "Pour voir le rapport complet: cat $SECURITY_REPORT" print_info "Pour voir les suggestions: grep Suggestion $SECURITY_REPORT" mark_step_done "$step_name" } # ============================================================================== # FONCTION NETTOYAGE SSH # ============================================================================== cleanup_ssh_port() { print_step "Nettoyage définitif port SSH 22" echo -e "${RED}╔════════════════════════════════════════════════════════════╗${NC}" echo -e "${RED}║ ⚠ ATTENTION CRITIQUE ⚠ ║${NC}" echo -e "${RED}╚════════════════════════════════════════════════════════════╝${NC}" echo "" echo "Cette action va supprimer définitivement l'accès SSH sur le port 22." echo "Assurez-vous d'avoir testé SSH sur le port $NEW_SSH_PORT." echo "" echo -n "Confirmez-vous avoir testé avec succès SSH:$NEW_SSH_PORT ? (oui/non): " read -r confirmation [[ "$confirmation" != "oui" ]] && { print_error "Annulation. Testez d'abord: ssh -p $NEW_SSH_PORT user@$(hostname -I | awk '{print $1}')" return 1 } backup_file "/etc/ssh/sshd_config" sed -i '/^Port 22$/d' /etc/ssh/sshd_config ufw delete allow 22/tcp 2>/dev/null || true sshd -t && { systemctl restart sshd print_success "Port SSH 22 supprimé" print_info "SSH disponible uniquement sur le port $NEW_SSH_PORT" } || { print_error "Erreur configuration - restauration" cp "${BACKUP_DIR}/sshd_config" /etc/ssh/sshd_config systemctl restart sshd return 1 } } # ============================================================================== # FONCTIONS PRINCIPALES # ============================================================================== check_requirements() { print_info "Vérification des prérequis..." [[ $EUID -ne 0 ]] && { print_error "Ce script doit être exécuté en tant que root" exit 1 } [[ ! -f /etc/debian_version ]] && \ print_warning "Système non Debian/Ubuntu - compatibilité limitée" mkdir -p "$BACKUP_DIR" touch "$LOG_FILE" "$STATUS_FILE" 2>/dev/null || { print_error "Impossible de créer les fichiers de log" exit 1 } print_success "Prérequis vérifiés" } print_header() { clear echo -e "${CYAN}" echo "╔══════════════════════════════════════════════════════════════════╗" echo "║ SCRIPT DE DURCISSEMENT SYSTÈME OPTIMISÉ v6.0 ║" echo "║ Sécurité Debian/Ubuntu ║" echo "╚══════════════════════════════════════════════════════════════════╝" echo -e "${NC}" echo -e "${BLUE}Informations système:${NC}" echo " • Distribution: $(lsb_release -ds 2>/dev/null || cat /etc/os-release | grep PRETTY_NAME | cut -d'=' -f2 | tr -d '\"')" echo " • Noyau: $(uname -r)" echo " • Hostname: $(hostname)" echo " • Type: $(detect_container && echo "Conteneur/LXC" || echo "Hôte physique/VM")" echo "" echo -e "${BLUE}Configuration:${NC}" echo " • Port SSH nouveau: $NEW_SSH_PORT" echo " • Fichier log: $LOG_FILE" echo " • Sauvegardes: $BACKUP_DIR" echo " • Rapport final: $SECURITY_REPORT" echo "" echo -e "${YELLOW}⚠ Ce script va modifier profondément la configuration système ⚠${NC}" echo "" read -p "Appuyez sur Entrée pour continuer ou Ctrl+C pour annuler..." } print_summary() { echo -e "\n${GREEN}" echo "╔══════════════════════════════════════════════════════════════════╗" echo "║ DURCISSEMENT SYSTÈME TERMINÉ AVEC SUCCÈS ║" echo "╚══════════════════════════════════════════════════════════════════╝" echo -e "${NC}" # Récupérer le score Lynis local lynis_score="N/A" local lynis_max_score=100 if [[ -f "/tmp/lynis_score.txt" ]]; then lynis_score=$(cat /tmp/lynis_score.txt) [[ -f "/tmp/lynis_max_score.txt" ]] && lynis_max_score=$(cat /tmp/lynis_max_score.txt) elif [[ -f "$SECURITY_REPORT" ]]; then lynis_score=$(grep -i "Hardening index" "$SECURITY_REPORT" | grep -oP '\d+' | head -1) [[ -z "$lynis_score" ]] && lynis_score="N/A" fi # Afficher le score avec barre de progression dans le résumé if [[ "$lynis_score" != "N/A" ]]; then # Calcul de la barre de progression local percentage=$((lynis_score * 100 / lynis_max_score)) local bar_length=25 # Barre plus courte pour le résumé local filled_length=$((percentage * bar_length / 100)) local empty_length=$((bar_length - filled_length)) local bar="[" for ((i=0; i/dev/null || echo "désactivé (conteneur)")" echo " • Fail2ban: $(systemctl is-active fail2ban 2>/dev/null || echo "inactif")" echo " • AIDE: Base initialisée + vérification hebdomadaire" echo " • ClamAV: $(systemctl is-active clamav-freshclam 2>/dev/null || echo "inactif")" echo " • Chrony: $(systemctl is-active chrony 2>/dev/null || echo "inactif (conteneur)")" echo "" echo -e "${YELLOW}📁 FICHIERS GÉNÉRÉS:${NC}" echo " • Log complet: $LOG_FILE" echo " • Sauvegardes: $BACKUP_DIR" echo " • Rapport Lynis: $SECURITY_REPORT" echo " • Rapport chkrootkit: $BACKUP_DIR/chkrootkit_report.log" echo "" # Statistiques Lynis détaillées if [[ -f "$SECURITY_REPORT" ]]; then local warnings=$(grep -c "Warning" "$SECURITY_REPORT" 2>/dev/null || echo "0") local suggestions=$(grep -c "Suggestion" "$SECURITY_REPORT" 2>/dev/null || echo "0") echo -e "${YELLOW}📈 STATISTIQUES LYNIS:${NC}" echo " • Avertissements: $warnings" echo " • Suggestions d'amélioration: $suggestions" echo "" fi detect_container && { echo -e "${YELLOW}📦 SPÉCIFICITÉS CONTENEUR:${NC}" echo " • Process Accounting: Désactivé (géré par hôte)" echo " • UFW/Pare-feu: Géré par l'hôte Proxmox" echo " • Modules noyau: Gérés par l'hôte" echo " • Partitions: Gérées par Proxmox" echo " • Chrony: Synchronisation horaire gérée par l'hôte" echo "" } echo -e "${YELLOW}🔍 COMMANDES UTILES POST-INSTALLATION:${NC}" echo " 1. État Fail2ban: sudo fail2ban-client status sshd" echo " 2. Logs SSH: sudo tail -f /var/log/auth.log" echo " 3. Ports ouverts: sudo ss -tlnp" echo " 4. Règles UFW: sudo ufw status verbose" echo " 5. Score Lynis: grep 'Hardening index' $SECURITY_REPORT" echo " 6. Suggestions: grep 'Suggestion' $SECURITY_REPORT" echo " 7. Rapport complet: cat $SECURITY_REPORT | less" echo "" # Interpréter le score if [[ "$lynis_score" != "N/A" ]]; then echo -e "${CYAN}💡 INTERPRÉTATION DU SCORE:${NC}" if [[ $lynis_score -ge 80 ]]; then echo " ✓ Excellente sécurité ! Votre système est bien durci." echo " → Continuez à surveiller et à appliquer les mises à jour." elif [[ $lynis_score -ge 60 ]]; then echo " ✓ Bonne sécurité, mais des améliorations sont possibles." echo " → Consultez les suggestions Lynis pour progresser." elif [[ $lynis_score -ge 40 ]]; then echo " ⚠ Sécurité moyenne, améliorations recommandées." echo " → Examinez attentivement les avertissements et suggestions." else echo " ⚠ Score faible - plusieurs améliorations nécessaires." echo " → Priorisez les warnings critiques de Lynis." fi echo "" fi echo -e "${RED}══════════════════════════════════════════════════════════════════${NC}" echo -e "${RED}⚠ TESTEZ SSH:$NEW_SSH_PORT IMMÉDIATEMENT AVANT TOUTE AUTRE ACTION ⚠${NC}" echo -e "${RED}══════════════════════════════════════════════════════════════════${NC}" # Nettoyage rm -f /tmp/lynis_score.txt /tmp/lynis_max_score.txt 2>/dev/null || true } main() { # Mode nettoyage SSH [[ $# -eq 1 && "$1" == "--cleanup-ssh" ]] && { cleanup_ssh_port return 0 } # Initialisation check_requirements print_header log_message "==================================================" "START" log_message "Démarrage durcissement système v6.0" "START" log_message "Système: $(hostname)" "START" log_message "==================================================" "START" # EXÉCUTION DES ÉTAPES install_security_tools configure_process_accounting configure_sysctl_security configure_log_permissions configure_pam_password_policy configure_login_defs configure_umask configure_aide_sha512 initialize_aide_db configure_clamav configure_chrony harden_ssh configure_banners configure_ufw_firewall configure_fail2ban remove_unneeded_packages restrict_file_permissions disable_risky_kernel_modules configure_security_limits verify_packages_integrity configure_automatic_updates configure_aide_cron harden_smtp_banner harden_systemd_services configure_advanced_pam check_partition_layout check_vmlinuz run_chkrootkit prepare_ssh_cleanup run_lynis_audit # Résumé print_summary log_message "==================================================" "END" log_message "Durcissement terminé - Durée: $((SECONDS / 60)) min" "END" log_message "==================================================" "END" } # Gestion signaux trap 'print_error "Script interrompu"; exit 130' INT TERM # Point d'entrée [[ "${BASH_SOURCE[0]}" == "${0}" ]] && main "$@"