#!/bin/bash ################################################################################ # Script: system_hardening_optimized.sh # Version: 8.0 # Date: $(date +%Y-%m-%d) # Author: Security Team # Description: Système de durcissement sécurité pour Debian/Ubuntu LTS # avec détection automatique des ports et contrôle des étapes # 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 readonly OPEN_PORTS_FILE="/tmp/open_ports_detected.txt" TOTAL_STEPS=31 CURRENT_STEP=1 # Variables de contrôle FORCE_ALL=false FORCE_STEPS=() SKIP_STEPS=() RESET_ALL=false LIST_STEPS=false SHOW_STATUS=false # ============================================================================== # 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' # ============================================================================== # DÉFINITION DES ÉTAPES # ============================================================================== declare -A STEP_DESCRIPTIONS=( ["install_security_tools"]="Installation des outils de sécurité" ["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" ) # ============================================================================== # 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" } # ============================================================================== # GESTION DES ÉTAPES # ============================================================================== check_step_done() { local step_name="$1" if $FORCE_ALL; then return 1 fi if [[ " ${FORCE_STEPS[@]} " =~ " ${step_name} " ]]; then print_info "Forçage de l'étape: $step_name" return 1 fi if [[ " ${SKIP_STEPS[@]} " =~ " ${step_name} " ]]; then print_info "Saut de l'étape: $step_name" return 0 fi grep -q "^${step_name}$" "$STATUS_FILE" 2>/dev/null } mark_step_done() { local step_name="$1" if [[ " ${SKIP_STEPS[@]} " =~ " ${step_name} " ]]; then print_info "Étape $step_name ignorée - non marquée comme terminée" return 0 fi sed -i "/^${step_name}$/d" "$STATUS_FILE" 2>/dev/null || true echo "$step_name" >> "$STATUS_FILE" log_message "Étape '$step_name' marquée comme 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() { grep -qE "lxc|container" /proc/self/cgroup 2>/dev/null || \ [[ -f /.dockerenv ]] || [[ -d /dev/lxc ]] } detect_lxc() { grep -q "lxc" /proc/self/cgroup 2>/dev/null || [[ -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 CONTRÔLE # ============================================================================== reset_step() { local step_name="$1" if [[ -f "$STATUS_FILE" ]]; then sed -i "/^${step_name}$/d" "$STATUS_FILE" 2>/dev/null || true print_success "Étape '$step_name' réinitialisée" case $step_name in "harden_ssh") if [[ -f "${BACKUP_DIR}/sshd_config" ]]; then read -p "Restaurer la configuration SSH originale ? (o/N): " -r restore_ssh if [[ "$restore_ssh" =~ ^[Oo]$ ]]; then cp "${BACKUP_DIR}/sshd_config" /etc/ssh/sshd_config systemctl restart sshd 2>/dev/null || true print_success "Configuration SSH restaurée" fi fi ;; "configure_firewall_ports") if command -v ufw > /dev/null 2>&1; then read -p "Réinitialiser les règles UFW ? (o/N): " -r reset_ufw if [[ "$reset_ufw" =~ ^[Oo]$ ]]; then ufw --force reset > /dev/null 2>&1 || true print_success "Règles UFW réinitialisées" fi fi ;; "configure_fail2ban") if [[ -f "${BACKUP_DIR}/jail.local" ]]; then read -p "Restaurer la configuration Fail2ban originale ? (o/N): " -r restore_fail2ban if [[ "$restore_fail2ban" =~ ^[Oo]$ ]]; then cp "${BACKUP_DIR}/jail.local" /etc/fail2ban/jail.local 2>/dev/null || true systemctl restart fail2ban 2>/dev/null || true print_success "Configuration Fail2ban restaurée" fi fi ;; esac else print_warning "Fichier de statut non trouvé" fi } reset_all_steps() { print_step "Réinitialisation de toutes les étapes" echo -e "${RED}╔══════════════════════════════════════════════════════════════╗${NC}" echo -e "${RED}║ ⚠ ATTENTION CRITIQUE ⚠ ║${NC}" echo -e "${RED}╚══════════════════════════════════════════════════════════════╝${NC}" echo "" echo "Cette action va:" echo "1. Supprimer l'historique d'exécution de toutes les étapes" echo "2. Permettre une ré-exécution complète du script" echo "3. NE supprime PAS les configurations appliquées" echo "" echo -n "Confirmez-vous la réinitialisation complète ? (oui/NON): " read -r confirmation if [[ "$confirmation" == "oui" ]]; then if [[ -f "$STATUS_FILE" ]]; then cp "$STATUS_FILE" "${STATUS_FILE}.backup.$(date +%Y%m%d_%H%M%S)" print_info "Ancien statut sauvegardé" fi > "$STATUS_FILE" print_success "Toutes les étapes ont été réinitialisées" print_info "Vous pouvez maintenant ré-exécuter le script" echo "" read -p "Voulez-vous restaurer les configurations originales ? (o/N): " -r restore_all if [[ "$restore_all" =~ ^[Oo]$ ]]; then echo "" echo "Sélectionnez les configurations à restaurer:" echo "1) SSH" echo "2) UFW (pare-feu)" echo "3) Fail2ban" echo "4) Toutes les configurations" echo "5) Aucune" echo "" read -p "Choix (1-5): " -r restore_choice case $restore_choice in 1) restore_ssh_config ;; 2) restore_ufw_config ;; 3) restore_fail2ban_config ;; 4) restore_all_configs ;; *) print_info "Aucune restauration effectuée" ;; esac fi else print_error "Réinitialisation annulée" exit 1 fi } restore_ssh_config() { if [[ -f "${BACKUP_DIR}/sshd_config" ]]; then cp "${BACKUP_DIR}/sshd_config" /etc/ssh/sshd_config systemctl restart sshd 2>/dev/null || true print_success "Configuration SSH restaurée" else print_warning "Sauvegarde SSH non trouvée" fi } restore_ufw_config() { if command -v ufw > /dev/null 2>&1; then ufw --force reset > /dev/null 2>&1 || true print_success "Configuration UFW réinitialisée" fi } restore_fail2ban_config() { if [[ -f "${BACKUP_DIR}/jail.local" ]]; then cp "${BACKUP_DIR}/jail.local" /etc/fail2ban/jail.local 2>/dev/null || true systemctl restart fail2ban 2>/dev/null || true print_success "Configuration Fail2ban restaurée" fi } restore_all_configs() { restore_ssh_config restore_ufw_config restore_fail2ban_config } list_all_steps() { echo -e "${CYAN}╔══════════════════════════════════════════════════════════════╗${NC}" echo -e "${CYAN}║ LISTE DES ÉTAPES DISPONIBLES ║${NC}" echo -e "${CYAN}╚══════════════════════════════════════════════════════════════╝${NC}" echo "" local i=1 for step_name in "${!STEP_DESCRIPTIONS[@]}"; do local status="❌ Non exécutée" if check_step_done "$step_name" 2>/dev/null; then status="✅ Terminée" fi printf "${BLUE}%2d.${NC} %-35s ${GREEN}%-12s${NC}\n" \ "$i" "${STEP_DESCRIPTIONS[$step_name]}" "$status" i=$((i + 1)) done echo "" echo -e "${YELLOW}COMMANDES DE CONTRÔLE:${NC}" echo " --force-step=NOM Forcer une étape spécifique" echo " --force-all Forcer toutes les étapes" echo " --skip-step=NOM Sauter une étape spécifique" echo " --reset-step=NOM Réinitialiser une étape" echo " --reset-all Réinitialiser toutes les étapes" echo " --list-steps Lister toutes les étapes" echo " --show-status Afficher le statut des étapes" echo "" if [[ -f "$STATUS_FILE" ]]; then local completed_count=$(wc -l < "$STATUS_FILE" 2>/dev/null || echo 0) echo -e "${GREEN}Étapes terminées: $completed_count/${#STEP_DESCRIPTIONS[@]}${NC}" fi } show_step_status() { echo -e "${CYAN}╔══════════════════════════════════════════════════════════════╗${NC}" echo -e "${CYAN}║ STATUT DES ÉTAPES ║${NC}" echo -e "${CYAN}╚══════════════════════════════════════════════════════════════╝${NC}" echo "" local completed=0 local total=${#STEP_DESCRIPTIONS[@]} local step_order=( "install_security_tools" "detect_open_ports" "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_firewall_ports" "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" ) for step_name in "${step_order[@]}"; do local description="${STEP_DESCRIPTIONS[$step_name]}" local status_color=$RED local status_icon="❌" local status_text="Non exécutée" if check_step_done "$step_name" 2>/dev/null; then status_color=$GREEN status_icon="✅" status_text="Terminée" completed=$((completed + 1)) fi local extra_info="" if [[ " ${FORCE_STEPS[@]} " =~ " ${step_name} " ]]; then extra_info=" [FORCÉ]" elif [[ " ${SKIP_STEPS[@]} " =~ " ${step_name} " ]]; then extra_info=" [IGNORÉ]" fi echo -e "${status_icon} ${status_color}${description}${NC}${extra_info}" done echo "" echo -e "${YELLOW}RÉSUMÉ:${NC}" echo -e " Progression: ${completed}/${total} étapes" local width=50 local percent=$((completed * 100 / total)) local filled=$((percent * width / 100)) local empty=$((width - filled)) echo -n " [" for ((i=0; i/dev/null || echo "Inconnu")" fi } # ============================================================================== # PARSING DES ARGUMENTS # ============================================================================== parse_arguments() { while [[ $# -gt 0 ]]; do case $1 in --force-all) FORCE_ALL=true print_warning "Mode FORCE ALL activé - Toutes les étapes seront ré-exécutées" shift ;; --force-step=*) local step="${1#*=}" FORCE_STEPS+=("$step") print_info "Étape forcée: $step" shift ;; --skip-step=*) local step="${1#*=}" SKIP_STEPS+=("$step") print_info "Étape ignorée: $step" shift ;; --reset-step=*) local step="${1#*=}" reset_step "$step" exit 0 ;; --reset-all) reset_all_steps exit 0 ;; --list-steps) LIST_STEPS=true shift ;; --show-status) SHOW_STATUS=true shift ;; --cleanup-ssh) shift ;; --help|-h) show_help exit 0 ;; *) print_error "Option inconnue: $1" show_help exit 1 ;; esac done } show_help() { echo -e "${CYAN}╔══════════════════════════════════════════════════════════════╗${NC}" echo -e "${CYAN}║ AIDE - SYSTEM HARDENING SCRIPT v8.0 ║${NC}" echo -e "${CYAN}╚══════════════════════════════════════════════════════════════╝${NC}" echo "" echo -e "${GREEN}USAGE:${NC}" echo " $0 [OPTIONS]" echo "" echo -e "${GREEN}OPTIONS:${NC}" echo " --force-all Forcer l'exécution de toutes les étapes" echo " --force-step=NOM Forcer une étape spécifique" echo " --skip-step=NOM Sauter une étape spécifique" echo " --reset-step=NOM Réinitialiser une étape (la marquer comme non faite)" echo " --reset-all Réinitialiser toutes les étapes" echo " --list-steps Lister toutes les étapes disponibles" echo " --show-status Afficher le statut des étapes" echo " --cleanup-ssh Nettoyer le port SSH 22 (après test)" echo " --help, -h Afficher cette aide" echo "" echo -e "${YELLOW}EXEMPLES:${NC}" echo " # Exécution normale" echo " $0" echo "" echo " # Forcer uniquement la configuration SSH" echo " $0 --force-step=harden_ssh" echo "" echo " # Forcer SSH et UFW, sauter Fail2ban" echo " $0 --force-step=harden_ssh --force-step=configure_firewall_ports --skip-step=configure_fail2ban" echo "" echo " # Réinitialiser uniquement la configuration SSH" echo " $0 --reset-step=harden_ssh" echo "" echo " # Voir le statut des étapes" echo " $0 --show-status" echo "" echo -e "${RED}⚠ AVERTISSEMENT:${NC}" echo " Le forçage d'étapes peut écraser des configurations existantes." echo " Assurez-vous d'avoir des sauvegardes avant d'utiliser --force-all." } # ============================================================================== # FONCTIONS DE DÉTECTION DES PORTS # ============================================================================== detect_open_ports() { local step_name="detect_open_ports" if check_step_done "$step_name"; then skip_step "${STEP_DESCRIPTIONS[$step_name]}" return 0 fi print_step "Détection des ports ouverts" print_info "Scan des ports en écoute localement..." ss -tlnp | grep LISTEN | awk '{print $4}' | awk -F: '{print $NF}' | sort -n | uniq > "$OPEN_PORTS_FILE" 2>/dev/null || { netstat -tlnp 2>/dev/null | grep LISTEN | awk '{print $4}' | awk -F: '{print $NF}' | sort -n | uniq > "$OPEN_PORTS_FILE" || { print_warning "Impossible de détecter les ports ouverts" touch "$OPEN_PORTS_FILE" } } local port_count=$(wc -l < "$OPEN_PORTS_FILE" 2>/dev/null || echo 0) if [[ $port_count -gt 0 ]]; then print_info "Ports détectés: $(tr '\n' ' ' < "$OPEN_PORTS_FILE")" print_success "$port_count port(s) ouvert(s) détecté(s)" else print_warning "Aucun port ouvert détecté" fi mark_step_done "$step_name" } is_port_open() { local port="$1" grep -q "^${port}$" "$OPEN_PORTS_FILE" 2>/dev/null } get_ssh_port_to_use() { if detect_lxc; then print_info "Conteneur LXC détecté - vérification du port 22022..." if is_port_open "22022"; then print_info "Port 22022 déjà ouvert dans LXC" echo "22022" else print_warning "Port 22022 non ouvert dans LXC - utilisation du port 22" echo "22" fi else echo "22022" fi } # ============================================================================== # FONCTIONS DE DURCISSEMENT # ============================================================================== install_security_tools() { local step_name="install_security_tools" if check_step_done "$step_name"; then skip_step "${STEP_DESCRIPTIONS[$step_name]}" return 0 fi 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" } configure_process_accounting() { local step_name="configure_process_accounting" if check_step_done "$step_name"; then skip_step "${STEP_DESCRIPTIONS[$step_name]}" return 0 fi 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" if check_step_done "$step_name"; then skip_step "${STEP_DESCRIPTIONS[$step_name]}" return 0 fi 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.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" if check_step_done "$step_name"; then skip_step "${STEP_DESCRIPTIONS[$step_name]}" return 0 fi 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" } configure_pam_password_policy() { local step_name="configure_pam_password_policy" if check_step_done "$step_name"; then skip_step "${STEP_DESCRIPTIONS[$step_name]}" return 0 fi 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 EOF backup_file "/etc/pam.d/common-password" sed -i 's/pam_unix.so.*/& remember=5 sha512 rounds=500000/' /etc/pam.d/common-password 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" } configure_login_defs() { local step_name="configure_login_defs" if check_step_done "$step_name"; then skip_step "${STEP_DESCRIPTIONS[$step_name]}" return 0 fi 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" } configure_umask() { local step_name="configure_umask" if check_step_done "$step_name"; then skip_step "${STEP_DESCRIPTIONS[$step_name]}" return 0 fi 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" } configure_aide_sha512() { local step_name="configure_aide_sha512" if check_step_done "$step_name"; then skip_step "${STEP_DESCRIPTIONS[$step_name]}" return 0 fi 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" } initialize_aide_db() { local step_name="initialize_aide_db" if check_step_done "$step_name"; then skip_step "${STEP_DESCRIPTIONS[$step_name]}" return 0 fi print_step "Initialisation de la base de données AIDE" print_info "Création de la base de données de référence..." 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" } configure_clamav() { local step_name="configure_clamav" if check_step_done "$step_name"; then skip_step "${STEP_DESCRIPTIONS[$step_name]}" return 0 fi 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" 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" } configure_chrony() { local step_name="configure_chrony" if check_step_done "$step_name"; then skip_step "${STEP_DESCRIPTIONS[$step_name]}" return 0 fi print_step "Configuration de la synchronisation horaire (Chrony)" if detect_container; then print_warning "Conteneur détecté - synchronisation horaire gérée par l'hôte" 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é" fi mark_step_done "$step_name" return 0 fi backup_file "/etc/chrony/chrony.conf" timedatectl set-timezone Europe/Paris || { print_warning "Impossible de définir le fuseau horaire" } 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 driftfile /var/lib/chrony/chrony.drift logdir /var/log/chrony makestep 1.0 3 rtcsync EOF 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" fi mark_step_done "$step_name" } harden_ssh() { local step_name="harden_ssh" if check_step_done "$step_name"; then skip_step "${STEP_DESCRIPTIONS[$step_name]}" return 0 fi print_step "Durcissement du service SSH" backup_file "/etc/ssh/sshd_config" local sshd_config="/etc/ssh/sshd_config" update_ssh_param() { local param="$1" local value="$2" local file="$3" sed -i "/^#*${param}/d" "$file" echo "${param} ${value}" >> "$file" } local ssh_port=$(get_ssh_port_to_use) sed -i '/^Port /d' "$sshd_config" sed -i '/^#Port /d' "$sshd_config" echo "Port $ssh_port" >> "$sshd_config" if detect_lxc && [[ "$ssh_port" == "22" ]]; then print_info "LXC détecté - port 22 maintenu" elif [[ "$ssh_port" == "22022" ]]; then echo "Port 22" >> "$sshd_config" print_info "Port 22 ajouté temporairement" fi 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" sed -i '/^Ciphers /d' "$sshd_config" sed -i '/^MACs /d' "$sshd_config" sed -i '/^KexAlgorithms /d' "$sshd_config" cat >> "$sshd_config" << 'EOF' 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 update_ssh_param "Banner" "/etc/issue.net" "$sshd_config" print_info "Test de la configuration SSH..." if sshd -t 2>&1 | tee -a "$LOG_FILE"; then print_success "Configuration SSH valide" 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" local ports=$(grep "^Port" "$sshd_config" | awk '{print $2}' | tr '\n' ' ') print_info "Ports SSH actifs: $ports" if [[ "$ssh_port" == "22022" ]]; then print_warning "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" print_warning "⚠ TESTEZ IMMÉDIATEMENT dans un NOUVEAU terminal:" print_warning " ssh -p $ssh_port $(whoami)@$(hostname -I | awk '{print $1}')" print_warning " NE FERMEZ PAS cette session avant validation!" print_warning "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" elif detect_lxc; then print_warning "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" print_warning "⚠ Conteneur LXC - Port SSH: 22" print_warning " Port 22022 non disponible dans ce conteneur" print_warning "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" fi 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" } configure_banners() { local step_name="configure_banners" if check_step_done "$step_name"; then skip_step "${STEP_DESCRIPTIONS[$step_name]}" return 0 fi print_step "Configuration des bannières légales" local banner_text="╔════════════════════════════════════════════════════════════╗ ║ SYSTÈME SÉCURISÉ ║ ║ ║ ║ Accès réservé aux personnes autorisées uniquement. ║ ║ Toute tentative d'accès non autorisée est interdite ║ ║ et sera tracée et poursuivie selon la loi. ║ ║ ║ ║ Les activités sont surveillées et enregistrées. ║ ╚════════════════════════════════════════════════════════════╝" echo "$banner_text" | tee /etc/issue /etc/issue.net /etc/motd > /dev/null print_success "Bannières légales configurées" mark_step_done "$step_name" } configure_firewall_ports() { local step_name="configure_firewall_ports" if check_step_done "$step_name"; then skip_step "${STEP_DESCRIPTIONS[$step_name]}" return 0 fi print_step "Configuration des règles de pare-feu basées sur les ports détectés" 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" 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é" fi print_success "Configuration pare-feu ignorée" mark_step_done "$step_name" return 0 fi 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 local ssh_port=$(get_ssh_port_to_use) print_info "Autorisation du port SSH $ssh_port..." ufw allow "${ssh_port}/tcp" comment 'SSH sécurisé' || print_warning "Erreur ajout règle SSH:${ssh_port}" if [[ "$ssh_port" != "22" ]] && is_port_open "22"; then print_info "Autorisation temporaire du port SSH 22..." ufw allow 22/tcp comment 'SSH temporaire (à désactiver)' || print_warning "Erreur ajout règle SSH:22" fi declare -A service_ports=( ["HTTP"]="80" ["HTTPS"]="443" ["DNS"]="53" ["DNS-TCP"]="53/tcp" ["DNS-UDP"]="53/udp" ["NTP"]="123" ["NTP-UDP"]="123/udp" ["SMTP"]="25" ["SMTP-Submission"]="587" ["SMTPS"]="465" ["IMAP"]="143" ["IMAPS"]="993" ["POP3"]="110" ["POP3S"]="995" ["FTP"]="21" ["FTP-Data"]="20" ["FTP-SSL"]="990" ["SFTP"]="22" ["MySQL"]="3306" ["PostgreSQL"]="5432" ["MongoDB"]="27017" ["Redis"]="6379" ["Redis-Sentinel"]="26379" ["Elasticsearch"]="9200" ["Elasticsearch-Cluster"]="9300" ["Graylog-Web"]="9000" ["Graylog-API"]="12900" ["Graylog-Syslog"]="514" ["Graylog-Syslog-UDP"]="514/udp" ["Wazuh-Manager"]="1514" ["Wazuh-Manager-UDP"]="1514/udp" ["Wazuh-API"]="55000" ["Prometheus"]="9090" ["Grafana"]="3000" ["Node-Exporter"]="9100" ["Zabbix-Server"]="10051" ["Zabbix-Agent"]="10050" ["Docker-Registry"]="5000" ["Docker-API"]="2375" ["Docker-API-TLS"]="2376" ["Kubernetes-API"]="6443" ["Kubelet-API"]="10250" ["Kube-Proxy"]="10256" ["LDAP"]="389" ["LDAPS"]="636" ["LDAP-Global-Catalog"]="3268" ["LDAPS-Global-Catalog"]="3269" ["Kerberos"]="88" ["Kerberos-UDP"]="88/udp" ["SMB"]="445" ["SMB-UDP"]="445/udp" ["NetBIOS"]="139" ["RDP"]="3389" ["VNC"]="5900" ["VNC-Web"]="5800" ["Matrix-Synapse"]="8008" ["Matrix-Synapse-TLS"]="8448" ["Rocket.Chat"]="3000" ["Mattermost"]="8065" ["Zulip"]="9991" ["Ansible"]="22" ["Salt-Master"]="4505 4506" ["Puppet"]="8140" ["Chef-Server"]="9463" ["NFS"]="2049" ["NFS-UDP"]="2049/udp" ["NFS-Mount"]="20048" ["Samba"]="137 138 139 445" ["Samba-UDP"]="137/udp 138/udp" ["GlusterFS"]="24007 24008 49152-49251" ["Ceph-Mon"]="6789" ["Ceph-OSD"]="6800-7300" ["Bacula-Director"]="9101" ["Bacula-Storage"]="9103" ["Bacula-Client"]="9102" ["Proxmox-Web"]="8006" ["Proxmox-VNC"]="5900-5999" ["Proxmox-SPN"]="3128" ["VMware-ESXi"]="902" ["VirtualBox-RDP"]="3389" ["Gitea"]="3000" ["Gitea-SSH"]="2222" ["Bitwarden"]="80 443" ["Bitwarden-Admin"]="8000" ["Ollama-API"]="11434" ["Teleport-Auth"]="3025" ["Teleport-Proxy"]="3023 3024" ["Teleport-SSH"]="3022" ["Teleport-Kube"]="3026" ["NetBox"]="8000" ["NetBox-SSL"]="8443" ["Nextcloud"]="80 443" ["Jitsi-Meet"]="80 443 10000/udp" ["Jitsi-Videobridge"]="4443" ["Jellyfin"]="8096" ["Jellyfin-SSL"]="8920" ["Plex"]="32400" ["Emby"]="8096" ["Emby-SSL"]="8920" ["GitLab"]="80 443 22" ["Jenkins"]="8080" ["Jenkins-Slave"]="50000" ["SonarQube"]="9000" ["Nexus"]="8081" ["Harbor"]="80 443" ["Harbor-API"]="8280" ["TheHive"]="9000" ["Cortex"]="9001" ["MISP"]="80 443" ["OpenCTI"]="8080" ["Velociraptor"]="8000 8889" ["Suricata"]="" ["Asterisk"]="5060 5061" ["Asterisk-UDP"]="5060/udp" ["FreePBX"]="80 443" ["FreeSWITCH"]="5060 5080 8021" ["RabbitMQ"]="5672 5671 15672" ["RabbitMQ-Management"]="15672" ["Mosquitto"]="1883 8883" ["Mosquitto-Websocket"]="8083 8084" ["Memcached"]="11211" ["Memcached-UDP"]="11211/udp" ["Beanstalkd"]="11300" ["Kong"]="8000 8001 8443 8444" ["Tyk"]="8080" ["Ethereum"]="30303" ["Ethereum-UDP"]="30303/udp" ["Bitcoin"]="8333" ["Bitcoin-Testnet"]="18333" ["Minecraft"]="25565" ["Minecraft-Bedrock"]="19132/udp" ["Steam"]="27015 27016 27017 27018 27019 27020" ["Steam-UDP"]="27015/udp 27016/udp 27017/udp 27018/udp 27019/udp 27020/udp" ["Home Assistant"]="8123" ["Home Assistant-SSL"]="443" ["MQTT-Home"]="1883" ["Zigbee2MQTT"]="8080" ["Active Directory"]="53 88 135 137 138 139 389 445 464 636 3268 3269" ["AD-UDP"]="53/udp 88/udp 123/udp 137/udp 138/udp" ["Exchange"]="25 80 110 143 443 465 587 993 995" ["SharePoint"]="80 443" ["SQL Server"]="1433" ["RPC"]="135" ["NetBIOS-Name"]="137/udp" ["NetBIOS-Datagram"]="138/udp" ["NetBIOS-Session"]="139" ["Apple-File-Service"]="548" ["Apple-Time-Machine"]="548" ["Bonjour"]="5353/udp" ["Google-Cloud-Print"]="5222" ["Google-Cast"]="8009 8443" ) print_info "Analyse des ports ouverts pour services connus..." local services_authorized=0 local services_skipped=0 for service_name in "${!service_ports[@]}"; do local ports="${service_ports[$service_name]}" for port_spec in $ports; do local port_num=$(echo "$port_spec" | sed 's|/.*||') if [[ "$port_num" =~ ^[0-9]+-[0-9]+$ ]]; then local start_port=$(echo "$port_num" | cut -d'-' -f1) local end_port=$(echo "$port_num" | cut -d'-' -f2) local port_in_range_open=false for ((p=start_port; p<=end_port; p++)); do if is_port_open "$p"; then port_in_range_open=true break fi done if $port_in_range_open; then echo -e "${YELLOW} Plage de ports détectée: $port_spec ($service_name)${NC}" read -p " Autoriser la plage $port_spec ? (o/N): " -r confirm_range if [[ "$confirm_range" =~ ^[Oo]$ ]]; then if [[ "$port_spec" == *"/udp" ]]; then ufw allow "$start_port:$end_port/udp" comment "$service_name" && { services_authorized=$((services_authorized + 1)) print_info " ✓ Plage $start_port:$end_port/udp autorisée" } || print_warning " ✗ Erreur avec la plage $port_spec" else ufw allow "$start_port:$end_port/tcp" comment "$service_name" && { services_authorized=$((services_authorized + 1)) print_info " ✓ Plage $start_port:$end_port/tcp autorisée" } || print_warning " ✗ Erreur avec la plage $port_spec" fi else print_info " ✗ Plage $port_spec non autorisée" services_skipped=$((services_skipped + 1)) fi fi else if is_port_open "$port_num"; then print_info " Autorisation du port $port_spec ($service_name)..." if [[ "$port_spec" == *"/udp" ]]; then ufw allow "$port_num/udp" comment "$service_name" && { services_authorized=$((services_authorized + 1)) print_info " ✓ Port $port_num/udp autorisé" } || print_warning " ✗ Erreur avec le port $port_num/udp" else ufw allow "$port_num/tcp" comment "$service_name" && { services_authorized=$((services_authorized + 1)) print_info " ✓ Port $port_num/tcp autorisé" } || print_warning " ✗ Erreur avec le port $port_num/tcp" fi fi fi done done print_info "Recherche de ports ouverts non reconnus..." local unknown_ports_added=0 while read -r port; do [[ "$port" == "22" || "$port" == "22022" || "$port" == "$ssh_port" ]] && continue local port_covered=false for service_name in "${!service_ports[@]}"; do local ports="${service_ports[$service_name]}" for port_spec in $ports; do local port_num=$(echo "$port_spec" | sed 's|/.*||') if [[ "$port_num" == "$port" ]]; then port_covered=true break 2 fi done done if ! $port_covered; then local service_info=$(grep -E "^[^#].*[[:space:]]$port/" /etc/services 2>/dev/null | head -1) local service_desc="" if [[ -n "$service_info" ]]; then service_desc=$(echo "$service_info" | awk '{print $1}') fi if [[ -n "$service_desc" ]]; then echo -e "${YELLOW} Port $port détecté ($service_desc)${NC}" else echo -e "${YELLOW} Port non standard détecté: $port${NC}" fi read -p " Autoriser ce port dans le pare-feu ? (o/N): " -r confirm_port if [[ "$confirm_port" =~ ^[Oo]$ ]]; then ufw allow "${port}/tcp" comment "Port $port ${service_desc:+- $service_desc}" && { unknown_ports_added=$((unknown_ports_added + 1)) print_info " ✓ Port $port/tcp autorisé" } || print_warning " ✗ Erreur avec le port $port/tcp" echo -n " Autoriser aussi en UDP ? (o/N): " read -r confirm_udp if [[ "$confirm_udp" =~ ^[Oo]$ ]]; then ufw allow "${port}/udp" comment "Port $port/udp ${service_desc:+- $service_desc}" && { unknown_ports_added=$((unknown_ports_added + 1)) print_info " ✓ Port $port/udp autorisé" } || print_warning " ✗ Erreur avec le port $port/udp" fi else print_info " ✗ Port $port non autorisé" fi fi done < "$OPEN_PORTS_FILE" print_info "Configuration limitation ICMP..." if [[ -f /etc/ufw/before.rules ]]; then backup_file "/etc/ufw/before.rules" if ! grep -q "Limiter pings" /etc/ufw/before.rules; then cat >> /etc/ufw/before.rules << 'EOF' -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 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é" print_info "Services autorisés:" print_info " • Services reconnus: $services_authorized" print_info " • Services ignorés: $services_skipped" print_info " • Ports personnalisés: $unknown_ports_added" echo "" print_info "Statut UFW:" ufw status numbered | head -30 else 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 cat > "$BACKUP_DIR/detected_services.log" << EOF === SERVICES DÉTECTÉS ET CONFIGURÉS === Date: $(date) Hostname: $(hostname) Port SSH utilisé: $ssh_port Ports ouverts détectés: $(tr '\n' ' ' < "$OPEN_PORTS_FILE") Services configurés dans UFW: $(ufw status | grep -A1000 '^To' | grep -v '^To' | grep -v '^\s*$') === FIN DU RAPPORT === EOF mark_step_done "$step_name" } configure_fail2ban() { local step_name="configure_fail2ban" if check_step_done "$step_name"; then skip_step "${STEP_DESCRIPTIONS[$step_name]}" return 0 fi print_step "Configuration de Fail2ban" backup_file "/etc/fail2ban/jail.conf" local ssh_port=$(get_ssh_port_to_use) 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 banaction = %(banaction_allports)s banaction_allports = iptables-multiport [sshd] enabled = true port = $ssh_port filter = sshd logpath = /var/log/auth.log maxretry = 5 bantime = 1h action = %(action_)s EOF print_info "Fail2ban configuré en mode conteneur" else 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 = $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" fi 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é" 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" fi mark_step_done "$step_name" } remove_unneeded_packages() { local step_name="remove_unneeded_packages" if check_step_done "$step_name"; then skip_step "${STEP_DESCRIPTIONS[$step_name]}" return 0 fi 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" } restrict_file_permissions() { local step_name="restrict_file_permissions" if check_step_done "$step_name"; then skip_step "${STEP_DESCRIPTIONS[$step_name]}" return 0 fi 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" if check_step_done "$step_name"; then skip_step "${STEP_DESCRIPTIONS[$step_name]}" return 0 fi 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 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" } configure_security_limits() { local step_name="configure_security_limits" if check_step_done "$step_name"; then skip_step "${STEP_DESCRIPTIONS[$step_name]}" return 0 fi 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" if check_step_done "$step_name"; then skip_step "${STEP_DESCRIPTIONS[$step_name]}" return 0 fi print_step "Vérification de l'intégrité des paquets" print_info "Vérification avec debsums..." debsums -s 2>&1 | tee -a "$LOG_FILE" || print_warning "Certains paquets ont des erreurs" print_success "Vérification d'intégrité terminée" mark_step_done "$step_name" } configure_automatic_updates() { local step_name="configure_automatic_updates" if check_step_done "$step_name"; then skip_step "${STEP_DESCRIPTIONS[$step_name]}" return 0 fi 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" } configure_aide_cron() { local step_name="configure_aide_cron" if check_step_done "$step_name"; then skip_step "${STEP_DESCRIPTIONS[$step_name]}" return 0 fi 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" } harden_smtp_banner() { local step_name="harden_smtp_banner" if check_step_done "$step_name"; then skip_step "${STEP_DESCRIPTIONS[$step_name]}" return 0 fi 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" } harden_systemd_services() { local step_name="harden_systemd_services" if check_step_done "$step_name"; then skip_step "${STEP_DESCRIPTIONS[$step_name]}" return 0 fi 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 print_warning "Conteneur détecté - durcissement systemd limité" 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" mark_step_done "$step_name" return 0 fi local services=(ssh sshd fail2ban chrony) local hardened=0 for service in "${services[@]}"; do local unit="${service}.service" 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" if [[ "$service" == "ssh" || "$service" == "sshd" ]]; then 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 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 systemctl daemon-reload 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" } configure_advanced_pam() { local step_name="configure_advanced_pam" if check_step_done "$step_name"; then skip_step "${STEP_DESCRIPTIONS[$step_name]}" return 0 fi 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 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" } check_partition_layout() { local step_name="check_partition_layout" if check_step_done "$step_name"; then skip_step "${STEP_DESCRIPTIONS[$step_name]}" return 0 fi 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" } check_vmlinuz() { local step_name="check_vmlinuz" if check_step_done "$step_name"; then skip_step "${STEP_DESCRIPTIONS[$step_name]}" return 0 fi 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" } run_chkrootkit() { local step_name="run_chkrootkit" if check_step_done "$step_name"; then skip_step "${STEP_DESCRIPTIONS[$step_name]}" return 0 fi 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" print_success "Scan chkrootkit terminé" mark_step_done "$step_name" } prepare_ssh_cleanup() { local step_name="prepare_ssh_cleanup" if check_step_done "$step_name"; then skip_step "${STEP_DESCRIPTIONS[$step_name]}" return 0 fi print_step "Préparation nettoyage port SSH 22" local ssh_port=$(get_ssh_port_to_use) if [[ "$ssh_port" == "22022" ]]; then print_warning "Port SSH 22 toujours actif (temporaire)" print_info "Après avoir testé SSH sur le port $ssh_port:" print_info "Exécutez: $0 --cleanup-ssh" else print_info "Conteneur LXC - Port SSH principal: 22" print_info "Aucun nettoyage nécessaire" fi mark_step_done "$step_name" } run_lynis_audit() { local step_name="run_lynis_audit" if check_step_done "$step_name"; then skip_step "${STEP_DESCRIPTIONS[$step_name]}" return 0 fi print_step "Exécution de l'audit Lynis" print_info "Audit système complet en cours..." lynis audit system --quick --no-colors > "$SECURITY_REPORT" 2>&1 local score=$(grep -i "Hardening index" "$SECURITY_REPORT" | grep -oP '\d+' | head -1) local max_score=100 [[ -z "$score" ]] && score=0 echo "$score" > "/tmp/lynis_score.txt" echo "$max_score" > "/tmp/lynis_max_score.txt" local percentage=$((score * 100 / max_score)) local bar_length=50 local filled_length=$((percentage * bar_length / 100)) local empty_length=$((bar_length - filled_length)) 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 "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" 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 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 "" if detect_lxc && ! is_port_open "22022"; then print_warning "Conteneur LXC détecté - port 22022 non disponible" print_info "Le port 22 restera actif dans ce conteneur" echo "" echo -n "Continuer malgré tout ? (oui/non): " read -r confirmation [[ "$confirmation" != "oui" ]] && { print_info "Annulation - port 22 maintenu" return 0 } fi 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 if systemctl is-active --quiet ufw 2>/dev/null; then ufw delete allow 22/tcp 2>/dev/null || true fi 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É v8.0 ║" echo "║ Sécurité Debian/Ubuntu ║" echo "║ avec détection automatique des ports ║" 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 " • Détection ports: Activée" 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}" 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 if [[ "$lynis_score" != "N/A" ]]; then local percentage=$((lynis_score * 100 / lynis_max_score)) local bar_length=25 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 "" if [[ -f "$OPEN_PORTS_FILE" ]]; then echo -e "${YELLOW}🌐 PORTS DÉTECTÉS ET CONFIGURÉS:${NC}" echo " • Ports ouverts détectés: $(tr '\n' ' ' < "$OPEN_PORTS_FILE" 2>/dev/null || echo "Aucun")" if systemctl is-active --quiet ufw 2>/dev/null; then echo " • Règles UFW appliquées: $(ufw status | grep -c 'ALLOW') règle(s)" fi echo "" fi 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 " • Ports détectés: $OPEN_PORTS_FILE" echo "" 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" if detect_lxc && ! is_port_open "22022"; then echo " • SSH: Port 22 maintenu (port 22022 non disponible en LXC)" fi 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 "" 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 if [[ "$ssh_port" == "22022" ]]; then echo -e "${RED}══════════════════════════════════════════════════════════════════${NC}" echo -e "${RED}⚠ TESTEZ SSH:22022 IMMÉDIATEMENT AVANT TOUTE AUTRE ACTION ⚠${NC}" echo -e "${RED}══════════════════════════════════════════════════════════════════${NC}" fi rm -f /tmp/lynis_score.txt /tmp/lynis_max_score.txt 2>/dev/null || true } main() { parse_arguments "$@" if $LIST_STEPS; then list_all_steps exit 0 fi if $SHOW_STATUS; then show_step_status exit 0 fi if [[ $# -eq 1 && "$1" == "--cleanup-ssh" ]]; then cleanup_ssh_port return 0 fi check_requirements if [[ ${#FORCE_STEPS[@]} -gt 0 ]]; then echo -e "${YELLOW}Étapes forcées:${NC} ${FORCE_STEPS[*]}" fi if [[ ${#SKIP_STEPS[@]} -gt 0 ]]; then echo -e "${YELLOW}Étapes ignorées:${NC} ${SKIP_STEPS[*]}" fi if $FORCE_ALL; then echo -e "${RED}MODE FORCE ALL ACTIVÉ - Toutes les étapes seront ré-exécutées${NC}" echo "" read -p "Appuyez sur Entrée pour continuer ou Ctrl+C pour annuler..." fi print_header log_message "==================================================" "START" log_message "Démarrage durcissement système v8.0" "START" log_message "Système: $(hostname)" "START" log_message "Type: $(detect_container && echo "Conteneur" || echo "Physique/VM")" "START" log_message "Mode: $($FORCE_ALL && echo "FORCE ALL" || echo "Normal")" "START" log_message "Étapes forcées: ${FORCE_STEPS[*]}" "START" log_message "Étapes ignorées: ${SKIP_STEPS[*]}" "START" log_message "==================================================" "START" install_security_tools detect_open_ports 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_firewall_ports 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 print_summary log_message "==================================================" "END" log_message "Durcissement terminé - Durée: $((SECONDS / 60)) min" "END" log_message "Mode: $($FORCE_ALL && echo "FORCE ALL" || echo "Normal")" "END" log_message "Ports détectés: $(tr '\n' ' ' < "$OPEN_PORTS_FILE" 2>/dev/null || echo "Aucun")" "END" log_message "==================================================" "END" } # ============================================================================== # EXÉCUTION PRINCIPALE # ============================================================================== trap 'print_error "Script interrompu"; exit 130' INT TERM [[ "${BASH_SOURCE[0]}" == "${0}" ]] && main "$@"