#!/bin/bash ################################################################################ # Script: system_hardening_optimized.sh # Version: 8.2 # Description: Système de durcissement sécurité pour Debian/Ubuntu LTS # Mode autonome avec valeurs par défaut sécurisées # License: GPLv3 ################################################################################ set -euo pipefail 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 OPEN_PORTS_FILE="/tmp/open_ports_detected.txt" readonly DEFAULT_SSH_PORT=22022 readonly DEFAULT_TIMEZONE="Europe/Paris" readonly DEFAULT_PASS_MAX_DAYS=90 readonly DEFAULT_PASS_MIN_DAYS=7 readonly DEFAULT_UMASK="027" : "${AUTO_SSH_PORT:=$DEFAULT_SSH_PORT}" : "${AUTO_TIMEZONE:=$DEFAULT_TIMEZONE}" : "${AUTO_PASS_MAX_DAYS:=$DEFAULT_PASS_MAX_DAYS}" : "${AUTO_PASS_MIN_DAYS:=$DEFAULT_PASS_MIN_DAYS}" : "${AUTO_UMASK:=$DEFAULT_UMASK}" : "${AUTO_DISABLE_ROOT_LOGIN:=no}" : "${AUTO_ENABLE_FAIL2BAN:=yes}" : "${AUTO_ENABLE_UFW:=yes}" : "${AUTO_ENABLE_AIDE:=yes}" : "${AUTO_ENABLE_CLAMAV:=yes}" : "${AUTO_SKIP_PORTS_DETECTION:=no}" : "${AUTO_SKIP_LYNIS:=no}" : "${AUTO_YES:=no}" : "${AUTO_CLEANUP_SSH:=no}" : "${AUTO_CHANGE_ROOT_PWD:=no}" : "${AUTO_SKIP_DEBSUMS_CONTAINER:=yes}" TOTAL_STEPS=33 CURRENT_STEP=1 FORCE_ALL=false FORCE_STEPS=() SKIP_STEPS=() RESET_ALL=false LIST_STEPS=false SHOW_STATUS=false UNATTENDED=false 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' declare -A STEP_DESCRIPTIONS=( ["install_security_tools"]="Installation des outils de sécurité" ["change_root_password"]="Changement mot de passe root aléatoire" ["detect_open_ports"]="Détection des ports ouverts" ["configure_process_accounting"]="Configuration Process Accounting" ["configure_sysctl_security"]="Durcissement sysctl" ["configure_log_permissions"]="Permissions des logs" ["configure_pam_password_policy"]="Politique mots de passe PAM" ["configure_login_defs"]="Configuration login.defs" ["configure_umask"]="Configuration umask" ["configure_aide_sha512"]="Configuration AIDE SHA512" ["initialize_aide_db"]="Initialisation base AIDE" ["configure_clamav"]="Configuration ClamAV" ["configure_chrony"]="Configuration Chrony" ["harden_ssh"]="Durcissement SSH" ["configure_banners"]="Configuration bannières" ["configure_firewall_ports"]="Configuration pare-feu UFW" ["configure_fail2ban"]="Configuration Fail2ban" ["remove_unneeded_packages"]="Suppression paquets inutiles" ["restrict_file_permissions"]="Restriction permissions fichiers" ["disable_risky_kernel_modules"]="Désactivation modules noyau" ["configure_security_limits"]="Configuration limites sécurité" ["verify_packages_integrity"]="Vérification intégrité paquets" ["configure_automatic_updates"]="Configuration mises à jour auto" ["configure_aide_cron"]="Configuration tâche AIDE cron" ["harden_smtp_banner"]="Durcissement bannière SMTP" ["harden_systemd_services"]="Durcissement services systemd" ["configure_advanced_pam"]="Configuration PAM avancée" ["check_partition_layout"]="Vérification partitions" ["check_vmlinuz"]="Vérification fichiers noyau" ["run_chkrootkit"]="Exécution chkrootkit" ["prepare_ssh_cleanup"]="Préparation nettoyage SSH" ["run_lynis_audit"]="Audit Lynis final" ) 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}[STEP ${CURRENT_STEP}/${TOTAL_STEPS}] $step_title${NC}" log_message "Début étape $CURRENT_STEP: $step_title" "STEP" CURRENT_STEP=$((CURRENT_STEP + 1)) } print_success() { echo -e "${GREEN}[OK]${NC} $1" log_message "$1" "SUCCESS" } print_warning() { echo -e "${YELLOW}[WARN]${NC} $1" log_message "$1" "WARNING" } print_error() { echo -e "${RED}[ERR]${NC} $1" >&2 log_message "$1" "ERROR" } print_info() { echo -e "${BLUE}[INFO]${NC} $1" log_message "$1" "INFO" } auto_confirm() { [[ "$AUTO_YES" == "yes" ]] && return 0 [[ "$UNATTENDED" == true ]] && return 0 return 1 } check_step_done() { local step_name="$1" $FORCE_ALL && return 1 [[ " ${FORCE_STEPS[@]} " =~ " ${step_name} " ]] && { print_info "Forçage de l'étape: $step_name"; return 1; } [[ " ${SKIP_STEPS[@]} " =~ " ${step_name} " ]] && { print_info "Saut de l'étape: $step_name"; return 0; } grep -q "^${step_name}$" "$STATUS_FILE" 2>/dev/null } mark_step_done() { local step_name="$1" [[ " ${SKIP_STEPS[@]} " =~ " ${step_name} " ]] && { print_info "Étape $step_name ignorée"; return 0; } sed -i "/^${step_name}$/d" "$STATUS_FILE" 2>/dev/null || true echo "$step_name" >> "$STATUS_FILE" log_message "Étape '$step_name' terminée" "STATUS" } skip_step() { local step_name="$1" print_info "Étape déjà effectuée : $step_name" CURRENT_STEP=$((CURRENT_STEP + 1)) } detect_container() { [[ -f /.dockerenv ]] && return 0 [[ -d /dev/lxc ]] && return 0 [[ -f /.lxcpid ]] && return 0 [[ -n "${container:-}" ]] && return 0 [[ -n "${DOCKER_HOST:-}" ]] && return 0 [[ -n "${KUBERNETES_SERVICE_HOST:-}" ]] && return 0 if [[ -f /proc/1/cgroup ]]; then local cgroup_content cgroup_content=$(cat /proc/1/cgroup 2>/dev/null || echo "") echo "$cgroup_content" | grep -qE "(docker|lxc|kubepods|containerd|podman)" && return 0 fi 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") return 0 ;; esac fi [[ -f /run/.containerenv ]] && return 0 log_message "Aucun conteneur détecté" "DETECT" return 1 } detect_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() { 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 } detect_open_ports() { local step_name="detect_open_ports" [[ "$AUTO_SKIP_PORTS_DETECTION" == "yes" ]] && { mark_step_done "$step_name"; return 0; } check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$step_name]}"; return 0; } print_step "Détection des ports ouverts" if command -v ss > /dev/null 2>&1; then ss -tlnp | grep LISTEN | awk '{print $4}' | awk -F: '{print $NF}' | sort -n | uniq > "$OPEN_PORTS_FILE" 2>/dev/null elif command -v netstat > /dev/null 2>&1; then netstat -tlnp 2>/dev/null | grep LISTEN | awk '{print $4}' | awk -F: '{print $NF}' | sort -n | uniq > "$OPEN_PORTS_FILE" else echo "22" > "$OPEN_PORTS_FILE" fi local port_count=$(wc -l < "$OPEN_PORTS_FILE" 2>/dev/null || echo 0) print_success "$port_count port(s) ouvert(s) détecté(s)" mark_step_done "$step_name" } is_port_open() { grep -q "^${1}$" "$OPEN_PORTS_FILE" 2>/dev/null } get_ssh_port_to_use() { detect_lxc && echo "22" || echo "$AUTO_SSH_PORT" } configure_banners() { local step_name="configure_banners" check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$step_name]}"; return 0; } print_step "Configuration des bannières légales" cat > /etc/issue.net << 'EOF' ╔══════════════════════════════════════════════════════════════════╗ ║ SYSTÈME SÉCURISÉ ║ ║ Accès strictement réservé aux personnes autorisées. ║ ║ Toutes les activités sont surveillées et enregistrées. ║ ╚══════════════════════════════════════════════════════════════════╝ EOF chmod 644 /etc/issue.net cat > /etc/issue << 'EOF' ╔══════════════════════════════════════════════════════════════════╗ ║ SYSTÈME SÉCURISÉ ║ ║ Accès strictement réservé aux personnes autorisées. ║ ╚══════════════════════════════════════════════════════════════════╝ EOF chmod 644 /etc/issue [[ -d /etc/update-motd.d ]] && chmod -x /etc/update-motd.d/* 2>/dev/null || true print_success "Bannières légales configurées" mark_step_done "$step_name" } _filter_available_packages() { local available=() for pkg in "$@"; do if apt-cache show "$pkg" >/dev/null 2>&1; then available+=("$pkg") else log_message "Paquet non disponible sur ce système, ignoré: $pkg" "WARNING" fi done echo "${available[@]:-}" } install_security_tools() { local step_name="install_security_tools" check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$step_name]}"; return 0; } print_step "Mise à jour système et installation outils de sécurité" DEBIAN_FRONTEND=noninteractive apt-get update -qq DEBIAN_FRONTEND=noninteractive apt-get upgrade -y -qq local candidate_packages=( lynis aide aide-common ufw libpam-pwquality apt-listchanges needrestart chrony chkrootkit libpam-tmpdir debsums unattended-upgrades ) [[ "$AUTO_ENABLE_FAIL2BAN" == "yes" ]] && candidate_packages+=(fail2ban) [[ "$AUTO_ENABLE_CLAMAV" == "yes" ]] && candidate_packages+=(clamav clamav-daemon) detect_container || candidate_packages+=(acct) local installable installable=$(_filter_available_packages "${candidate_packages[@]}") if [[ -n "$installable" ]]; then # shellcheck disable=SC2086 DEBIAN_FRONTEND=noninteractive apt-get install -y -qq $installable fi print_success "Système mis à jour et outils installés" mark_step_done "$step_name" } change_root_password() { local step_name="change_root_password" check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$step_name]}"; return 0; } print_step "Changement du mot de passe root" if [[ "$AUTO_CHANGE_ROOT_PWD" != "yes" ]]; then print_info "Changement mot de passe root désactivé (AUTO_CHANGE_ROOT_PWD=no)" mark_step_done "$step_name" return 0 fi detect_container && { print_warning "Conteneur détecté - changement ignoré"; mark_step_done "$step_name"; return 0; } local root_shell=$(grep "^root:" /etc/passwd | cut -d: -f7) [[ "$root_shell" == "/usr/sbin/nologin" || "$root_shell" == "/bin/false" ]] && { print_warning "Root a un shell non interactif - ignoré" mark_step_done "$step_name" return 0 } local NEW_ROOT_PASSWORD NEW_ROOT_PASSWORD=$(tr -dc 'A-Za-z0-9!@#$%^&*' < /dev/urandom | head -c 20) local PWD_FILE="/root/root_password_$(date +%Y%m%d_%H%M%S).txt" cat > "$PWD_FILE" << EOF === MOT DE PASSE ROOT - $(hostname) - $(date) === Mot de passe root: $NEW_ROOT_PASSWORD CONSERVEZ CE FICHIER EN LIEU SÛR === FIN === EOF chmod 600 "$PWD_FILE" chown root:root "$PWD_FILE" if echo "root:$NEW_ROOT_PASSWORD" | chpasswd -c SHA512 2>/dev/null; then chage -M 90 root 2>/dev/null || true print_success "Mot de passe root changé - sauvegardé dans $PWD_FILE" else rm -f "$PWD_FILE" print_error "Échec changement mot de passe root" return 1 fi mark_step_done "$step_name" } configure_process_accounting() { local step_name="configure_process_accounting" check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$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" } configure_sysctl_security() { local step_name="configure_sysctl_security" check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$step_name]}"; return 0; } print_step "Durcissement des paramètres noyau (sysctl)" cat > /etc/sysctl.d/99-security-hardening.conf << 'EOF' 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 net.ipv4.tcp_max_syn_backlog = 2048 net.ipv4.tcp_synack_retries = 2 net.ipv4.tcp_syn_retries = 5 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 kernel.randomize_va_space = 2 kernel.kptr_restrict = 2 kernel.dmesg_restrict = 1 kernel.yama.ptrace_scope = 1 kernel.unprivileged_bpf_disabled = 1 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" } configure_log_permissions() { local step_name="configure_log_permissions" check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$step_name]}"; return 0; } print_step "Configuration des permissions des fichiers de log" chmod 750 /var/log 2>/dev/null || true chown root:adm /var/log 2>/dev/null || true find /var/log -type f -exec chmod 640 {} \; 2>/dev/null || true for log_file in /var/log/auth.log /var/log/syslog /var/log/secure /var/log/messages; do [[ -f "$log_file" ]] && chmod 600 "$log_file" 2>/dev/null || true done [[ -d /var/log/audit ]] && { chmod 700 /var/log/audit 2>/dev/null || true; find /var/log/audit -type f -exec chmod 600 {} \; 2>/dev/null || true; } command -v journalctl > /dev/null 2>&1 && { chmod 2750 /var/log/journal 2>/dev/null || true; chown root:systemd-journal /var/log/journal 2>/dev/null || true; } print_success "Permissions des logs configurées" mark_step_done "$step_name" } configure_pam_password_policy() { local step_name="configure_pam_password_policy" check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$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' minlen = 14 minclass = 3 maxrepeat = 3 maxsequence = 3 dcredit = -1 ucredit = -1 lcredit = -1 ocredit = -1 difok = 3 maxclassrepeat = 3 gecoscheck = 1 EOF backup_file "/etc/pam.d/common-password" if grep -q "pam_pwquality.so" /etc/pam.d/common-password; then sed -i 's/pam_pwquality.so.*/pam_pwquality.so retry=3 minlen=14 minclass=3 ucredit=-1 lcredit=-1 dcredit=-1 ocredit=-1 enforce_for_root/' /etc/pam.d/common-password else sed -i '/^password.*pam_unix.so/ipassword requisite pam_pwquality.so retry=3 minlen=14 minclass=3 ucredit=-1 lcredit=-1 dcredit=-1 ocredit=-1 enforce_for_root' /etc/pam.d/common-password fi sed -i 's/pam_unix.so.*/& sha512 rounds=500000 remember=5/' /etc/pam.d/common-password print_success "Politique PAM configurée" mark_step_done "$step_name" } configure_login_defs() { local step_name="configure_login_defs" check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$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" "$AUTO_PASS_MAX_DAYS" update_config_value "/etc/login.defs" "PASS_MIN_DAYS" "$AUTO_PASS_MIN_DAYS" 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" "$AUTO_UMASK" 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" } configure_umask() { local step_name="configure_umask" check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$step_name]}"; return 0; } print_step "Configuration de l'umask par défaut" for file in /etc/profile /etc/bash.bashrc /etc/zsh/zshrc; do [[ -f "$file" ]] && { backup_file "$file"; sed -i '/^umask/d' "$file"; add_unique_line "umask $AUTO_UMASK" "$file"; } done echo "umask $AUTO_UMASK" > /etc/profile.d/umask.sh chmod 644 /etc/profile.d/umask.sh print_success "Umask configuré à $AUTO_UMASK" mark_step_done "$step_name" } configure_aide_sha512() { local step_name="configure_aide_sha512" check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$step_name]}"; return 0; } [[ "$AUTO_ENABLE_AIDE" != "yes" ]] && { mark_step_done "$step_name"; return 0; } print_step "Configuration AIDE pour SHA512" backup_file "/etc/aide/aide.conf" cat > /etc/aide/aide.conf << 'EOF' database=file:/var/lib/aide/aide.db.gz database_out=file:/var/lib/aide/aide.db.new.gz gzip_dbout=yes Normal = sha512 Large = sha512+ftype Everything = sha512+ftype+p+i+n+u+g+s+m+c+acl+selinux+xattrs /etc Everything /bin Large /sbin Large /usr/bin Large /usr/sbin Large /boot Large /lib Large /lib64 Large /root Large !/proc !/sys !/tmp !/var/tmp !/var/run !/var/log !/dev EOF print_success "AIDE configuré pour SHA512" mark_step_done "$step_name" } initialize_aide_db() { local step_name="initialize_aide_db" check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$step_name]}"; return 0; } [[ "$AUTO_ENABLE_AIDE" != "yes" ]] && { mark_step_done "$step_name"; return 0; } print_step "Initialisation de la base de données AIDE" # Corriger la config AIDE si elle utilise encore les macros @@{} non supportées if grep -q '@@{' /etc/aide/aide.conf 2>/dev/null; then print_warning "Config AIDE avec macros non supportées détectée - correction automatique" cat > /etc/aide/aide.conf << 'EOF' database=file:/var/lib/aide/aide.db.gz database_out=file:/var/lib/aide/aide.db.new.gz gzip_dbout=yes Normal = sha512 Large = sha512+ftype Everything = sha512+ftype+p+i+n+u+g+s+m+c+acl+selinux+xattrs /etc Everything /bin Large /sbin Large /usr/bin Large /usr/sbin Large /boot Large /lib Large /lib64 Large /root Large !/proc !/sys !/tmp !/var/tmp !/var/run !/var/log !/dev EOF fi if aide --init --config /etc/aide/aide.conf 2>&1 | tee -a "$LOG_FILE"; then [[ -f /var/lib/aide/aide.db.new.gz ]] && mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz print_success "Base de données AIDE initialisée" else print_warning "Échec initialisation AIDE - étape ignorée, durcissement continue" fi mark_step_done "$step_name" } configure_clamav() { local step_name="configure_clamav" check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$step_name]}"; return 0; } [[ "$AUTO_ENABLE_CLAMAV" != "yes" ]] && { mark_step_done "$step_name"; return 0; } print_step "Configuration de ClamAV" mkdir -p /var/log/clamav /var/lib/clamav /run/clamav id -u clamav >/dev/null 2>&1 || useradd -r -M -d /var/lib/clamav -s /bin/false -c "Clam AntiVirus" clamav 2>/dev/null || true chown -R clamav:clamav /var/log/clamav /var/lib/clamav /run/clamav 2>/dev/null || true touch /var/log/clamav/freshclam.log /var/log/clamav/clamav.log 2>/dev/null || true chown clamav:clamav /var/log/clamav/freshclam.log /var/log/clamav/clamav.log 2>/dev/null || true systemctl stop clamav-freshclam clamav-daemon 2>/dev/null || true sleep 2 su - clamav -s /bin/bash -c "freshclam --quiet" 2>&1 | tee -a "$LOG_FILE" || \ freshclam --user=clamav --quiet 2>&1 | tee -a "$LOG_FILE" || \ print_warning "Mise à jour ClamAV échouée" systemctl enable clamav-freshclam clamav-daemon 2>/dev/null || true systemctl start clamav-freshclam 2>/dev/null && print_success "freshclam démarré" || print_warning "freshclam non démarré" sleep 3 systemctl start clamav-daemon 2>/dev/null && print_success "clamav-daemon démarré" || print_warning "clamav-daemon non démarré" print_success "ClamAV configuré" mark_step_done "$step_name" } configure_chrony() { local step_name="configure_chrony" check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$step_name]}"; return 0; } print_step "Configuration de la synchronisation horaire" detect_container && { print_warning "Conteneur détecté - NTP géré par l'hôte"; mark_step_done "$step_name"; return 0; } backup_file "/etc/chrony/chrony.conf" cat > /etc/chrony/chrony.conf << EOF pool 2.debian.pool.ntp.org iburst server 0.fr.pool.ntp.org iburst server 1.fr.pool.ntp.org iburst server 2.fr.pool.ntp.org iburst server 3.fr.pool.ntp.org iburst driftfile /var/lib/chrony/chrony.drift logdir /var/log/chrony makestep 1.0 3 rtcsync allow 127.0.0.1 deny all local stratum 10 EOF timedatectl set-timezone "$AUTO_TIMEZONE" 2>/dev/null || print_warning "Impossible de définir le fuseau horaire $AUTO_TIMEZONE" systemctl restart chrony 2>/dev/null && print_success "Chrony configuré" || print_warning "Erreur démarrage Chrony" mark_step_done "$step_name" } harden_ssh() { local step_name="harden_ssh" check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$step_name]}"; return 0; } print_step "Durcissement du service SSH" backup_file "/etc/ssh/sshd_config" local sshd_config="/etc/ssh/sshd_config" local ssh_port="22" detect_lxc || { ssh_port="$AUTO_SSH_PORT"; [[ "$ssh_port" =~ ^[0-9]+$ ]] && [ "$ssh_port" -ge 1 ] && [ "$ssh_port" -le 65535 ] || ssh_port="22"; } print_info "Configuration SSH avec le port: $ssh_port" local temp_config temp_config=$(mktemp) { echo "# Configuration SSH sécurisée - $(date)" if [[ "$ssh_port" != "22" ]] && ! detect_lxc; then echo "Port 22" echo "Port $ssh_port" else echo "Port $ssh_port" fi echo "ListenAddress 0.0.0.0" echo "ListenAddress ::" [[ "$AUTO_DISABLE_ROOT_LOGIN" == "yes" ]] && echo "PermitRootLogin no" || echo "PermitRootLogin prohibit-password" 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 "Protocol 2" echo "StrictModes yes" echo "UseDNS no" echo "IgnoreRhosts yes" echo "HostbasedAuthentication no" echo "PrintLastLog yes" echo "X11Forwarding no" echo "AllowAgentForwarding no" echo "AllowTcpForwarding no" echo "TCPKeepAlive no" echo "Compression no" echo "LogLevel VERBOSE" echo "Banner /etc/issue.net" 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" } > "$temp_config" if ! sshd -t -f "$temp_config" 2>&1; then print_warning "Configuration étendue invalide, passage en mode minimal" cat > "$temp_config" << 'EOF' Port 22 Protocol 2 PermitRootLogin prohibit-password PasswordAuthentication no PubkeyAuthentication yes PermitEmptyPasswords no X11Forwarding no AllowTcpForwarding no AllowAgentForwarding no UsePAM yes PrintLastLog yes TCPKeepAlive no ClientAliveInterval 300 ClientAliveCountMax 2 EOF if ! sshd -t -f "$temp_config" 2>&1; then print_error "Configuration SSH minimale invalide" rm -f "$temp_config" return 1 fi fi cp "$temp_config" "$sshd_config" chmod 600 "$sshd_config" chown root:root "$sshd_config" rm -f "$temp_config" if sshd -t -f "$sshd_config" 2>&1 | tee -a "$LOG_FILE"; then systemctl restart sshd 2>/dev/null || systemctl restart ssh 2>/dev/null && \ print_success "SSH durci avec succès (port $ssh_port)" || { print_error "Échec redémarrage SSH - restauration" cp "${BACKUP_DIR}/sshd_config" /etc/ssh/sshd_config systemctl restart sshd 2>/dev/null || systemctl restart ssh 2>/dev/null return 1 } else print_error "Configuration SSH invalide" cp "${BACKUP_DIR}/sshd_config" /etc/ssh/sshd_config return 1 fi mark_step_done "$step_name" } configure_firewall_ports() { local step_name="configure_firewall_ports" check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$step_name]}"; return 0; } [[ "$AUTO_ENABLE_UFW" != "yes" ]] && { mark_step_done "$step_name"; return 0; } print_step "Configuration des règles de pare-feu" if detect_container; then print_warning "Conteneur détecté - pare-feu géré par l'hôte" command -v ufw > /dev/null 2>&1 && { ufw --force disable 2>/dev/null || true; systemctl stop ufw 2>/dev/null || true; systemctl disable ufw 2>/dev/null || true; } mark_step_done "$step_name"; return 0 fi ufw --force disable > /dev/null 2>&1 || true ufw --force reset > /dev/null 2>&1 ufw default deny incoming ufw default allow outgoing local ssh_port ssh_port=$(get_ssh_port_to_use) ufw allow "${ssh_port}/tcp" comment 'SSH sécurisé' [[ "$ssh_port" != "22" ]] && is_port_open "22" && ufw allow 22/tcp comment 'SSH temporaire' ufw allow 22/tcp comment 'SSH' ufw allow 53/udp comment 'DNS' ufw allow 123/udp comment 'NTP' if [[ -f "$OPEN_PORTS_FILE" ]]; then while read -r port; do [[ "$port" == "22" || "$port" == "$ssh_port" || "$port" == "53" || "$port" == "123" ]] && continue local service_name="" case $port in 80) service_name="HTTP" ;; 443) service_name="HTTPS" ;; 25) service_name="SMTP" ;; 587) service_name="SMTP Submission" ;; 465) service_name="SMTPS" ;; 993) service_name="IMAPS" ;; 995) service_name="POP3S" ;; 3306) service_name="MySQL" ;; 5432) service_name="PostgreSQL" ;; 6379) service_name="Redis" ;; 27017) service_name="MongoDB" ;; 3000) service_name="Grafana/WebApp" ;; 8080) service_name="Proxy/Web" ;; 8443) service_name="HTTPS Alt" ;; *) ufw deny "${port}/tcp" comment "Port non standard $port (bloqué auto)"; continue ;; esac [[ -n "$service_name" ]] && ufw allow "${port}/tcp" comment "$service_name" done < "$OPEN_PORTS_FILE" fi echo "y" | ufw --force enable > /dev/null 2>&1 ufw status | grep -q "Status: active" && print_success "Pare-feu UFW configuré et activé" || print_warning "UFW activé avec avertissements" mark_step_done "$step_name" } configure_fail2ban() { local step_name="configure_fail2ban" check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$step_name]}"; return 0; } [[ "$AUTO_ENABLE_FAIL2BAN" != "yes" ]] && { mark_step_done "$step_name"; return 0; } print_step "Configuration de Fail2ban" backup_file "/etc/fail2ban/jail.conf" local ssh_port ssh_port=$(get_ssh_port_to_use) cat > /etc/fail2ban/jail.local << EOF [DEFAULT] bantime = 1h findtime = 10m maxretry = 3 ignoreip = 127.0.0.1/8 ::1 backend = auto banaction = ufw action = %(action_mwl)s [sshd] enabled = true port = $ssh_port filter = sshd logpath = /var/log/auth.log maxretry = 5 bantime = 2h [recidive] enabled = true filter = recidive logpath = /var/log/fail2ban.log action = iptables-allports[name=recidive] bantime = 1w findtime = 1d maxretry = 3 EOF systemctl enable fail2ban 2>/dev/null || true systemctl restart fail2ban 2>&1 | tee -a "$LOG_FILE" && print_success "Fail2ban configuré" || print_warning "Fail2ban démarré avec avertissements" mark_step_done "$step_name" } remove_unneeded_packages() { local step_name="remove_unneeded_packages" check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$step_name]}"; return 0; } print_step "Suppression des paquets inutiles" for package in telnet rsh-client rsh-server netcat-openbsd netcat-traditional nis talk talkd; 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" } restrict_file_permissions() { local step_name="restrict_file_permissions" check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$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" } disable_risky_kernel_modules() { local step_name="disable_risky_kernel_modules" check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$step_name]}"; return 0; } print_step "Désactivation des modules noyau risqués" cat > /etc/modprobe.d/hardening-blacklist.conf << 'EOF' blacklist dccp install dccp /bin/true blacklist sctp install sctp /bin/true blacklist rds install rds /bin/true blacklist tipc install tipc /bin/true 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 blacklist firewire-core install firewire-core /bin/true blacklist thunderbolt install thunderbolt /bin/true EOF print_success "Modules noyau risqués désactivés" mark_step_done "$step_name" } configure_security_limits() { local step_name="configure_security_limits" check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$step_name]}"; return 0; } print_step "Configuration des limites de sécurité" backup_file "/etc/security/limits.conf" cat >> /etc/security/limits.conf << 'EOF' * hard core 0 * soft nproc 512 * hard nproc 1024 * soft nofile 65536 * hard nofile 65536 EOF print_success "Limites de sécurité configurées" mark_step_done "$step_name" } verify_packages_integrity() { local step_name="verify_packages_integrity" check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$step_name]}"; return 0; } print_step "Vérification de l'intégrité des paquets" command -v debsums > /dev/null 2>&1 || { DEBIAN_FRONTEND=noninteractive apt-get install -y -qq debsums 2>/dev/null || { print_warning "Impossible d'installer debsums"; mark_step_done "$step_name"; return 0; }; } local debsums_output debsums_output=$(debsums -s 2>&1 | grep -v "Permission denied" || true) if [[ -n "$debsums_output" ]]; then echo "$debsums_output" | tee -a "$LOG_FILE" print_warning "Certains paquets ont des erreurs d'intégrité" else print_success "Tous les paquets sont intacts" fi mark_step_done "$step_name" } configure_aide_cron() { local step_name="configure_aide_cron" check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$step_name]}"; return 0; } [[ "$AUTO_ENABLE_AIDE" != "yes" ]] && { mark_step_done "$step_name"; return 0; } print_step "Configuration des vérifications AIDE planifiées" cat > /etc/cron.daily/aide-check << 'EOF' #!/bin/bash LOGFILE="/var/log/aide-check-$(date +%Y%m%d).log" command -v aide > /dev/null || exit 0 echo "=== Vérification AIDE $(date) ===" > "$LOGFILE" /usr/bin/aide --check 2>&1 >> "$LOGFILE" && \ echo "AIDE: OK - $(date)" >> "$LOGFILE" || \ { echo "AIDE: ALERTE - $(date)" >> "$LOGFILE"; echo "Alerte AIDE sur $(hostname)" | mail -s "[AIDE] Changements détectés" root 2>/dev/null || true; } find /var/log -name "aide-check-*.log" -mtime +30 -delete 2>/dev/null || true EOF chmod 755 /etc/cron.daily/aide-check print_success "Vérification AIDE quotidienne configurée" mark_step_done "$step_name" } harden_smtp_banner() { local step_name="harden_smtp_banner" check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$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" else print_info "Aucun serveur SMTP détecté" fi mark_step_done "$step_name" } harden_systemd_services() { local step_name="harden_systemd_services" check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$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; } if detect_container; then for service in ssh sshd fail2ban chrony; do rm -rf "/etc/systemd/system/${service}.service.d" 2>/dev/null || true done systemctl daemon-reload print_success "Configuration systemd nettoyée (conteneur)" mark_step_done "$step_name"; return 0 fi local hardened=0 for service in ssh sshd fail2ban chrony; do local unit="${service}.service" systemctl list-unit-files | grep -q "^${unit}" || continue mkdir -p "/etc/systemd/system/${unit}.d" 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 hardened=$((hardened + 1)) done systemctl daemon-reload print_success "$hardened service(s) systemd durci(s)" mark_step_done "$step_name" } configure_advanced_pam() { local step_name="configure_advanced_pam" check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$step_name]}"; return 0; } print_step "Configuration PAM avancée" backup_file "/etc/pam.d/common-password" 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 } 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 } done < /etc/passwd print_success "Configuration PAM avancée appliquée" mark_step_done "$step_name" } check_partition_layout() { local step_name="check_partition_layout" check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$step_name]}"; return 0; } print_step "Vérification disposition partitions" local warnings=0 for partition in /home /tmp /var /var/log /var/log/audit; do mount | grep -q " on ${partition} " && print_info " ✓ $partition: Partition séparée" || { warnings=$((warnings+1)); 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)" mark_step_done "$step_name" } check_vmlinuz() { local step_name="check_vmlinuz" check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$step_name]}"; return 0; } print_step "Vérification fichiers noyau" local found=0 for kfile in /vmlinuz /boot/vmlinuz "/boot/vmlinuz-$(uname -r)" "/boot/initrd.img-$(uname -r)"; do [[ -f "$kfile" ]] && { found=$((found+1)); print_info " ✓ $kfile"; } done [[ $found -eq 0 ]] && print_warning "Aucun fichier noyau trouvé" || print_success "$found fichier(s) noyau trouvé(s)" mark_step_done "$step_name" } run_chkrootkit() { local step_name="run_chkrootkit" check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$step_name]}"; return 0; } print_step "Exécution de chkrootkit" mkdir -p "$BACKUP_DIR" chkrootkit > "$BACKUP_DIR/chkrootkit_report.log" 2>&1 || print_warning "Chkrootkit a détecté des avertissements" print_success "Scan chkrootkit terminé - rapport: $BACKUP_DIR/chkrootkit_report.log" mark_step_done "$step_name" } prepare_ssh_cleanup() { local step_name="prepare_ssh_cleanup" check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$step_name]}"; return 0; } print_step "Préparation nettoyage port SSH 22" local ssh_port ssh_port=$(get_ssh_port_to_use) [[ "$ssh_port" != "22" ]] && print_info "Après test du port $ssh_port: relancez avec --cleanup-ssh" || print_info "Port SSH principal: 22 - aucun nettoyage nécessaire" mark_step_done "$step_name" } run_lynis_audit() { local step_name="run_lynis_audit" check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$step_name]}"; return 0; } [[ "$AUTO_SKIP_LYNIS" == "yes" ]] && { mark_step_done "$step_name"; return 0; } print_step "Exécution de l'audit Lynis" lynis audit system --quick --no-colors > "$SECURITY_REPORT" 2>&1 local score score=$(grep -i "Hardening index" "$SECURITY_REPORT" | grep -oP '\d+' | head -1 || echo "0") print_success "Audit Lynis terminé - Score: ${score}/100 - Rapport: $SECURITY_REPORT" mark_step_done "$step_name" } configure_automatic_updates() { local step_name="configure_automatic_updates" check_step_done "$step_name" && { skip_step "${STEP_DESCRIPTIONS[$step_name]}"; return 0; } print_step "Configuration des mises à jour automatiques" dpkg -l | grep -q "^ii.*unattended-upgrades" || DEBIAN_FRONTEND=noninteractive apt-get install -y -qq unattended-upgrades apt-listchanges needrestart 2>/dev/null || true backup_file "/etc/apt/apt.conf.d/50unattended-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}:${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::Mail "root"; Unattended-Upgrade::MailOnlyOnError "true"; 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"; APT::Periodic::Verbose "0"; EOF systemctl enable unattended-upgrades 2>/dev/null || true systemctl restart unattended-upgrades 2>/dev/null || systemctl start unattended-upgrades 2>/dev/null || true print_success "Mises à jour automatiques configurées" mark_step_done "$step_name" } cleanup_ssh_port() { local ssh_port ssh_port=$(get_ssh_port_to_use) [[ "$ssh_port" == "22" ]] && { print_info "Port SSH principal déjà sur 22"; return 0; } backup_file "/etc/ssh/sshd_config" sed -i '/^Port 22$/d' /etc/ssh/sshd_config command -v ufw > /dev/null 2>&1 && ufw status | grep -q "22/tcp" && ufw delete allow 22/tcp 2>/dev/null || true sshd -t && { systemctl restart sshd 2>/dev/null; print_success "Port SSH 22 supprimé - SSH sur port $ssh_port uniquement"; } || { print_error "Erreur configuration - restauration" cp "${BACKUP_DIR}/sshd_config" /etc/ssh/sshd_config systemctl restart sshd 2>/dev/null return 1 } } check_requirements() { [[ $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" } parse_arguments() { while [[ $# -gt 0 ]]; do case $1 in --force-all) FORCE_ALL=true; shift ;; --force-step=*) FORCE_STEPS+=("${1#*=}"); shift ;; --skip-step=*) SKIP_STEPS+=("${1#*=}"); shift ;; --cleanup-ssh) AUTO_CLEANUP_SSH="yes"; shift ;; --unattended) UNATTENDED=true; AUTO_YES="yes"; shift ;; --ssh-port=*) AUTO_SSH_PORT="${1#*=}"; shift ;; --timezone=*) AUTO_TIMEZONE="${1#*=}"; shift ;; --change-root-pwd) AUTO_CHANGE_ROOT_PWD="yes"; shift ;; --yes|-y) AUTO_YES="yes"; shift ;; --skip-lynis) AUTO_SKIP_LYNIS="yes"; shift ;; *) print_error "Option inconnue: $1"; exit 1 ;; esac done } main() { parse_arguments "$@" check_requirements log_message "==================================================" "START" log_message "Démarrage durcissement système v8.2" "START" log_message "Mode: $([ "$UNATTENDED" == true ] && echo "Autonome" || echo "Interactif")" "START" log_message "Port SSH: $AUTO_SSH_PORT" "START" log_message "Hostname: $(hostname)" "START" log_message "==================================================" "START" install_security_tools || print_warning "install_security_tools: erreur ignorée" change_root_password || print_warning "change_root_password: erreur ignorée" detect_open_ports || print_warning "detect_open_ports: erreur ignorée" configure_process_accounting || print_warning "configure_process_accounting: erreur ignorée" configure_sysctl_security || print_warning "configure_sysctl_security: erreur ignorée" configure_log_permissions || print_warning "configure_log_permissions: erreur ignorée" configure_pam_password_policy || print_warning "configure_pam_password_policy: erreur ignorée" configure_login_defs || print_warning "configure_login_defs: erreur ignorée" configure_umask || print_warning "configure_umask: erreur ignorée" configure_aide_sha512 || print_warning "configure_aide_sha512: erreur ignorée" initialize_aide_db || print_warning "initialize_aide_db: erreur ignorée" [[ "$AUTO_ENABLE_CLAMAV" == "yes" ]] && { configure_clamav || print_warning "configure_clamav: erreur ignorée"; } configure_chrony || print_warning "configure_chrony: erreur ignorée" harden_ssh || print_warning "harden_ssh: erreur ignorée" configure_banners || print_warning "configure_banners: erreur ignorée" configure_firewall_ports || print_warning "configure_firewall_ports: erreur ignorée" configure_fail2ban || print_warning "configure_fail2ban: erreur ignorée" remove_unneeded_packages || print_warning "remove_unneeded_packages: erreur ignorée" restrict_file_permissions || print_warning "restrict_file_permissions: erreur ignorée" disable_risky_kernel_modules || print_warning "disable_risky_kernel_modules: erreur ignorée" configure_security_limits || print_warning "configure_security_limits: erreur ignorée" verify_packages_integrity || print_warning "verify_packages_integrity: erreur ignorée" configure_automatic_updates || print_warning "configure_automatic_updates: erreur ignorée" configure_aide_cron || print_warning "configure_aide_cron: erreur ignorée" harden_smtp_banner || print_warning "harden_smtp_banner: erreur ignorée" harden_systemd_services || print_warning "harden_systemd_services: erreur ignorée" configure_advanced_pam || print_warning "configure_advanced_pam: erreur ignorée" check_partition_layout || print_warning "check_partition_layout: erreur ignorée" check_vmlinuz || print_warning "check_vmlinuz: erreur ignorée" run_chkrootkit || print_warning "run_chkrootkit: erreur ignorée" prepare_ssh_cleanup || print_warning "prepare_ssh_cleanup: erreur ignorée" run_lynis_audit || print_warning "run_lynis_audit: erreur ignorée" [[ "$AUTO_CLEANUP_SSH" == "yes" ]] && { cleanup_ssh_port || print_warning "cleanup_ssh_port: erreur ignorée"; } log_message "Durcissement terminé" "END" log_message "==================================================" "END" } trap 'print_error "Script interrompu"; exit 130' INT TERM [[ "${BASH_SOURCE[0]}" == "${0}" ]] && main "$@"