From 7b70acd9f19a92276778f30074c24f86d0ab4efd Mon Sep 17 00:00:00 2001 From: Johnny Date: Fri, 2 Jan 2026 09:22:53 +0000 Subject: [PATCH] Actualiser system_hardening_optimized.sh --- system_hardening_optimized.sh | 1298 +++++++++++++++++++++++++++------ 1 file changed, 1077 insertions(+), 221 deletions(-) diff --git a/system_hardening_optimized.sh b/system_hardening_optimized.sh index 947cdac..4ce5fe2 100644 --- a/system_hardening_optimized.sh +++ b/system_hardening_optimized.sh @@ -2,10 +2,11 @@ ################################################################################ # Script: system_hardening_optimized.sh -# Version: 6.0 +# 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 ################################################################################ @@ -20,10 +21,19 @@ 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=30 +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 # ============================================================================== @@ -36,6 +46,44 @@ 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 # ============================================================================== @@ -75,17 +123,46 @@ print_info() { log_message "$1" "INFO" } +# ============================================================================== +# GESTION DES ÉTAPES +# ============================================================================== + check_step_done() { - grep -q "^${1}$" "$STATUS_FILE" 2>/dev/null + 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() { - echo "$1" >> "$STATUS_FILE" - log_message "Étape '$1' marquée comme terminée" "STATUS" + 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() { - print_info "Étape déjà effectuée : $1" + local step_name="$1" + print_info "Étape déjà effectuée : $step_name" CURRENT_STEP=$((CURRENT_STEP + 1)) } @@ -94,6 +171,10 @@ detect_container() { [[ -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" ]] && { @@ -125,14 +206,430 @@ update_config_value() { 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 # ============================================================================== -# ÉTAPE 1: Mise à jour système et installation outils install_security_tools() { local step_name="install_security_tools" - check_step_done "$step_name" && { skip_step "$step_name"; return 0; } + 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é" @@ -155,10 +652,12 @@ install_security_tools() { mark_step_done "$step_name" } -# ÉTAPE 2: Configuration Process Accounting configure_process_accounting() { local step_name="configure_process_accounting" - check_step_done "$step_name" && { skip_step "$step_name"; return 0; } + if check_step_done "$step_name"; then + skip_step "${STEP_DESCRIPTIONS[$step_name]}" + return 0 + fi print_step "Configuration du Process Accounting" @@ -181,15 +680,16 @@ configure_process_accounting() { mark_step_done "$step_name" } -# ÉTAPE 3: Durcissement sysctl configure_sysctl_security() { local step_name="configure_sysctl_security" - check_step_done "$step_name" && { skip_step "$step_name"; return 0; } + 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' -# Sécurité réseau IPv4 net.ipv4.conf.all.accept_source_route = 0 net.ipv4.conf.all.accept_redirects = 0 net.ipv4.conf.all.send_redirects = 0 @@ -201,21 +701,15 @@ net.ipv4.conf.default.log_martians = 1 net.ipv4.icmp_echo_ignore_broadcasts = 1 net.ipv4.icmp_ignore_bogus_error_responses = 1 net.ipv4.tcp_syncookies = 1 - -# Sécurité réseau IPv6 net.ipv6.conf.all.accept_redirects = 0 net.ipv6.conf.all.accept_source_route = 0 net.ipv6.conf.default.accept_redirects = 0 net.ipv6.conf.default.accept_source_route = 0 - -# Sécurité noyau kernel.randomize_va_space = 2 kernel.kptr_restrict = 2 kernel.dmesg_restrict = 1 kernel.yama.ptrace_scope = 1 kernel.unprivileged_bpf_disabled = 1 - -# Sécurité système fs.suid_dumpable = 0 fs.protected_fifos = 2 fs.protected_regular = 2 @@ -230,10 +724,12 @@ EOF mark_step_done "$step_name" } -# ÉTAPE 4: Configuration permissions logs configure_log_permissions() { local step_name="configure_log_permissions" - check_step_done "$step_name" && { skip_step "$step_name"; return 0; } + 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" @@ -247,18 +743,18 @@ configure_log_permissions() { mark_step_done "$step_name" } -# ÉTAPE 5: Politique mots de passe PAM configure_pam_password_policy() { local step_name="configure_pam_password_policy" - check_step_done "$step_name" && { skip_step "$step_name"; return 0; } + 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' - -# Configuration renforcée minlen = 14 minclass = 3 maxrepeat = 3 @@ -272,10 +768,8 @@ EOF backup_file "/etc/pam.d/common-password" - # Ajouter remember et sha512 sed -i 's/pam_unix.so.*/& remember=5 sha512 rounds=500000/' /etc/pam.d/common-password - # Ajouter pwquality si absent grep -q "pam_pwquality.so" /etc/pam.d/common-password || \ sed -i '1i password requisite pam_pwquality.so retry=3' /etc/pam.d/common-password @@ -283,10 +777,12 @@ EOF mark_step_done "$step_name" } -# ÉTAPE 6: Configuration login.defs configure_login_defs() { local step_name="configure_login_defs" - check_step_done "$step_name" && { skip_step "$step_name"; return 0; } + 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)" @@ -306,10 +802,12 @@ configure_login_defs() { mark_step_done "$step_name" } -# ÉTAPE 7: Configuration umask global configure_umask() { local step_name="configure_umask" - check_step_done "$step_name" && { skip_step "$step_name"; return 0; } + 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" @@ -323,10 +821,12 @@ configure_umask() { mark_step_done "$step_name" } -# ÉTAPE 8: Configuration AIDE SHA512 configure_aide_sha512() { local step_name="configure_aide_sha512" - check_step_done "$step_name" && { skip_step "$step_name"; return 0; } + if check_step_done "$step_name"; then + skip_step "${STEP_DESCRIPTIONS[$step_name]}" + return 0 + fi print_step "Configuration AIDE pour SHA512" @@ -340,14 +840,16 @@ configure_aide_sha512() { mark_step_done "$step_name" } -# ÉTAPE 9: Initialisation AIDE initialize_aide_db() { local step_name="initialize_aide_db" - check_step_done "$step_name" && { skip_step "$step_name"; return 0; } + 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 (peut prendre plusieurs minutes)..." + 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" || { @@ -372,16 +874,18 @@ initialize_aide_db() { mark_step_done "$step_name" } -# ÉTAPE 10: Configuration ClamAV configure_clamav() { local step_name="configure_clamav" - check_step_done "$step_name" && { skip_step "$step_name"; return 0; } + 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 (normal si déjà récent)" + freshclam || print_warning "Échec mise à jour ClamAV" systemctl enable clamav-freshclam systemctl start clamav-freshclam @@ -392,67 +896,60 @@ configure_clamav() { mark_step_done "$step_name" } -# ÉTAPE 11: Configuration Chrony configure_chrony() { local step_name="configure_chrony" - check_step_done "$step_name" && { skip_step "$step_name"; return 0; } + if check_step_done "$step_name"; then + skip_step "${STEP_DESCRIPTIONS[$step_name]}" + return 0 + fi print_step "Configuration de la synchronisation horaire (Chrony)" - # Vérifier si on est dans un conteneur if detect_container; then print_warning "Conteneur détecté - synchronisation horaire gérée par l'hôte" - print_info "La configuration de Chrony est ignorée en environnement conteneurisé" - # Désactiver/masquer chrony s'il est présent if systemctl list-unit-files | grep -q "^chrony.service"; then systemctl stop chrony 2>/dev/null || true systemctl disable chrony 2>/dev/null || true systemctl mask chrony 2>/dev/null || true - print_info "Service Chrony désactivé (non nécessaire en conteneur)" + print_info "Service Chrony désactivé" fi mark_step_done "$step_name" return 0 fi - # Configuration pour système physique/VM backup_file "/etc/chrony/chrony.conf" - # Configurer le fuseau horaire timedatectl set-timezone Europe/Paris || { print_warning "Impossible de définir le fuseau horaire" } cat > /etc/chrony/chrony.conf << 'EOF' -# Serveurs NTP pool 2.debian.pool.ntp.org iburst server 0.fr.pool.ntp.org iburst server 1.fr.pool.ntp.org iburst - -# Fichiers de drift et log driftfile /var/lib/chrony/chrony.drift logdir /var/log/chrony - -# Restrictions makestep 1.0 3 rtcsync EOF - # Activer et démarrer le service if systemctl enable chrony 2>/dev/null && systemctl restart chrony 2>/dev/null; then print_success "Chrony configuré et démarré" else - print_warning "Erreur lors du démarrage de Chrony - service peut-être non disponible" + print_warning "Erreur lors du démarrage de Chrony" fi mark_step_done "$step_name" } -# ÉTAPE 12: Durcissement SSH harden_ssh() { local step_name="harden_ssh" - check_step_done "$step_name" && { skip_step "$step_name"; return 0; } + if check_step_done "$step_name"; then + skip_step "${STEP_DESCRIPTIONS[$step_name]}" + return 0 + fi print_step "Durcissement du service SSH" @@ -460,25 +957,27 @@ harden_ssh() { local sshd_config="/etc/ssh/sshd_config" - # Fonction helper pour mettre à jour une valeur update_ssh_param() { local param="$1" local value="$2" local file="$3" - - # Supprimer toutes les occurrences (commentées ou non) sed -i "/^#*${param}/d" "$file" - # Ajouter la nouvelle valeur echo "${param} ${value}" >> "$file" } - # Configuration des ports + local ssh_port=$(get_ssh_port_to_use) + sed -i '/^Port /d' "$sshd_config" sed -i '/^#Port /d' "$sshd_config" - echo "Port 22" >> "$sshd_config" - echo "Port $NEW_SSH_PORT" >> "$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 - # Paramètres de sécurité avec la fonction helper update_ssh_param "PermitRootLogin" "yes" "$sshd_config" update_ssh_param "PasswordAuthentication" "no" "$sshd_config" update_ssh_param "PubkeyAuthentication" "yes" "$sshd_config" @@ -496,40 +995,41 @@ harden_ssh() { update_ssh_param "Compression" "no" "$sshd_config" update_ssh_param "Protocol" "2" "$sshd_config" - # Algorithmes sécurisés (s'assurer qu'ils n'existent pas déjà) sed -i '/^Ciphers /d' "$sshd_config" sed -i '/^MACs /d' "$sshd_config" sed -i '/^KexAlgorithms /d' "$sshd_config" cat >> "$sshd_config" << 'EOF' - -# Algorithmes cryptographiques sécurisés Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512,hmac-sha2-256 KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256 EOF - # Bannière update_ssh_param "Banner" "/etc/issue.net" "$sshd_config" - # Test de la configuration avant application print_info "Test de la configuration SSH..." if sshd -t 2>&1 | tee -a "$LOG_FILE"; then print_success "Configuration SSH valide" - # Redémarrer le service print_info "Redémarrage du service SSH..." if systemctl restart sshd 2>/dev/null || systemctl restart ssh 2>/dev/null; then print_success "SSH durci avec succès" - # Afficher les ports configurés local ports=$(grep "^Port" "$sshd_config" | awk '{print $2}' | tr '\n' ' ') print_info "Ports SSH actifs: $ports" - print_warning "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - print_warning "⚠ TESTEZ IMMÉDIATEMENT dans un NOUVEAU terminal:" - print_warning " ssh -p $NEW_SSH_PORT $(whoami)@$(hostname -I | awk '{print $1}')" - print_warning " NE FERMEZ PAS cette session avant validation!" - print_warning "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + + 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..." @@ -547,10 +1047,12 @@ EOF mark_step_done "$step_name" } -# ÉTAPE 13: Configuration bannières configure_banners() { local step_name="configure_banners" - check_step_done "$step_name" && { skip_step "$step_name"; return 0; } + if check_step_done "$step_name"; then + skip_step "${STEP_DESCRIPTIONS[$step_name]}" + return 0 + fi print_step "Configuration des bannières légales" @@ -570,34 +1072,32 @@ configure_banners() { mark_step_done "$step_name" } -# ÉTAPE 14: Configuration UFW -configure_ufw_firewall() { - local step_name="configure_ufw_firewall" - check_step_done "$step_name" && { skip_step "$step_name"; return 0; } +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 du pare-feu UFW" + print_step "Configuration des règles de pare-feu basées sur les ports détectés" - # Vérifier si on est dans un conteneur if detect_container; then print_warning "Conteneur détecté - pare-feu géré par l'hôte Proxmox" print_info "UFW ne peut pas être configuré dans un conteneur LXC" - print_info "Les règles de pare-feu doivent être configurées au niveau de l'hôte" - # Désactiver UFW s'il est actif if systemctl is-active --quiet ufw 2>/dev/null; then ufw --force disable 2>/dev/null || true systemctl stop ufw 2>/dev/null || true systemctl disable ufw 2>/dev/null || true systemctl mask ufw 2>/dev/null || true - print_info "Service UFW désactivé (non nécessaire en conteneur)" + print_info "Service UFW désactivé" fi - print_success "Configuration pare-feu ignorée (environnement conteneurisé)" + print_success "Configuration pare-feu ignorée" mark_step_done "$step_name" return 0 fi - # Configuration pour système physique/VM backup_file "/etc/ufw/user.rules" backup_file "/etc/ufw/user6.rules" @@ -612,33 +1112,310 @@ configure_ufw_firewall() { ufw default deny incoming ufw default allow outgoing - # Ports SSH - print_info "Autorisation des ports SSH..." - ufw allow 22/tcp comment 'SSH temporaire' || print_warning "Erreur ajout règle SSH:22" - ufw allow "${NEW_SSH_PORT}/tcp" comment 'SSH sécurisé' || print_warning "Erreur ajout règle SSH:${NEW_SSH_PORT}" + 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" - # Limiter ICMP print_info "Configuration limitation ICMP..." if [[ -f /etc/ufw/before.rules ]]; then backup_file "/etc/ufw/before.rules" - # Vérifier si la règle n'existe pas déjà if ! grep -q "Limiter pings" /etc/ufw/before.rules; then cat >> /etc/ufw/before.rules << 'EOF' - -# Limiter pings -A ufw-before-input -p icmp --icmp-type echo-request -m limit --limit 3/second --limit-burst 5 -j ACCEPT -A ufw-before-input -p icmp --icmp-type echo-request -j DROP EOF fi fi - # Activer UFW print_info "Activation du pare-feu..." if echo "y" | ufw --force enable > /dev/null 2>&1; then - print_success "Pare-feu UFW configuré et activé" + 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 - # Fallback if ufw enable <<< 'y' > /dev/null 2>&1; then print_success "Pare-feu UFW configuré et activé (fallback)" else @@ -646,19 +1423,36 @@ EOF 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" } -# ÉTAPE 15: Configuration Fail2ban configure_fail2ban() { local step_name="configure_fail2ban" - check_step_done "$step_name" && { skip_step "$step_name"; return 0; } + 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" - # Configuration adaptée selon l'environnement + local ssh_port=$(get_ssh_port_to_use) + if detect_container; then print_warning "Conteneur détecté - configuration Fail2ban limitée" @@ -669,24 +1463,20 @@ findtime = 10m maxretry = 3 ignoreip = 127.0.0.1/8 ::1 backend = systemd - -# Mode conteneur - pas d'action iptables banaction = %(banaction_allports)s banaction_allports = iptables-multiport [sshd] enabled = true -port = 22,${NEW_SSH_PORT} +port = $ssh_port filter = sshd logpath = /var/log/auth.log maxretry = 5 bantime = 1h -# Action désactivée en conteneur (pas d'accès iptables) action = %(action_)s EOF - print_info "Fail2ban configuré en mode conteneur (logging seul, sans bannissement iptables)" + print_info "Fail2ban configuré en mode conteneur" else - # Configuration complète pour système physique/VM cat > /etc/fail2ban/jail.local << EOF [DEFAULT] bantime = 1h @@ -697,7 +1487,7 @@ backend = systemd [sshd] enabled = true -port = 22,${NEW_SSH_PORT} +port = $ssh_port filter = sshd logpath = /var/log/auth.log maxretry = 5 @@ -709,16 +1499,14 @@ port = http,https logpath = /var/log/apache*/*error.log maxretry = 3 EOF - print_info "Fail2ban configuré en mode complet avec bannissement iptables" + print_info "Fail2ban configuré en mode complet" fi - # Activer et démarrer le service systemctl enable fail2ban 2>&1 | tee -a "$LOG_FILE" || true if systemctl restart fail2ban 2>&1 | tee -a "$LOG_FILE"; then print_success "Fail2ban configuré et démarré" - # Vérifier le statut sleep 2 if systemctl is-active --quiet fail2ban; then print_info "Statut Fail2ban: $(systemctl is-active fail2ban)" @@ -726,16 +1514,18 @@ EOF print_warning "Fail2ban démarré mais statut incertain" fi else - print_warning "Problème au démarrage de Fail2ban - vérifiez les logs: journalctl -xeu fail2ban" + print_warning "Problème au démarrage de Fail2ban" fi mark_step_done "$step_name" } -# ÉTAPE 16: Suppression paquets inutiles remove_unneeded_packages() { local step_name="remove_unneeded_packages" - check_step_done "$step_name" && { skip_step "$step_name"; return 0; } + if check_step_done "$step_name"; then + skip_step "${STEP_DESCRIPTIONS[$step_name]}" + return 0 + fi print_step "Suppression des paquets inutiles" @@ -754,10 +1544,12 @@ remove_unneeded_packages() { mark_step_done "$step_name" } -# ÉTAPE 17: Restriction permissions restrict_file_permissions() { local step_name="restrict_file_permissions" - check_step_done "$step_name" && { skip_step "$step_name"; return 0; } + if check_step_done "$step_name"; then + skip_step "${STEP_DESCRIPTIONS[$step_name]}" + return 0 + fi print_step "Restriction des permissions des fichiers critiques" @@ -775,15 +1567,16 @@ restrict_file_permissions() { mark_step_done "$step_name" } -# ÉTAPE 18: Désactivation modules noyau disable_risky_kernel_modules() { local step_name="disable_risky_kernel_modules" - check_step_done "$step_name" && { skip_step "$step_name"; return 0; } + 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' -# Protocoles réseau non utilisés blacklist dccp install dccp /bin/true blacklist sctp @@ -792,8 +1585,6 @@ blacklist rds install rds /bin/true blacklist tipc install tipc /bin/true - -# Systèmes de fichiers non utilisés blacklist cramfs install cramfs /bin/true blacklist freevxfs @@ -808,12 +1599,8 @@ blacklist squashfs install squashfs /bin/true blacklist udf install udf /bin/true - -# FireWire (DMA attack) blacklist firewire-core install firewire-core /bin/true - -# Thunderbolt (DMA attack) blacklist thunderbolt install thunderbolt /bin/true EOF @@ -826,25 +1613,21 @@ EOF mark_step_done "$step_name" } -# ÉTAPE 19: Configuration limites sécurité configure_security_limits() { local step_name="configure_security_limits" - check_step_done "$step_name" && { skip_step "$step_name"; return 0; } + 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' - -# Désactiver core dumps * hard core 0 - -# Limiter les processus * soft nproc 512 * hard nproc 1024 - -# Limiter les fichiers ouverts * soft nofile 65536 * hard nofile 65536 EOF @@ -853,24 +1636,28 @@ EOF mark_step_done "$step_name" } -# ÉTAPE 20: Vérification intégrité paquets verify_packages_integrity() { local step_name="verify_packages_integrity" - check_step_done "$step_name" && { skip_step "$step_name"; return 0; } + 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 (peut prendre du temps)..." + 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" } -# ÉTAPE 21: Configuration mises à jour auto configure_automatic_updates() { local step_name="configure_automatic_updates" - check_step_done "$step_name" && { skip_step "$step_name"; return 0; } + if check_step_done "$step_name"; then + skip_step "${STEP_DESCRIPTIONS[$step_name]}" + return 0 + fi print_step "Configuration des mises à jour automatiques" @@ -879,7 +1666,6 @@ 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"; @@ -901,10 +1687,12 @@ EOF mark_step_done "$step_name" } -# ÉTAPE 22: Configuration tâche AIDE cron configure_aide_cron() { local step_name="configure_aide_cron" - check_step_done "$step_name" && { skip_step "$step_name"; return 0; } + 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" @@ -936,10 +1724,12 @@ EOF mark_step_done "$step_name" } -# ÉTAPE 23: Durcissement bannière SMTP harden_smtp_banner() { local step_name="harden_smtp_banner" - check_step_done "$step_name" && { skip_step "$step_name"; return 0; } + if check_step_done "$step_name"; then + skip_step "${STEP_DESCRIPTIONS[$step_name]}" + return 0 + fi print_step "Durcissement bannière SMTP" @@ -962,10 +1752,12 @@ harden_smtp_banner() { mark_step_done "$step_name" } -# ÉTAPE 24: Durcissement services systemd harden_systemd_services() { local step_name="harden_systemd_services" - check_step_done "$step_name" && { skip_step "$step_name"; return 0; } + if check_step_done "$step_name"; then + skip_step "${STEP_DESCRIPTIONS[$step_name]}" + return 0 + fi print_step "Durcissement services systemd" @@ -975,13 +1767,9 @@ harden_systemd_services() { return 0 } - # Vérifier si on est dans un conteneur if detect_container; then print_warning "Conteneur détecté - durcissement systemd limité" - print_info "Les namespaces systemd ne sont pas supportés dans les conteneurs LXC" - print_info "Le durcissement systemd est ignoré pour éviter les erreurs de démarrage" - # Nettoyer les éventuelles configurations précédentes qui pourraient causer des problèmes local services=(ssh sshd fail2ban chrony) for service in "${services[@]}"; do local unit="${service}.service" @@ -994,19 +1782,17 @@ harden_systemd_services() { done systemctl daemon-reload - print_success "Configuration systemd nettoyée pour compatibilité conteneur" + print_success "Configuration systemd nettoyée" mark_step_done "$step_name" return 0 fi - # Configuration pour système physique/VM local services=(ssh sshd fail2ban chrony) local hardened=0 for service in "${services[@]}"; do local unit="${service}.service" - # Vérifier si le service existe et est actif ou enabled systemctl list-unit-files | grep -q "^${unit}" || continue if ! systemctl is-enabled "$unit" > /dev/null 2>&1 && \ @@ -1017,9 +1803,7 @@ harden_systemd_services() { mkdir -p "/etc/systemd/system/${unit}.d" - # Configuration de sécurité adaptée selon le service if [[ "$service" == "ssh" || "$service" == "sshd" ]]; then - # Configuration plus permissive pour SSH cat > "/etc/systemd/system/${unit}.d/security.conf" << EOF [Service] NoNewPrivileges=yes @@ -1032,7 +1816,6 @@ ProtectControlGroups=yes RestrictRealtime=yes EOF else - # Configuration standard pour les autres services cat > "/etc/systemd/system/${unit}.d/security.conf" << EOF [Service] NoNewPrivileges=yes @@ -1052,10 +1835,8 @@ EOF print_info "Service $unit durci" done - # Recharger la configuration systemd systemctl daemon-reload - # Vérifier que les services critiques démarrent toujours print_info "Vérification des services critiques..." for service in ssh sshd; do local unit="${service}.service" @@ -1075,16 +1856,17 @@ EOF mark_step_done "$step_name" } -# ÉTAPE 25: Configuration PAM avancée configure_advanced_pam() { local step_name="configure_advanced_pam" - check_step_done "$step_name" && { skip_step "$step_name"; return 0; } + 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" - # S'assurer sha512 et rounds grep -q "pam_unix.so" /etc/pam.d/common-password && { grep -q "sha512" /etc/pam.d/common-password || \ sed -i 's/pam_unix.so.*/& sha512/' /etc/pam.d/common-password @@ -1093,7 +1875,6 @@ configure_advanced_pam() { sed -i 's/pam_unix.so.*/& rounds=500000/' /etc/pam.d/common-password } - # Appliquer expiration mots de passe print_info "Application expiration mots de passe aux utilisateurs..." while IFS=: read -r user _ uid _ _ _ shell; do [[ "$uid" -ge 1000 || "$user" == "root" ]] && \ @@ -1110,10 +1891,12 @@ configure_advanced_pam() { mark_step_done "$step_name" } -# ÉTAPE 26: Vérification partitions check_partition_layout() { local step_name="check_partition_layout" - check_step_done "$step_name" && { skip_step "$step_name"; return 0; } + if check_step_done "$step_name"; then + skip_step "${STEP_DESCRIPTIONS[$step_name]}" + return 0 + fi print_step "Vérification disposition partitions" @@ -1140,10 +1923,12 @@ check_partition_layout() { mark_step_done "$step_name" } -# ÉTAPE 27: Vérification fichiers noyau check_vmlinuz() { local step_name="check_vmlinuz" - check_step_done "$step_name" && { skip_step "$step_name"; return 0; } + if check_step_done "$step_name"; then + skip_step "${STEP_DESCRIPTIONS[$step_name]}" + return 0 + fi print_step "Vérification fichiers noyau" @@ -1170,65 +1955,72 @@ check_vmlinuz() { mark_step_done "$step_name" } -# ÉTAPE 28: Exécution chkrootkit run_chkrootkit() { local step_name="run_chkrootkit" - check_step_done "$step_name" && { skip_step "$step_name"; return 0; } + 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 (voir $BACKUP_DIR/chkrootkit_report.log)" + print_warning "Chkrootkit a détecté des avertissements" print_success "Scan chkrootkit terminé" mark_step_done "$step_name" } -# ÉTAPE 29: Nettoyage port SSH 22 temporaire prepare_ssh_cleanup() { local step_name="prepare_ssh_cleanup" - check_step_done "$step_name" && { skip_step "$step_name"; return 0; } + if check_step_done "$step_name"; then + skip_step "${STEP_DESCRIPTIONS[$step_name]}" + return 0 + fi print_step "Préparation nettoyage port SSH 22" - print_warning "Port SSH 22 toujours actif (temporaire)" - print_info "Après avoir testé SSH sur le port $NEW_SSH_PORT:" - print_info "Exécutez: $0 --cleanup-ssh" + 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" } -# ÉTAPE 30: Audit Lynis final run_lynis_audit() { local step_name="run_lynis_audit" - check_step_done "$step_name" && { skip_step "$step_name"; return 0; } + 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 (peut prendre plusieurs minutes)..." + print_info "Audit système complet en cours..." - # Exécuter Lynis et sauvegarder le rapport lynis audit system --quick --no-colors > "$SECURITY_REPORT" 2>&1 - # Extraire le score de durcissement local score=$(grep -i "Hardening index" "$SECURITY_REPORT" | grep -oP '\d+' | head -1) - local max_score=100 # Score maximum standard Lynis + local max_score=100 - # Si on ne trouve pas le score dans le rapport, utiliser une valeur par défaut [[ -z "$score" ]] && score=0 - # Sauvegarder le score pour le résumé echo "$score" > "/tmp/lynis_score.txt" echo "$max_score" > "/tmp/lynis_max_score.txt" - # Calculer le pourcentage pour la barre de progression local percentage=$((score * 100 / max_score)) - local bar_length=50 # Longueur de la barre en caractères + local bar_length=50 local filled_length=$((percentage * bar_length / 100)) local empty_length=$((bar_length - filled_length)) - # Créer la barre de progression local bar="[" for ((i=0; i/dev/null || true + + 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 @@ -1367,8 +2170,9 @@ print_header() { echo -e "${CYAN}" echo "╔══════════════════════════════════════════════════════════════════╗" - echo "║ SCRIPT DE DURCISSEMENT SYSTÈME OPTIMISÉ v6.0 ║" + 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}" @@ -1380,6 +2184,7 @@ print_header() { 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" @@ -1398,7 +2203,6 @@ print_summary() { echo "╚══════════════════════════════════════════════════════════════════╝" echo -e "${NC}" - # Récupérer le score Lynis local lynis_score="N/A" local lynis_max_score=100 @@ -1410,11 +2214,9 @@ print_summary() { [[ -z "$lynis_score" ]] && lynis_score="N/A" fi - # Afficher le score avec barre de progression dans le résumé if [[ "$lynis_score" != "N/A" ]]; then - # Calcul de la barre de progression local percentage=$((lynis_score * 100 / lynis_max_score)) - local bar_length=25 # Barre plus courte pour le résumé + local bar_length=25 local filled_length=$((percentage * bar_length / 100)) local empty_length=$((bar_length - filled_length)) @@ -1427,7 +2229,6 @@ print_summary() { done bar+="]" - # Déterminer la couleur du score local score_color=$RED [[ $lynis_score -ge 80 ]] && score_color=$GREEN [[ $lynis_score -ge 60 && $lynis_score -lt 80 ]] && score_color=$YELLOW @@ -1443,7 +2244,6 @@ print_summary() { echo -e "\n${MAGENTA}════════════════════ RÉSUMÉ FINAL ════════════════════${NC}\n" - # Déterminer la couleur du score pour le résumé local score_color=$RED if [[ "$lynis_score" != "N/A" ]]; then [[ $lynis_score -ge 80 ]] && score_color=$GREEN @@ -1468,17 +2268,29 @@ print_summary() { echo "" fi + local ssh_port=$(get_ssh_port_to_use) + echo -e "${RED}🔐 ACTIONS CRITIQUES IMMÉDIATES:${NC}" echo -e " 1. ${RED}TESTEZ SSH MAINTENANT:${NC}" - echo " → ssh -p $NEW_SSH_PORT $(whoami)@$(hostname -I | awk '{print $1}')" - echo -e " → ${RED}NE FERMEZ PAS CETTE SESSION AVANT VALIDATION !${NC}" - echo "" - echo " 2. Une fois SSH:$NEW_SSH_PORT validé:" - echo " → sudo $0 --cleanup-ssh" + echo " → ssh -p $ssh_port $(whoami)@$(hostname -I | awk '{print $1}')" + + if [[ "$ssh_port" == "22022" ]]; then + echo -e " → ${RED}NE FERMEZ PAS CETTE SESSION AVANT VALIDATION !${NC}" + echo "" + echo " 2. Une fois SSH:$ssh_port validé:" + echo " → sudo $0 --cleanup-ssh" + else + echo "" + echo " 2. Conteneur LXC - Port SSH: $ssh_port" + echo " → Aucun nettoyage nécessaire" + fi echo "" echo -e "${YELLOW}📊 SERVICES CONFIGURÉS:${NC}" - echo " • SSH: Ports 22 (temporaire) + $NEW_SSH_PORT (permanent)" + echo " • SSH: Port $ssh_port" + if [[ "$ssh_port" == "22022" ]]; then + echo " (Port 22 temporairement actif)" + fi echo " • UFW: $(systemctl is-active ufw 2>/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" @@ -1486,14 +2298,23 @@ print_summary() { 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 "" - # Statistiques Lynis détaillées if [[ -f "$SECURITY_REPORT" ]]; then local warnings=$(grep -c "Warning" "$SECURITY_REPORT" 2>/dev/null || echo "0") local suggestions=$(grep -c "Suggestion" "$SECURITY_REPORT" 2>/dev/null || echo "0") @@ -1511,6 +2332,9 @@ print_summary() { 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 "" } @@ -1524,7 +2348,6 @@ print_summary() { echo " 7. Rapport complet: cat $SECURITY_REPORT | less" echo "" - # Interpréter le score if [[ "$lynis_score" != "N/A" ]]; then echo -e "${CYAN}💡 INTERPRÉTATION DU SCORE:${NC}" if [[ $lynis_score -ge 80 ]]; then @@ -1543,32 +2366,62 @@ print_summary() { echo "" fi - echo -e "${RED}══════════════════════════════════════════════════════════════════${NC}" - echo -e "${RED}⚠ TESTEZ SSH:$NEW_SSH_PORT IMMÉDIATEMENT AVANT TOUTE AUTRE ACTION ⚠${NC}" - echo -e "${RED}══════════════════════════════════════════════════════════════════${NC}" + 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 - # Nettoyage rm -f /tmp/lynis_score.txt /tmp/lynis_max_score.txt 2>/dev/null || true } main() { - # Mode nettoyage SSH - [[ $# -eq 1 && "$1" == "--cleanup-ssh" ]] && { + 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 - # Initialisation 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 v6.0" "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" - # EXÉCUTION DES ÉTAPES install_security_tools + detect_open_ports configure_process_accounting configure_sysctl_security configure_log_permissions @@ -1581,7 +2434,7 @@ main() { configure_chrony harden_ssh configure_banners - configure_ufw_firewall + configure_firewall_ports configure_fail2ban remove_unneeded_packages restrict_file_permissions @@ -1599,16 +2452,19 @@ main() { prepare_ssh_cleanup run_lynis_audit - # Résumé print_summary log_message "==================================================" "END" log_message "Durcissement terminé - Durée: $((SECONDS / 60)) min" "END" + log_message "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" } -# Gestion signaux +# ============================================================================== +# EXÉCUTION PRINCIPALE +# ============================================================================== + trap 'print_error "Script interrompu"; exit 130' INT TERM -# Point d'entrée [[ "${BASH_SOURCE[0]}" == "${0}" ]] && main "$@" \ No newline at end of file