2470 lines
89 KiB
Bash
2470 lines
89 KiB
Bash
#!/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<filled; i++)); do echo -n "█"; done
|
||
for ((i=0; i<empty; i++)); do echo -n "░"; done
|
||
echo "] ${percent}%"
|
||
echo ""
|
||
|
||
if [[ -f "$STATUS_FILE" ]]; then
|
||
echo -e "${BLUE}Dernière modification:${NC} $(stat -c %y "$STATUS_FILE" 2>/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<filled_length; i++)); do
|
||
bar+="█"
|
||
done
|
||
for ((i=0; i<empty_length; i++)); do
|
||
bar+="░"
|
||
done
|
||
bar+="]"
|
||
|
||
local color=$RED
|
||
[[ $score -ge 80 ]] && color=$GREEN
|
||
[[ $score -ge 60 && $score -lt 80 ]] && color=$YELLOW
|
||
|
||
print_success "Audit Lynis terminé"
|
||
|
||
echo ""
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
echo " SCORE LYNIS FINAL"
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
echo -e "Score de durcissement: ${color}${bar} ${score}/${max_score}${NC}"
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
echo ""
|
||
|
||
print_info "Rapport complet: $SECURITY_REPORT"
|
||
|
||
echo ""
|
||
print_info "Statistiques de l'audit:"
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
|
||
local warnings=$(grep -c "Warning" "$SECURITY_REPORT" 2>/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<filled_length; i++)); do
|
||
bar+="█"
|
||
done
|
||
for ((i=0; i<empty_length; i++)); do
|
||
bar+="░"
|
||
done
|
||
bar+="]"
|
||
|
||
local score_color=$RED
|
||
[[ $lynis_score -ge 80 ]] && score_color=$GREEN
|
||
[[ $lynis_score -ge 60 && $lynis_score -lt 80 ]] && score_color=$YELLOW
|
||
|
||
echo ""
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
echo " SCORE LYNIS FINAL"
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
echo -e "Score de durcissement: ${score_color}${bar} ${lynis_score}/${lynis_max_score}${NC}"
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
echo ""
|
||
fi
|
||
|
||
echo -e "\n${MAGENTA}════════════════════ RÉSUMÉ FINAL ════════════════════${NC}\n"
|
||
|
||
local score_color=$RED
|
||
if [[ "$lynis_score" != "N/A" ]]; then
|
||
[[ $lynis_score -ge 80 ]] && score_color=$GREEN
|
||
[[ $lynis_score -ge 60 && $lynis_score -lt 80 ]] && score_color=$YELLOW
|
||
|
||
echo -e "${score_color}╔══════════════════════════════════════════════════════════════╗${NC}"
|
||
echo -e "${score_color}║ 🏆 SCORE DE SÉCURITÉ FINAL ║${NC}"
|
||
echo -e "${score_color}║ ║${NC}"
|
||
printf "${score_color}║ %3s / 100 points ║${NC}\n" "$lynis_score"
|
||
|
||
if [[ $lynis_score -ge 80 ]]; then
|
||
echo -e "${score_color}║ ★★★★★ Excellent ║${NC}"
|
||
elif [[ $lynis_score -ge 60 ]]; then
|
||
echo -e "${score_color}║ ★★★☆☆ Bon ║${NC}"
|
||
elif [[ $lynis_score -ge 40 ]]; then
|
||
echo -e "${score_color}║ ★★☆☆☆ Moyen ║${NC}"
|
||
else
|
||
echo -e "${score_color}║ ★☆☆☆☆ À améliorer ║${NC}"
|
||
fi
|
||
echo -e "${score_color}║ ║${NC}"
|
||
echo -e "${score_color}╚══════════════════════════════════════════════════════════════╝${NC}"
|
||
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 $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: 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"
|
||
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 "$@" |