#!/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 "$@"
