Files
SecureCheck/securecheck/assets/system_hardening.sh
2026-04-06 08:37:54 +02:00

1155 lines
47 KiB
Bash

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