1857 lines
57 KiB
Bash
1857 lines
57 KiB
Bash
#!/bin/bash
|
|
|
|
# Script de durcissement système Linux
|
|
# Version: 3.0 - Idempotent et Robuste
|
|
# Auteur: Optimisé pour sécurité renforcée et réutilisabilité
|
|
|
|
set -euo pipefail
|
|
|
|
# ============================================================================
|
|
# CONFIGURATION GLOBALE
|
|
# ============================================================================
|
|
|
|
readonly SCRIPT_VERSION="3.0"
|
|
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
readonly STATE_DIR="/var/lib/hardening-script"
|
|
readonly BACKUP_BASE_DIR="/root/backup_hardening"
|
|
readonly LOG_FILE="/var/log/hardening-script.log"
|
|
readonly SSH_PORT="${SSH_PORT:-2222}"
|
|
readonly LOCK_FILE="/var/run/hardening-script.lock"
|
|
|
|
# Couleurs
|
|
readonly RED='\033[0;31m'
|
|
readonly GREEN='\033[0;32m'
|
|
readonly YELLOW='\033[1;33m'
|
|
readonly BLUE='\033[0;34m'
|
|
readonly MAGENTA='\033[0;35m'
|
|
readonly CYAN='\033[0;36m'
|
|
readonly NC='\033[0m'
|
|
|
|
# ============================================================================
|
|
# FONCTIONS UTILITAIRES
|
|
# ============================================================================
|
|
|
|
# Logging amélioré avec fichier
|
|
log() {
|
|
local msg="[$(date '+%Y-%m-%d %H:%M:%S')] $1"
|
|
echo -e "${GREEN}${msg}${NC}"
|
|
echo "$msg" >> "$LOG_FILE"
|
|
}
|
|
|
|
warn() {
|
|
local msg="[ATTENTION] $1"
|
|
echo -e "${YELLOW}${msg}${NC}"
|
|
echo "$msg" >> "$LOG_FILE"
|
|
}
|
|
|
|
error() {
|
|
local msg="[ERREUR] $1"
|
|
echo -e "${RED}${msg}${NC}" >&2
|
|
echo "$msg" >> "$LOG_FILE"
|
|
}
|
|
|
|
info() {
|
|
local msg="[INFO] $1"
|
|
echo -e "${CYAN}${msg}${NC}"
|
|
echo "$msg" >> "$LOG_FILE"
|
|
}
|
|
|
|
success() {
|
|
local msg="[OK] $1"
|
|
echo -e "${GREEN}${msg}${NC}"
|
|
echo "$msg" >> "$LOG_FILE"
|
|
}
|
|
|
|
# Fonction pour vérifier si une tâche a déjà été exécutée (idempotence)
|
|
is_task_done() {
|
|
local task_name="$1"
|
|
[[ -f "${STATE_DIR}/${task_name}.done" ]]
|
|
}
|
|
|
|
# Marquer une tâche comme terminée
|
|
mark_task_done() {
|
|
local task_name="$1"
|
|
mkdir -p "$STATE_DIR"
|
|
touch "${STATE_DIR}/${task_name}.done"
|
|
echo "$(date '+%Y-%m-%d %H:%M:%S')" > "${STATE_DIR}/${task_name}.timestamp"
|
|
}
|
|
|
|
# Réinitialiser l'état d'une tâche
|
|
reset_task() {
|
|
local task_name="$1"
|
|
rm -f "${STATE_DIR}/${task_name}.done" "${STATE_DIR}/${task_name}.timestamp"
|
|
}
|
|
|
|
# Exécuter une tâche de manière idempotente
|
|
run_task() {
|
|
local task_name="$1"
|
|
local task_function="$2"
|
|
local force="${3:-false}"
|
|
|
|
if is_task_done "$task_name" && [[ "$force" != "true" ]]; then
|
|
info "Tâche '$task_name' déjà effectuée - ignorée (utilisez --force pour réexécuter)"
|
|
return 0
|
|
fi
|
|
|
|
log "Exécution de la tâche: $task_name"
|
|
if $task_function; then
|
|
mark_task_done "$task_name"
|
|
success "Tâche '$task_name' terminée avec succès"
|
|
return 0
|
|
else
|
|
error "Échec de la tâche '$task_name'"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Gestion du verrou pour éviter les exécutions simultanées
|
|
acquire_lock() {
|
|
exec 200>"$LOCK_FILE"
|
|
if ! flock -n 200; then
|
|
error "Une autre instance du script est en cours d'exécution"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
release_lock() {
|
|
flock -u 200
|
|
rm -f "$LOCK_FILE"
|
|
}
|
|
|
|
# Nettoyage amélioré
|
|
cleanup() {
|
|
error "Script interrompu. Nettoyage en cours..."
|
|
release_lock
|
|
exit 1
|
|
}
|
|
|
|
trap cleanup INT TERM EXIT
|
|
|
|
# ============================================================================
|
|
# VÉRIFICATIONS PRÉLIMINAIRES
|
|
# ============================================================================
|
|
|
|
check_root() {
|
|
if [[ $EUID -ne 0 ]]; then
|
|
error "Ce script doit être exécuté en tant que root (sudo ./script.sh)"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
check_distribution() {
|
|
if [[ -f /etc/os-release ]]; then
|
|
source /etc/os-release
|
|
info "Distribution détectée: $PRETTY_NAME"
|
|
|
|
if [[ "$ID_LIKE" =~ (debian|ubuntu) ]] || [[ "$ID" =~ (debian|ubuntu) ]]; then
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
error "Ce script est conçu pour les distributions basées sur Debian/Ubuntu"
|
|
exit 1
|
|
}
|
|
|
|
check_disk_space() {
|
|
local required_space=1048576 # 1GB en KB
|
|
local available_space=$(df / | awk 'NR==2 {print $4}')
|
|
|
|
if [[ $available_space -lt $required_space ]]; then
|
|
error "Espace disque insuffisant. Requis: 1GB, Disponible: $((available_space/1024))MB"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
check_internet_connectivity() {
|
|
if ! ping -c 1 -W 2 8.8.8.8 &> /dev/null; then
|
|
warn "Pas de connectivité Internet détectée. Certaines opérations peuvent échouer."
|
|
read -p "Continuer quand même? (y/N) " -n 1 -r
|
|
echo
|
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
exit 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# SAUVEGARDE ET RESTAURATION
|
|
# ============================================================================
|
|
|
|
backup_configs() {
|
|
local backup_dir="${BACKUP_BASE_DIR}_$(date +%Y%m%d_%H%M%S)"
|
|
|
|
if is_task_done "backup_configs"; then
|
|
info "Sauvegarde déjà effectuée"
|
|
return 0
|
|
fi
|
|
|
|
log "Sauvegarde des fichiers de configuration..."
|
|
mkdir -p "$backup_dir"
|
|
|
|
local files=(
|
|
"/etc/ssh/sshd_config"
|
|
"/etc/login.defs"
|
|
"/etc/pam.d/common-password"
|
|
"/etc/pam.d/common-auth"
|
|
"/etc/security/limits.conf"
|
|
"/etc/sysctl.conf"
|
|
"/etc/fstab"
|
|
"/etc/default/grub"
|
|
"/etc/audit/auditd.conf"
|
|
"/etc/rsyslog.conf"
|
|
)
|
|
|
|
for file in "${files[@]}"; do
|
|
if [[ -f "$file" ]]; then
|
|
cp -p "$file" "$backup_dir/" 2>/dev/null || warn "Impossible de sauvegarder $file"
|
|
fi
|
|
done
|
|
|
|
# Sauvegarder la liste des paquets installés
|
|
dpkg --get-selections > "$backup_dir/installed_packages.list"
|
|
|
|
# Sauvegarder les règles UFW
|
|
if command -v ufw &> /dev/null; then
|
|
ufw status numbered > "$backup_dir/ufw_rules.txt" 2>/dev/null || true
|
|
fi
|
|
|
|
# Sauvegarder les services actifs
|
|
systemctl list-units --type=service --state=running > "$backup_dir/active_services.txt"
|
|
|
|
# Créer un fichier de métadonnées
|
|
cat > "$backup_dir/metadata.txt" <<EOF
|
|
Script Version: $SCRIPT_VERSION
|
|
Backup Date: $(date)
|
|
Hostname: $(hostname)
|
|
Kernel: $(uname -r)
|
|
Distribution: $(lsb_release -d | cut -f2)
|
|
EOF
|
|
|
|
log "Sauvegarde créée dans : $backup_dir"
|
|
echo "$backup_dir" > "${STATE_DIR}/last_backup_dir"
|
|
}
|
|
|
|
# ============================================================================
|
|
# MISE À JOUR ET INSTALLATION
|
|
# ============================================================================
|
|
|
|
update_system() {
|
|
log "Mise à jour du système..."
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
|
|
# Nettoyer les verrous APT si nécessaire
|
|
rm -f /var/lib/apt/lists/lock
|
|
rm -f /var/cache/apt/archives/lock
|
|
rm -f /var/lib/dpkg/lock*
|
|
|
|
# Configurer dpkg pour éviter les prompts
|
|
dpkg --configure -a
|
|
|
|
apt update -qq || {
|
|
error "Échec de la mise à jour des dépôts"
|
|
return 1
|
|
}
|
|
|
|
apt upgrade -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold"
|
|
apt autoremove -y -qq
|
|
apt autoclean -qq
|
|
|
|
# Mettre à jour la base de données locate si présente
|
|
command -v updatedb &> /dev/null && updatedb || true
|
|
}
|
|
|
|
install_security_tools() {
|
|
log "Installation des outils de durcissement..."
|
|
|
|
local packages=(
|
|
# Outils de sécurité
|
|
lynis fail2ban ufw aide aide-common
|
|
# Antivirus
|
|
clamav clamav-daemon clamav-freshclam
|
|
# Détection rootkit
|
|
chkrootkit rkhunter
|
|
# Audit et logging
|
|
auditd audispd-plugins
|
|
rsyslog logwatch logrotate
|
|
# Authentification
|
|
libpam-tmpdir libpam-pwquality libpam-cracklib
|
|
# Monitoring
|
|
sysstat iotop htop
|
|
# Outils réseau
|
|
net-tools tcpdump nmap
|
|
# Synchronisation temps
|
|
chrony
|
|
# Détection d'intrusion
|
|
psad
|
|
# Autres
|
|
debsums apt-listchanges unattended-upgrades
|
|
acct apparmor apparmor-utils apparmor-profiles
|
|
tiger needrestart debsecan
|
|
)
|
|
|
|
local failed_packages=()
|
|
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
|
|
for package in "${packages[@]}"; do
|
|
if ! dpkg -l | grep -q "^ii $package "; then
|
|
if apt install -y -qq -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" "$package" 2>/dev/null; then
|
|
info "✓ $package installé"
|
|
else
|
|
warn "✗ Échec de l'installation de $package"
|
|
failed_packages+=("$package")
|
|
fi
|
|
else
|
|
info "✓ $package déjà installé"
|
|
fi
|
|
done
|
|
|
|
if [[ ${#failed_packages[@]} -gt 0 ]]; then
|
|
warn "Paquets non installés: ${failed_packages[*]}"
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# CONFIGURATION FAIL2BAN
|
|
# ============================================================================
|
|
|
|
configure_fail2ban() {
|
|
log "Configuration de Fail2ban..."
|
|
|
|
# Configuration locale qui ne sera pas écrasée
|
|
cat > /etc/fail2ban/jail.local <<EOF
|
|
[DEFAULT]
|
|
# Ignorer les adresses locales et les réseaux de confiance
|
|
ignoreip = 127.0.0.1/8 ::1
|
|
# Ajouter vos IPs de confiance ici: 192.168.1.0/24
|
|
|
|
# Durée de bannissement (en secondes)
|
|
bantime = 3600
|
|
findtime = 600
|
|
maxretry = 3
|
|
|
|
# Action par défaut
|
|
banaction = ufw
|
|
banaction_allports = ufw
|
|
|
|
# Email (optionnel)
|
|
# destemail = admin@example.com
|
|
# sendername = Fail2Ban
|
|
# mta = sendmail
|
|
|
|
[sshd]
|
|
enabled = true
|
|
port = ${SSH_PORT}
|
|
logpath = /var/log/auth.log
|
|
maxretry = 3
|
|
findtime = 600
|
|
bantime = 7200
|
|
|
|
[sshd-ddos]
|
|
enabled = true
|
|
port = ${SSH_PORT}
|
|
logpath = /var/log/auth.log
|
|
maxretry = 6
|
|
findtime = 600
|
|
bantime = 600
|
|
|
|
[recidive]
|
|
enabled = true
|
|
logpath = /var/log/fail2ban.log
|
|
bantime = 86400
|
|
findtime = 86400
|
|
maxretry = 3
|
|
EOF
|
|
|
|
# Ajouter des filtres pour Apache si installé
|
|
if systemctl is-active --quiet apache2 2>/dev/null; then
|
|
cat >> /etc/fail2ban/jail.local <<EOF
|
|
|
|
[apache-auth]
|
|
enabled = true
|
|
logpath = /var/log/apache2/error.log
|
|
|
|
[apache-badbots]
|
|
enabled = true
|
|
logpath = /var/log/apache2/access.log
|
|
|
|
[apache-noscript]
|
|
enabled = true
|
|
logpath = /var/log/apache2/access.log
|
|
|
|
[apache-overflows]
|
|
enabled = true
|
|
logpath = /var/log/apache2/error.log
|
|
maxretry = 2
|
|
EOF
|
|
fi
|
|
|
|
# Ajouter des filtres pour Nginx si installé
|
|
if systemctl is-active --quiet nginx 2>/dev/null; then
|
|
cat >> /etc/fail2ban/jail.local <<EOF
|
|
|
|
[nginx-http-auth]
|
|
enabled = true
|
|
logpath = /var/log/nginx/error.log
|
|
|
|
[nginx-noscript]
|
|
enabled = true
|
|
logpath = /var/log/nginx/access.log
|
|
|
|
[nginx-badbots]
|
|
enabled = true
|
|
logpath = /var/log/nginx/access.log
|
|
maxretry = 2
|
|
|
|
[nginx-noproxy]
|
|
enabled = true
|
|
logpath = /var/log/nginx/access.log
|
|
maxretry = 2
|
|
EOF
|
|
fi
|
|
|
|
# Ajouter des filtres pour Postfix si installé
|
|
if systemctl is-active --quiet postfix 2>/dev/null; then
|
|
cat >> /etc/fail2ban/jail.local <<EOF
|
|
|
|
[postfix-sasl]
|
|
enabled = true
|
|
logpath = /var/log/mail.log
|
|
|
|
[postfix-rbl]
|
|
enabled = true
|
|
logpath = /var/log/mail.log
|
|
maxretry = 1
|
|
EOF
|
|
fi
|
|
|
|
# Vérifier la configuration
|
|
if fail2ban-client -t &> /dev/null; then
|
|
systemctl enable fail2ban
|
|
systemctl restart fail2ban
|
|
success "Fail2ban configuré et démarré"
|
|
else
|
|
error "Erreur dans la configuration Fail2ban"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# CONFIGURATION UFW (PARE-FEU)
|
|
# ============================================================================
|
|
|
|
configure_ufw() {
|
|
log "Configuration du pare-feu UFW..."
|
|
|
|
# Sauvegarder les règles actuelles si UFW est déjà actif
|
|
if ufw status | grep -q "Status: active"; then
|
|
ufw status numbered > "${STATE_DIR}/ufw_backup_$(date +%Y%m%d_%H%M%S).txt"
|
|
fi
|
|
|
|
# Désactiver UFW temporairement
|
|
ufw --force disable
|
|
|
|
# Réinitialiser SEULEMENT si pas déjà configuré
|
|
if [[ ! -f "${STATE_DIR}/ufw_configured" ]]; then
|
|
ufw --force reset
|
|
fi
|
|
|
|
# Politique par défaut
|
|
ufw default deny incoming
|
|
ufw default allow outgoing
|
|
ufw default deny forward
|
|
|
|
# Autoriser le loopback
|
|
ufw allow in on lo
|
|
ufw allow out on lo
|
|
|
|
# SSH sur port personnalisé
|
|
ufw allow ${SSH_PORT}/tcp comment 'SSH Custom Port'
|
|
|
|
# Services web (commentés par défaut)
|
|
# ufw allow 80/tcp comment 'HTTP'
|
|
# ufw allow 443/tcp comment 'HTTPS'
|
|
|
|
# DNS (sortant seulement, entrant généralement pas nécessaire)
|
|
ufw allow out 53 comment 'DNS'
|
|
|
|
# NTP
|
|
ufw allow out 123/udp comment 'NTP'
|
|
|
|
# Protection contre les scans de ports
|
|
ufw limit ${SSH_PORT}/tcp
|
|
|
|
# Logging
|
|
ufw logging medium
|
|
|
|
# Activer UFW
|
|
ufw --force enable
|
|
|
|
touch "${STATE_DIR}/ufw_configured"
|
|
|
|
# Afficher les règles
|
|
ufw status verbose
|
|
}
|
|
|
|
# ============================================================================
|
|
# CONFIGURATION SSH SÉCURISÉE
|
|
# ============================================================================
|
|
|
|
configure_ssh() {
|
|
log "Configuration sécurisée de SSH..."
|
|
|
|
# Vérifier si des clés SSH existent pour les utilisateurs
|
|
local has_ssh_keys=false
|
|
for user_home in /home/*; do
|
|
if [[ -f "$user_home/.ssh/authorized_keys" ]] && [[ -s "$user_home/.ssh/authorized_keys" ]]; then
|
|
has_ssh_keys=true
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [[ "$has_ssh_keys" == "false" ]]; then
|
|
warn "ATTENTION: Aucune clé SSH trouvée pour les utilisateurs !"
|
|
warn "L'authentification par mot de passe SSH sera DÉSACTIVÉE."
|
|
read -p "Continuer? Vous pourriez perdre l'accès SSH! (y/N) " -n 1 -r
|
|
echo
|
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
error "Configuration SSH annulée. Configurez d'abord vos clés SSH."
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
# Sauvegarder la config actuelle si pas déjà fait
|
|
[[ ! -f /etc/ssh/sshd_config.bak ]] && cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
|
|
|
|
# Configuration SSH durcie
|
|
cat > /etc/ssh/sshd_config <<EOF
|
|
# Configuration SSH durcie - v${SCRIPT_VERSION}
|
|
# Généré le $(date)
|
|
|
|
# Réseau
|
|
Port ${SSH_PORT}
|
|
AddressFamily inet
|
|
ListenAddress 0.0.0.0
|
|
|
|
# Protocol
|
|
Protocol 2
|
|
|
|
# HostKeys (selon la disponibilité)
|
|
HostKey /etc/ssh/ssh_host_rsa_key
|
|
HostKey /etc/ssh/ssh_host_ecdsa_key
|
|
HostKey /etc/ssh/ssh_host_ed25519_key
|
|
|
|
# Logging
|
|
SyslogFacility AUTH
|
|
LogLevel VERBOSE
|
|
|
|
# Authentification
|
|
LoginGraceTime 30
|
|
PermitRootLogin no
|
|
StrictModes yes
|
|
MaxAuthTries 3
|
|
MaxSessions 3
|
|
MaxStartups 3:50:10
|
|
|
|
PubkeyAuthentication yes
|
|
AuthorizedKeysFile .ssh/authorized_keys
|
|
|
|
# Désactiver les méthodes d'authentification non sécurisées
|
|
HostbasedAuthentication no
|
|
IgnoreRhosts yes
|
|
PermitEmptyPasswords no
|
|
ChallengeResponseAuthentication no
|
|
|
|
# Authentification par mot de passe (DÉSACTIVÉE pour sécurité)
|
|
PasswordAuthentication no
|
|
|
|
# Kerberos
|
|
KerberosAuthentication no
|
|
|
|
# GSSAPI
|
|
GSSAPIAuthentication no
|
|
|
|
# PAM
|
|
UsePAM yes
|
|
|
|
# Forwarding
|
|
AllowAgentForwarding no
|
|
AllowTcpForwarding no
|
|
X11Forwarding no
|
|
PrintMotd no
|
|
PrintLastLog yes
|
|
TCPKeepAlive no
|
|
PermitTunnel no
|
|
GatewayPorts no
|
|
|
|
# Compression
|
|
Compression no
|
|
|
|
# Client Alive
|
|
ClientAliveInterval 300
|
|
ClientAliveCountMax 2
|
|
|
|
# Chiffrement fort
|
|
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-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256
|
|
|
|
# Bannière
|
|
Banner /etc/issue.net
|
|
|
|
# Environnement
|
|
PermitUserEnvironment no
|
|
AcceptEnv LANG LC_*
|
|
|
|
# Subsystem
|
|
Subsystem sftp /usr/lib/openssh/sftp-server -f AUTHPRIV -l INFO
|
|
|
|
# Restrictions optionnelles par utilisateur (décommenter et adapter)
|
|
# Match User deployer
|
|
# PasswordAuthentication yes
|
|
# AllowTcpForwarding yes
|
|
|
|
# Match Group sftp-only
|
|
# ChrootDirectory /var/sftp/%u
|
|
# ForceCommand internal-sftp
|
|
# AllowTcpForwarding no
|
|
EOF
|
|
|
|
# Créer une bannière de sécurité
|
|
cat > /etc/issue.net <<'EOF'
|
|
################################################################################
|
|
# #
|
|
# SYSTÈME SÉCURISÉ - ACCÈS RESTREINT #
|
|
# #
|
|
# Ce système est réservé exclusivement aux utilisateurs autorisés. #
|
|
# Toutes les connexions et activités sont surveillées et enregistrées. #
|
|
# L'accès non autorisé est strictement interdit et sera poursuivi. #
|
|
# #
|
|
# Si vous n'êtes pas autorisé, déconnectez-vous immédiatement. #
|
|
# #
|
|
################################################################################
|
|
EOF
|
|
|
|
# Appliquer aussi la bannière au login local
|
|
cp /etc/issue.net /etc/issue
|
|
|
|
# Vérifier et appliquer la configuration
|
|
if sshd -t 2>/dev/null; then
|
|
systemctl restart sshd
|
|
success "Configuration SSH appliquée - Port: ${SSH_PORT}"
|
|
info "Testez votre connexion SSH sur le port ${SSH_PORT} AVANT de fermer cette session!"
|
|
else
|
|
error "Erreur dans la configuration SSH!"
|
|
sshd -t
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# CONFIGURATION SYSCTL (KERNEL)
|
|
# ============================================================================
|
|
|
|
configure_sysctl() {
|
|
log "Configuration des paramètres kernel (sysctl)..."
|
|
|
|
# Créer un fichier de configuration dédié
|
|
cat > /etc/sysctl.d/99-hardening.conf <<'EOF'
|
|
# Configuration de sécurité kernel - Hardening Script
|
|
# Généré automatiquement
|
|
|
|
# ============================================================================
|
|
# SÉCURITÉ RÉSEAU IPv4
|
|
# ============================================================================
|
|
|
|
# Protection contre IP spoofing (vérification de la source)
|
|
net.ipv4.conf.default.rp_filter = 1
|
|
net.ipv4.conf.all.rp_filter = 1
|
|
|
|
# Ignorer les pings ICMP (protection contre ping flood)
|
|
net.ipv4.icmp_echo_ignore_all = 1
|
|
net.ipv4.icmp_ignore_bogus_error_responses = 1
|
|
|
|
# Ignorer les redirections ICMP
|
|
net.ipv4.conf.all.accept_redirects = 0
|
|
net.ipv4.conf.default.accept_redirects = 0
|
|
net.ipv4.conf.all.secure_redirects = 0
|
|
net.ipv4.conf.default.secure_redirects = 0
|
|
|
|
# Ne pas envoyer de redirections ICMP
|
|
net.ipv4.conf.all.send_redirects = 0
|
|
net.ipv4.conf.default.send_redirects = 0
|
|
|
|
# Ignorer les paquets source routed
|
|
net.ipv4.conf.all.accept_source_route = 0
|
|
net.ipv4.conf.default.accept_source_route = 0
|
|
|
|
# Protection SYN flood
|
|
net.ipv4.tcp_syncookies = 1
|
|
net.ipv4.tcp_max_syn_backlog = 2048
|
|
net.ipv4.tcp_synack_retries = 2
|
|
net.ipv4.tcp_syn_retries = 5
|
|
|
|
# Désactiver le routage IP si non routeur
|
|
net.ipv4.ip_forward = 0
|
|
net.ipv4.conf.all.forwarding = 0
|
|
net.ipv4.conf.default.forwarding = 0
|
|
|
|
# Log des paquets suspects
|
|
net.ipv4.conf.all.log_martians = 1
|
|
net.ipv4.conf.default.log_martians = 1
|
|
|
|
# Protection contre les attaques de temps
|
|
net.ipv4.tcp_timestamps = 0
|
|
|
|
# Amélioration des performances TCP
|
|
net.ipv4.tcp_window_scaling = 1
|
|
net.ipv4.tcp_sack = 1
|
|
|
|
# ============================================================================
|
|
# SÉCURITÉ RÉSEAU IPv6
|
|
# ============================================================================
|
|
|
|
# Désactiver IPv6 si non utilisé (décommenter si nécessaire)
|
|
# net.ipv6.conf.all.disable_ipv6 = 1
|
|
# net.ipv6.conf.default.disable_ipv6 = 1
|
|
# net.ipv6.conf.lo.disable_ipv6 = 1
|
|
|
|
# Si IPv6 est utilisé, appliquer les mêmes protections
|
|
net.ipv6.conf.all.accept_redirects = 0
|
|
net.ipv6.conf.default.accept_redirects = 0
|
|
net.ipv6.conf.all.accept_source_route = 0
|
|
net.ipv6.conf.default.accept_source_route = 0
|
|
net.ipv6.conf.all.accept_ra = 0
|
|
net.ipv6.conf.default.accept_ra = 0
|
|
net.ipv6.conf.all.forwarding = 0
|
|
|
|
# ============================================================================
|
|
# SÉCURITÉ KERNEL
|
|
# ============================================================================
|
|
|
|
# Restreindre l'accès aux logs kernel
|
|
kernel.dmesg_restrict = 1
|
|
|
|
# Masquer les adresses kernel
|
|
kernel.kptr_restrict = 2
|
|
|
|
# Protection ptrace (empêche l'attachement aux processus)
|
|
kernel.yama.ptrace_scope = 1
|
|
|
|
# Protection contre les attaques par lien symbolique
|
|
fs.protected_symlinks = 1
|
|
fs.protected_hardlinks = 1
|
|
|
|
# Protection FIFO et fichiers réguliers
|
|
fs.protected_fifos = 2
|
|
fs.protected_regular = 2
|
|
|
|
# Désactiver les SysRq sauf reboot/shutdown d'urgence
|
|
kernel.sysrq = 16
|
|
|
|
# Limiter la visibilité des processus
|
|
# kernel.unprivileged_userns_clone = 0
|
|
|
|
# ============================================================================
|
|
# AUTRES PARAMÈTRES DE SÉCURITÉ
|
|
# ============================================================================
|
|
|
|
# Randomisation de l'espace d'adressage
|
|
kernel.randomize_va_space = 2
|
|
|
|
# Core dumps
|
|
kernel.core_uses_pid = 1
|
|
fs.suid_dumpable = 0
|
|
|
|
# Limite des fichiers ouverts
|
|
fs.file-max = 2097152
|
|
|
|
# IPC
|
|
kernel.msgmnb = 65536
|
|
kernel.msgmax = 65536
|
|
|
|
# Shared memory
|
|
kernel.shmmax = 68719476736
|
|
kernel.shmall = 4294967296
|
|
EOF
|
|
|
|
# Appliquer immédiatement les changements
|
|
sysctl -p /etc/sysctl.d/99-hardening.conf
|
|
|
|
success "Paramètres kernel appliqués"
|
|
}
|
|
|
|
# ============================================================================
|
|
# POLITIQUE DE MOTS DE PASSE
|
|
# ============================================================================
|
|
|
|
configure_password_policy() {
|
|
log "Configuration de la politique de mots de passe..."
|
|
|
|
# Sauvegarde
|
|
[[ ! -f /etc/login.defs.bak ]] && cp /etc/login.defs /etc/login.defs.bak
|
|
|
|
# Configuration login.defs (idempotent avec sed)
|
|
sed -i.tmp 's/^PASS_MAX_DAYS.*/PASS_MAX_DAYS 90/' /etc/login.defs
|
|
sed -i.tmp 's/^PASS_MIN_DAYS.*/PASS_MIN_DAYS 1/' /etc/login.defs
|
|
sed -i.tmp 's/^PASS_WARN_AGE.*/PASS_WARN_AGE 14/' /etc/login.defs
|
|
sed -i.tmp 's/^UMASK.*/UMASK 027/' /etc/login.defs
|
|
sed -i.tmp 's/^ENCRYPT_METHOD.*/ENCRYPT_METHOD SHA512/' /etc/login.defs
|
|
|
|
# Ajouter les paramètres manquants (idempotent)
|
|
grep -q "^SHA_CRYPT_MIN_ROUNDS" /etc/login.defs || echo "SHA_CRYPT_MIN_ROUNDS 5000" >> /etc/login.defs
|
|
grep -q "^SHA_CRYPT_MAX_ROUNDS" /etc/login.defs || echo "SHA_CRYPT_MAX_ROUNDS 5000" >> /etc/login.defs
|
|
grep -q "^FAILLOG_ENAB" /etc/login.defs || echo "FAILLOG_ENAB yes" >> /etc/login.defs
|
|
grep -q "^LOG_UNKFAIL_ENAB" /etc/login.defs || echo "LOG_UNKFAIL_ENAB yes" >> /etc/login.defs
|
|
|
|
# Configuration PAM pour la qualité des mots de passe (idempotent)
|
|
if [[ -f /etc/pam.d/common-password ]]; then
|
|
[[ ! -f /etc/pam.d/common-password.bak ]] && cp /etc/pam.d/common-password /etc/pam.d/common-password.bak
|
|
|
|
if ! grep -q "pam_pwquality.so" /etc/pam.d/common-password; then
|
|
sed -i '/pam_unix.so/i password requisite pam_pwquality.so retry=3 minlen=14 difok=4 ucredit=-1 lcredit=-1 dcredit=-1 ocredit=-1 maxrepeat=2 reject_username enforce_for_root' /etc/pam.d/common-password
|
|
fi
|
|
|
|
# Ajouter pam_pwhistory pour empêcher la réutilisation des mots de passe
|
|
if ! grep -q "pam_pwhistory.so" /etc/pam.d/common-password; then
|
|
sed -i '/pam_unix.so/a password required pam_pwhistory.so remember=5 use_authtok' /etc/pam.d/common-password
|
|
fi
|
|
fi
|
|
|
|
# Configuration de verrouillage de compte après échecs
|
|
if [[ -f /etc/pam.d/common-auth ]]; then
|
|
[[ ! -f /etc/pam.d/common-auth.bak ]] && cp /etc/pam.d/common-auth /etc/pam.d/common-auth.bak
|
|
|
|
if ! grep -q "pam_faillock.so" /etc/pam.d/common-auth; then
|
|
sed -i '1i auth required pam_faillock.so preauth silent audit deny=5 unlock_time=900' /etc/pam.d/common-auth
|
|
echo "auth [default=die] pam_faillock.so authfail audit deny=5 unlock_time=900" >> /etc/pam.d/common-auth
|
|
echo "auth sufficient pam_faillock.so authsucc" >> /etc/pam.d/common-auth
|
|
fi
|
|
fi
|
|
|
|
success "Politique de mots de passe configurée"
|
|
}
|
|
|
|
# ============================================================================
|
|
# LIMITES SYSTÈME
|
|
# ============================================================================
|
|
|
|
configure_limits() {
|
|
log "Configuration des limites système..."
|
|
|
|
[[ ! -f /etc/security/limits.conf.bak ]] && cp /etc/security/limits.conf /etc/security/limits.conf.bak
|
|
|
|
# Ajouter les limites si elles n'existent pas déjà
|
|
if ! grep -q "# Hardening Script Limits" /etc/security/limits.conf; then
|
|
cat >> /etc/security/limits.conf <<'EOF'
|
|
|
|
# Hardening Script Limits
|
|
# Core dumps désactivés pour tous
|
|
* hard core 0
|
|
* soft core 0
|
|
|
|
# Limites de processus
|
|
* soft nproc 65536
|
|
* hard nproc 65536
|
|
|
|
# Limites de fichiers ouverts
|
|
* soft nofile 65536
|
|
* hard nofile 65536
|
|
|
|
# Stack size
|
|
* hard stack 2048
|
|
|
|
# Root illimité pour les processus
|
|
root soft nproc unlimited
|
|
root hard nproc unlimited
|
|
EOF
|
|
fi
|
|
|
|
success "Limites système configurées"
|
|
}
|
|
|
|
# ============================================================================
|
|
# DÉSACTIVATION DES MODULES ET SERVICES
|
|
# ============================================================================
|
|
|
|
disable_unnecessary_services() {
|
|
log "Désactivation des services et modules non nécessaires..."
|
|
|
|
# Désactiver les modules noyau dangereux
|
|
cat > /etc/modprobe.d/hardening-disable-modules.conf <<'EOF'
|
|
# Désactivation des protocoles réseau non nécessaires
|
|
install dccp /bin/true
|
|
install sctp /bin/true
|
|
install tipc /bin/true
|
|
install rds /bin/true
|
|
|
|
# Désactivation des systèmes de fichiers rares
|
|
install cramfs /bin/true
|
|
install freevxfs /bin/true
|
|
install jffs2 /bin/true
|
|
install hfs /bin/true
|
|
install hfsplus /bin/true
|
|
install squashfs /bin/true
|
|
install udf /bin/true
|
|
install vfat /bin/true
|
|
|
|
# Désactivation des modules sans fil et bluetooth
|
|
blacklist firewire-core
|
|
blacklist thunderbolt
|
|
blacklist bluetooth
|
|
blacklist btusb
|
|
blacklist bnep
|
|
|
|
# Désactivation USB (décommenter si serveur sans USB)
|
|
# blacklist usb-storage
|
|
# blacklist uas
|
|
|
|
# Désactivation de modules anciens
|
|
blacklist floppy
|
|
EOF
|
|
|
|
# Liste des services à désactiver (si présents)
|
|
local services_to_disable=(
|
|
bluetooth.service
|
|
cups.service
|
|
cups-browsed.service
|
|
avahi-daemon.service
|
|
avahi-daemon.socket
|
|
rpcbind.service
|
|
rpcbind.socket
|
|
nfs-common.service
|
|
nfs-kernel-server.service
|
|
iscsid.service
|
|
open-iscsi.service
|
|
snapd.service
|
|
snapd.socket
|
|
)
|
|
|
|
for service in "${services_to_disable[@]}"; do
|
|
if systemctl list-unit-files | grep -q "^${service}"; then
|
|
if systemctl is-enabled "$service" &>/dev/null; then
|
|
systemctl disable "$service" 2>/dev/null || true
|
|
systemctl stop "$service" 2>/dev/null || true
|
|
info "✓ Service désactivé: $service"
|
|
fi
|
|
fi
|
|
done
|
|
|
|
success "Services inutiles désactivés"
|
|
}
|
|
|
|
# ============================================================================
|
|
# CONFIGURATION ANTIVIRUS (CLAMAV)
|
|
# ============================================================================
|
|
|
|
configure_clamav() {
|
|
log "Configuration de ClamAV..."
|
|
|
|
if ! command -v freshclam &> /dev/null; then
|
|
warn "ClamAV non installé, passage"
|
|
return 0
|
|
fi
|
|
|
|
# Arrêter le service temporairement
|
|
systemctl stop clamav-freshclam 2>/dev/null || true
|
|
|
|
# Configuration freshclam pour mises à jour automatiques
|
|
if [[ -f /etc/clamav/freshclam.conf ]]; then
|
|
sed -i 's/^#DatabaseMirror/DatabaseMirror/' /etc/clamav/freshclam.conf
|
|
sed -i 's/^Example/#Example/' /etc/clamav/freshclam.conf
|
|
fi
|
|
|
|
# Mettre à jour la base de données (avec timeout)
|
|
timeout 300 freshclam || warn "Mise à jour ClamAV timeout (normal lors de la première exécution)"
|
|
|
|
# Activer et démarrer les services
|
|
systemctl enable clamav-freshclam 2>/dev/null || true
|
|
systemctl start clamav-freshclam 2>/dev/null || true
|
|
|
|
if systemctl list-unit-files | grep -q "clamav-daemon"; then
|
|
systemctl enable clamav-daemon 2>/dev/null || true
|
|
systemctl start clamav-daemon 2>/dev/null || true
|
|
fi
|
|
|
|
# Créer un script de scan quotidien
|
|
cat > /etc/cron.daily/clamav-scan <<'EOF'
|
|
#!/bin/bash
|
|
SCAN_DIR="/home /var/www"
|
|
LOG_FILE="/var/log/clamav/daily-scan.log"
|
|
mkdir -p /var/log/clamav
|
|
|
|
# Scanner les répertoires importants
|
|
clamscan -r -i --exclude-dir="^/sys" --exclude-dir="^/proc" --exclude-dir="^/dev" \
|
|
$SCAN_DIR > "$LOG_FILE" 2>&1
|
|
|
|
# Envoyer un email si des virus sont détectés
|
|
if grep -q "Infected files: [1-9]" "$LOG_FILE"; then
|
|
mail -s "ClamAV: Virus détectés sur $(hostname)" root < "$LOG_FILE"
|
|
fi
|
|
EOF
|
|
chmod +x /etc/cron.daily/clamav-scan
|
|
|
|
success "ClamAV configuré"
|
|
}
|
|
|
|
# ============================================================================
|
|
# CONFIGURATION AIDE (DÉTECTION D'INTRUSION)
|
|
# ============================================================================
|
|
|
|
configure_aide() {
|
|
log "Configuration d'AIDE (détection d'intrusion)..."
|
|
|
|
if ! command -v aide &> /dev/null; then
|
|
warn "AIDE non installé, passage"
|
|
return 0
|
|
fi
|
|
|
|
# Configuration AIDE personnalisée
|
|
cat > /etc/aide/aide.conf.d/99_hardening <<'EOF'
|
|
# Configuration AIDE - Hardening Script
|
|
|
|
# Répertoires système critiques à surveiller
|
|
/bin R+b+sha256
|
|
/sbin R+b+sha256
|
|
/usr/bin R+b+sha256
|
|
/usr/sbin R+b+sha256
|
|
/lib R+b+sha256
|
|
/lib64 R+b+sha256
|
|
/usr/lib R+b+sha256
|
|
/usr/lib64 R+b+sha256
|
|
|
|
# Configuration système
|
|
/etc R+b+sha256
|
|
!/etc/mtab
|
|
!/etc/adjtime
|
|
|
|
# Fichiers de démarrage
|
|
/boot R+b+sha256
|
|
|
|
# Exclure les répertoires dynamiques
|
|
!/var/log
|
|
!/var/cache
|
|
!/var/tmp
|
|
!/tmp
|
|
!/proc
|
|
!/sys
|
|
!/dev
|
|
!/run
|
|
EOF
|
|
|
|
# Initialiser la base de données AIDE (long processus)
|
|
if [[ ! -f /var/lib/aide/aide.db ]]; then
|
|
log "Initialisation de la base AIDE (peut prendre plusieurs minutes)..."
|
|
aideinit || warn "Échec de l'initialisation AIDE"
|
|
|
|
if [[ -f /var/lib/aide/aide.db.new ]]; then
|
|
mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db
|
|
fi
|
|
fi
|
|
|
|
# Script de vérification quotidienne
|
|
cat > /etc/cron.daily/aide-check <<'EOF'
|
|
#!/bin/bash
|
|
LOG_FILE="/var/log/aide/aide-check-$(date +%Y%m%d).log"
|
|
mkdir -p /var/log/aide
|
|
|
|
# Exécuter la vérification
|
|
/usr/bin/aide --check > "$LOG_FILE" 2>&1
|
|
EXIT_CODE=$?
|
|
|
|
# Si des changements sont détectés (code 7)
|
|
if [[ $EXIT_CODE -eq 7 ]]; then
|
|
# Envoyer un email d'alerte
|
|
mail -s "AIDE: Modifications détectées sur $(hostname)" root < "$LOG_FILE"
|
|
|
|
# Logger dans syslog
|
|
logger -t aide-check "ATTENTION: Modifications système détectées"
|
|
fi
|
|
|
|
# Rotation des logs (garder 30 jours)
|
|
find /var/log/aide -name "aide-check-*.log" -mtime +30 -delete
|
|
EOF
|
|
chmod +x /etc/cron.daily/aide-check
|
|
|
|
success "AIDE configuré"
|
|
}
|
|
|
|
# ============================================================================
|
|
# MISES À JOUR AUTOMATIQUES
|
|
# ============================================================================
|
|
|
|
configure_auto_updates() {
|
|
log "Configuration des mises à jour automatiques de sécurité..."
|
|
|
|
if ! command -v unattended-upgrade &> /dev/null; then
|
|
warn "unattended-upgrades non installé, passage"
|
|
return 0
|
|
fi
|
|
|
|
# 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}ESMApps:${distro_codename}-apps-security";
|
|
"${distro_id}ESM:${distro_codename}-infra-security";
|
|
};
|
|
|
|
Unattended-Upgrade::Package-Blacklist {
|
|
// Ajouter ici les paquets à ne jamais mettre à jour automatiquement
|
|
// "mysql-server";
|
|
// "postgresql";
|
|
};
|
|
|
|
Unattended-Upgrade::DevRelease "false";
|
|
Unattended-Upgrade::AutoFixInterruptedDpkg "true";
|
|
Unattended-Upgrade::MinimalSteps "true";
|
|
Unattended-Upgrade::InstallOnShutdown "false";
|
|
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
|
|
Unattended-Upgrade::Remove-New-Unused-Dependencies "true";
|
|
Unattended-Upgrade::Remove-Unused-Dependencies "true";
|
|
Unattended-Upgrade::Automatic-Reboot "false";
|
|
Unattended-Upgrade::Automatic-Reboot-WithUsers "false";
|
|
Unattended-Upgrade::Automatic-Reboot-Time "03:00";
|
|
|
|
Unattended-Upgrade::Mail "root";
|
|
Unattended-Upgrade::MailReport "on-change";
|
|
|
|
Unattended-Upgrade::SyslogEnable "true";
|
|
Unattended-Upgrade::SyslogFacility "daemon";
|
|
EOF
|
|
|
|
cat > /etc/apt/apt.conf.d/20auto-upgrades <<'EOF'
|
|
APT::Periodic::Update-Package-Lists "1";
|
|
APT::Periodic::Download-Upgradeable-Packages "1";
|
|
APT::Periodic::Unattended-Upgrade "1";
|
|
APT::Periodic::AutocleanInterval "7";
|
|
APT::Periodic::Verbose "1";
|
|
EOF
|
|
|
|
# Tester la configuration
|
|
unattended-upgrade --dry-run --debug
|
|
|
|
systemctl enable unattended-upgrades
|
|
systemctl restart unattended-upgrades
|
|
|
|
success "Mises à jour automatiques configurées"
|
|
}
|
|
|
|
# ============================================================================
|
|
# CONFIGURATION AUDITD
|
|
# ============================================================================
|
|
|
|
configure_auditd() {
|
|
log "Configuration d'auditd (audit système)..."
|
|
|
|
if ! command -v auditctl &> /dev/null; then
|
|
warn "auditd non installé, passage"
|
|
return 0
|
|
fi
|
|
|
|
# Règles d'audit personnalisées
|
|
cat > /etc/audit/rules.d/hardening.rules <<'EOF'
|
|
# Règles d'audit - Hardening Script
|
|
# Supprimer toutes les règles précédentes
|
|
-D
|
|
|
|
# Buffer
|
|
-b 8192
|
|
|
|
# Échec d'audit
|
|
-f 1
|
|
|
|
# Surveiller les modifications de configuration
|
|
-w /etc/passwd -p wa -k identity
|
|
-w /etc/group -p wa -k identity
|
|
-w /etc/shadow -p wa -k identity
|
|
-w /etc/gshadow -p wa -k identity
|
|
-w /etc/security/opasswd -p wa -k identity
|
|
|
|
# Surveiller les modifications sudo
|
|
-w /etc/sudoers -p wa -k sudoers
|
|
-w /etc/sudoers.d/ -p wa -k sudoers
|
|
|
|
# Surveiller les commandes privilégiées
|
|
-a always,exit -F path=/usr/bin/sudo -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
|
|
-a always,exit -F path=/usr/bin/su -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
|
|
|
|
# Surveiller les modifications du système de fichiers
|
|
-a always,exit -F arch=b64 -S mount -F auid>=1000 -F auid!=4294967295 -k mounts
|
|
-a always,exit -F arch=b32 -S mount -F auid>=1000 -F auid!=4294967295 -k mounts
|
|
|
|
# Surveiller les suppressions
|
|
-a always,exit -F arch=b64 -S unlink -S unlinkat -S rename -S renameat -F auid>=1000 -F auid!=4294967295 -k delete
|
|
-a always,exit -F arch=b32 -S unlink -S unlinkat -S rename -S renameat -F auid>=1000 -F auid!=4294967295 -k delete
|
|
|
|
# Surveiller les changements de permissions
|
|
-a always,exit -F arch=b64 -S chmod -S fchmod -S fchmodat -F auid>=1000 -F auid!=4294967295 -k perm_mod
|
|
-a always,exit -F arch=b32 -S chmod -S fchmod -S fchmodat -F auid>=1000 -F auid!=4294967295 -k perm_mod
|
|
|
|
# Surveiller les accès réseau
|
|
-a always,exit -F arch=b64 -S socket -S connect -k network_connect
|
|
-a always,exit -F arch=b32 -S socket -S connect -k network_connect
|
|
|
|
# Surveiller les modifications de configuration réseau
|
|
-w /etc/network/ -p wa -k network_modifications
|
|
-w /etc/sysconfig/network-scripts/ -p wa -k network_modifications
|
|
|
|
# Surveiller les modifications SSH
|
|
-w /etc/ssh/sshd_config -p wa -k sshd_config
|
|
|
|
# Surveiller les logs
|
|
-w /var/log/wtmp -p wa -k logins
|
|
-w /var/log/btmp -p wa -k logins
|
|
-w /var/log/lastlog -p wa -k logins
|
|
|
|
# Surveiller les modules kernel
|
|
-w /sbin/insmod -p x -k modules
|
|
-w /sbin/rmmod -p x -k modules
|
|
-w /sbin/modprobe -p x -k modules
|
|
|
|
# Rendre les règles immuables (nécessite un reboot pour changer)
|
|
-e 2
|
|
EOF
|
|
|
|
# Recharger les règles
|
|
systemctl enable auditd
|
|
systemctl restart auditd
|
|
|
|
success "Auditd configuré"
|
|
}
|
|
|
|
# ============================================================================
|
|
# CONFIGURATION APPARMOR
|
|
# ============================================================================
|
|
|
|
configure_apparmor() {
|
|
log "Configuration d'AppArmor..."
|
|
|
|
if ! command -v aa-status &> /dev/null; then
|
|
warn "AppArmor non installé, passage"
|
|
return 0
|
|
fi
|
|
|
|
# Activer AppArmor
|
|
systemctl enable apparmor
|
|
systemctl start apparmor
|
|
|
|
# Activer tous les profils en mode enforce
|
|
aa-enforce /etc/apparmor.d/* 2>/dev/null || true
|
|
|
|
# Afficher le statut
|
|
aa-status
|
|
|
|
success "AppArmor configuré"
|
|
}
|
|
|
|
# ============================================================================
|
|
# SÉCURISATION DU GRUB
|
|
# ============================================================================
|
|
|
|
secure_grub() {
|
|
log "Sécurisation du bootloader GRUB..."
|
|
|
|
if [[ ! -f /etc/default/grub ]]; then
|
|
warn "GRUB non trouvé, passage"
|
|
return 0
|
|
fi
|
|
|
|
[[ ! -f /etc/default/grub.bak ]] && cp /etc/default/grub /etc/default/grub.bak
|
|
|
|
# Ajouter les paramètres de sécurité au kernel
|
|
if ! grep -q "GRUB_CMDLINE_LINUX.*audit=1" /etc/default/grub; then
|
|
sed -i 's/GRUB_CMDLINE_LINUX="/GRUB_CMDLINE_LINUX="audit=1 /' /etc/default/grub
|
|
fi
|
|
|
|
# Générer un mot de passe GRUB si demandé
|
|
read -p "Voulez-vous protéger GRUB par mot de passe? (y/N) " -n 1 -r
|
|
echo
|
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
log "Génération du mot de passe GRUB..."
|
|
grub-mkpasswd-pbkdf2 | tee /tmp/grub-password.txt
|
|
warn "Copiez le hash PBKDF2 et ajoutez-le manuellement dans /etc/grub.d/40_custom"
|
|
warn "Exemple: set superusers=\"root\""
|
|
warn " password_pbkdf2 root <VOTRE_HASH>"
|
|
fi
|
|
|
|
# Mettre à jour GRUB
|
|
update-grub
|
|
|
|
success "GRUB sécurisé"
|
|
}
|
|
|
|
# ============================================================================
|
|
# SÉCURISATION DES PARTITIONS
|
|
# ============================================================================
|
|
|
|
secure_partitions() {
|
|
log "Vérification de la sécurisation des partitions..."
|
|
|
|
# Créer une copie de fstab
|
|
[[ ! -f /etc/fstab.bak ]] && cp /etc/fstab /etc/fstab.bak
|
|
|
|
# Suggestions pour /tmp, /var/tmp, /dev/shm
|
|
info "Vérification des options de montage sécurisées..."
|
|
|
|
local suggestions=""
|
|
|
|
# Vérifier /tmp
|
|
if mount | grep -q "on /tmp "; then
|
|
if ! mount | grep "on /tmp " | grep -q "noexec"; then
|
|
suggestions+="- /tmp devrait être monté avec noexec,nodev,nosuid\n"
|
|
fi
|
|
else
|
|
suggestions+="- Envisagez de créer une partition séparée pour /tmp\n"
|
|
fi
|
|
|
|
# Vérifier /var/tmp
|
|
if mount | grep -q "on /var/tmp "; then
|
|
if ! mount | grep "on /var/tmp " | grep -q "noexec"; then
|
|
suggestions+="- /var/tmp devrait être monté avec noexec,nodev,nosuid\n"
|
|
fi
|
|
fi
|
|
|
|
# Vérifier /dev/shm
|
|
if ! mount | grep "on /dev/shm " | grep -q "noexec"; then
|
|
suggestions+="- /dev/shm devrait être monté avec noexec,nodev,nosuid\n"
|
|
fi
|
|
|
|
# Vérifier /home
|
|
if mount | grep -q "on /home "; then
|
|
if ! mount | grep "on /home " | grep -q "nodev"; then
|
|
suggestions+="- /home devrait être monté avec nodev\n"
|
|
fi
|
|
fi
|
|
|
|
if [[ -n "$suggestions" ]]; then
|
|
warn "Suggestions de sécurisation des partitions:"
|
|
echo -e "$suggestions"
|
|
warn "Ces modifications doivent être faites manuellement dans /etc/fstab"
|
|
else
|
|
success "Partitions correctement sécurisées"
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# CONFIGURATION DES LOGS
|
|
# ============================================================================
|
|
|
|
configure_logging() {
|
|
log "Configuration avancée des logs..."
|
|
|
|
# Configuration rsyslog pour logs centralisés
|
|
cat > /etc/rsyslog.d/99-hardening.conf <<'EOF'
|
|
# Configuration rsyslog - Hardening Script
|
|
|
|
# Logger les messages authpriv séparément
|
|
authpriv.* /var/log/auth.log
|
|
|
|
# Logger les cron jobs
|
|
cron.* /var/log/cron.log
|
|
|
|
# Logger les messages kernel
|
|
kern.* /var/log/kern.log
|
|
|
|
# Logger tous les messages d'urgence
|
|
*.emerg :omusrmsg:*
|
|
|
|
# Rotation et rétention
|
|
$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
|
|
EOF
|
|
|
|
systemctl restart rsyslog
|
|
|
|
# Configuration logrotate pour conservation
|
|
cat > /etc/logrotate.d/hardening <<'EOF'
|
|
/var/log/auth.log
|
|
/var/log/cron.log
|
|
/var/log/kern.log
|
|
{
|
|
rotate 90
|
|
daily
|
|
missingok
|
|
notifempty
|
|
compress
|
|
delaycompress
|
|
sharedscripts
|
|
postrotate
|
|
/usr/lib/rsyslog/rsyslog-rotate
|
|
endscript
|
|
}
|
|
EOF
|
|
|
|
success "Logging configuré"
|
|
}
|
|
|
|
# ============================================================================
|
|
# SCAN ET AUDIT FINAL
|
|
# ============================================================================
|
|
|
|
run_security_scan() {
|
|
log "Exécution du scan de sécurité final..."
|
|
|
|
local report_file="${STATE_DIR}/security_report_$(date +%Y%m%d_%H%M%S).txt"
|
|
|
|
{
|
|
echo "=========================================="
|
|
echo "RAPPORT DE SÉCURITÉ"
|
|
echo "Date: $(date)"
|
|
echo "Hostname: $(hostname)"
|
|
echo "=========================================="
|
|
echo ""
|
|
|
|
# Lynis
|
|
if command -v lynis &> /dev/null; then
|
|
echo "=== AUDIT LYNIS ==="
|
|
lynis audit system --quick --quiet
|
|
echo ""
|
|
fi
|
|
|
|
# Vérification des paquets
|
|
echo "=== VÉRIFICATION INTÉGRITÉ PAQUETS ==="
|
|
debsums -s 2>&1 | head -20
|
|
echo ""
|
|
|
|
# Rootkit check
|
|
if command -v chkrootkit &> /dev/null; then
|
|
echo "=== SCAN ROOTKIT (chkrootkit) ==="
|
|
chkrootkit -q 2>&1 | head -20
|
|
echo ""
|
|
fi
|
|
|
|
if command -v rkhunter &> /dev/null; then
|
|
echo "=== SCAN ROOTKIT (rkhunter) ==="
|
|
rkhunter --check --skip-keypress --report-warnings-only 2>&1 | head -20
|
|
echo ""
|
|
fi
|
|
|
|
# Ports ouverts
|
|
echo "=== PORTS OUVERTS ==="
|
|
ss -tulpn
|
|
echo ""
|
|
|
|
# Services actifs
|
|
echo "=== SERVICES ACTIFS ==="
|
|
systemctl list-units --type=service --state=running
|
|
echo ""
|
|
|
|
# Utilisateurs avec shell
|
|
echo "=== UTILISATEURS AVEC SHELL ==="
|
|
grep -v "/nologin\|/false" /etc/passwd
|
|
echo ""
|
|
|
|
# Dernières connexions
|
|
echo "=== DERNIÈRES CONNEXIONS ==="
|
|
last -n 20
|
|
echo ""
|
|
|
|
} | tee "$report_file"
|
|
|
|
success "Rapport de sécurité généré: $report_file"
|
|
}
|
|
|
|
# ============================================================================
|
|
# FONCTIONS D'AIDE ET D'INFORMATION
|
|
# ============================================================================
|
|
|
|
show_help() {
|
|
cat <<EOF
|
|
Script de Durcissement Linux v${SCRIPT_VERSION}
|
|
|
|
Usage: $0 [OPTIONS]
|
|
|
|
OPTIONS:
|
|
-h, --help Afficher cette aide
|
|
-v, --version Afficher la version
|
|
-f, --force Forcer la réexécution de toutes les tâches
|
|
-s, --skip TASK Ignorer une tâche spécifique
|
|
-o, --only TASK Exécuter uniquement une tâche spécifique
|
|
--status Afficher l'état des tâches
|
|
--reset Réinitialiser toutes les tâches
|
|
--list-tasks Lister toutes les tâches disponibles
|
|
|
|
EXEMPLES:
|
|
$0 # Exécution normale
|
|
$0 --force # Réexécuter toutes les tâches
|
|
$0 --only update # Exécuter uniquement la mise à jour
|
|
$0 --skip clamav # Ignorer la configuration ClamAV
|
|
$0 --status # Voir quelles tâches sont terminées
|
|
|
|
TÂCHES DISPONIBLES:
|
|
- backup_configs Sauvegarde des configurations
|
|
- update_system Mise à jour du système
|
|
- install_tools Installation des outils de sécurité
|
|
- fail2ban Configuration Fail2ban
|
|
- ufw Configuration pare-feu UFW
|
|
- ssh Configuration SSH sécurisée
|
|
- sysctl Configuration paramètres kernel
|
|
- password_policy Politique de mots de passe
|
|
- limits Limites système
|
|
- disable_services Désactivation services inutiles
|
|
- clamav Configuration antivirus
|
|
- aide Configuration détection intrusion
|
|
- auto_updates Mises à jour automatiques
|
|
- auditd Configuration audit système
|
|
- apparmor Configuration AppArmor
|
|
- grub Sécurisation GRUB
|
|
- partitions Sécurisation partitions
|
|
- logging Configuration logs
|
|
- security_scan Scan de sécurité final
|
|
|
|
EOF
|
|
}
|
|
|
|
show_status() {
|
|
log "État des tâches de durcissement:"
|
|
echo ""
|
|
|
|
local tasks=(
|
|
"backup_configs"
|
|
"update_system"
|
|
"install_tools"
|
|
"fail2ban"
|
|
"ufw"
|
|
"ssh"
|
|
"sysctl"
|
|
"password_policy"
|
|
"limits"
|
|
"disable_services"
|
|
"clamav"
|
|
"aide"
|
|
"auto_updates"
|
|
"auditd"
|
|
"apparmor"
|
|
"grub"
|
|
"partitions"
|
|
"logging"
|
|
"security_scan"
|
|
)
|
|
|
|
for task in "${tasks[@]}"; do
|
|
if is_task_done "$task"; then
|
|
local timestamp=$(cat "${STATE_DIR}/${task}.timestamp" 2>/dev/null || echo "inconnue")
|
|
echo -e "${GREEN}[✓]${NC} $task (${timestamp})"
|
|
else
|
|
echo -e "${RED}[✗]${NC} $task"
|
|
fi
|
|
done
|
|
}
|
|
|
|
list_tasks() {
|
|
echo "Tâches disponibles:"
|
|
echo " - backup_configs"
|
|
echo " - update_system"
|
|
echo " - install_tools"
|
|
echo " - fail2ban"
|
|
echo " - ufw"
|
|
echo " - ssh"
|
|
echo " - sysctl"
|
|
echo " - password_policy"
|
|
echo " - limits"
|
|
echo " - disable_services"
|
|
echo " - clamav"
|
|
echo " - aide"
|
|
echo " - auto_updates"
|
|
echo " - auditd"
|
|
echo " - apparmor"
|
|
echo " - grub"
|
|
echo " - partitions"
|
|
echo " - logging"
|
|
echo " - security_scan"
|
|
}
|
|
|
|
reset_all_tasks() {
|
|
warn "Réinitialisation de toutes les tâches..."
|
|
rm -rf "$STATE_DIR"/*.done "$STATE_DIR"/*.timestamp
|
|
success "Toutes les tâches ont été réinitialisées"
|
|
}
|
|
|
|
# ============================================================================
|
|
# FONCTION PRINCIPALE
|
|
# ============================================================================
|
|
|
|
main() {
|
|
local force_mode=false
|
|
local skip_tasks=()
|
|
local only_task=""
|
|
|
|
# Parser les arguments
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
-h|--help)
|
|
show_help
|
|
exit 0
|
|
;;
|
|
-v|--version)
|
|
echo "Version $SCRIPT_VERSION"
|
|
exit 0
|
|
;;
|
|
-f|--force)
|
|
force_mode=true
|
|
shift
|
|
;;
|
|
-s|--skip)
|
|
skip_tasks+=("$2")
|
|
shift 2
|
|
;;
|
|
-o|--only)
|
|
only_task="$2"
|
|
shift 2
|
|
;;
|
|
--status)
|
|
show_status
|
|
exit 0
|
|
;;
|
|
--reset)
|
|
check_root
|
|
reset_all_tasks
|
|
exit 0
|
|
;;
|
|
--list-tasks)
|
|
list_tasks
|
|
exit 0
|
|
;;
|
|
*)
|
|
error "Option inconnue: $1"
|
|
show_help
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Initialisation
|
|
mkdir -p "$STATE_DIR"
|
|
touch "$LOG_FILE"
|
|
|
|
log "========================================"
|
|
log "Script de Durcissement Linux v${SCRIPT_VERSION}"
|
|
log "Début: $(date)"
|
|
log "========================================"
|
|
echo ""
|
|
|
|
# Vérifications préliminaires
|
|
check_root
|
|
check_distribution
|
|
check_disk_space
|
|
check_internet_connectivity
|
|
|
|
# Acquérir le verrou
|
|
acquire_lock
|
|
|
|
# Si mode "only", exécuter uniquement la tâche spécifiée
|
|
if [[ -n "$only_task" ]]; then
|
|
info "Mode: exécution de la tâche '$only_task' uniquement"
|
|
case $only_task in
|
|
backup_configs) run_task "backup_configs" backup_configs "$force_mode" ;;
|
|
update_system) run_task "update_system" update_system "$force_mode" ;;
|
|
install_tools) run_task "install_tools" install_security_tools "$force_mode" ;;
|
|
fail2ban) run_task "fail2ban" configure_fail2ban "$force_mode" ;;
|
|
ufw) run_task "ufw" configure_ufw "$force_mode" ;;
|
|
ssh) run_task "ssh" configure_ssh "$force_mode" ;;
|
|
sysctl) run_task "sysctl" configure_sysctl "$force_mode" ;;
|
|
password_policy) run_task "password_policy" configure_password_policy "$force_mode" ;;
|
|
limits) run_task "limits" configure_limits "$force_mode" ;;
|
|
disable_services) run_task "disable_services" disable_unnecessary_services "$force_mode" ;;
|
|
clamav) run_task "clamav" configure_clamav "$force_mode" ;;
|
|
aide) run_task "aide" configure_aide "$force_mode" ;;
|
|
auto_updates) run_task "auto_updates" configure_auto_updates "$force_mode" ;;
|
|
auditd) run_task "auditd" configure_auditd "$force_mode" ;;
|
|
apparmor) run_task "apparmor" configure_apparmor "$force_mode" ;;
|
|
grub) run_task "grub" secure_grub "$force_mode" ;;
|
|
partitions) run_task "partitions" secure_partitions "$force_mode" ;;
|
|
logging) run_task "logging" configure_logging "$force_mode" ;;
|
|
security_scan) run_task "security_scan" run_security_scan "$force_mode" ;;
|
|
*)
|
|
error "Tâche inconnue: $only_task"
|
|
list_tasks
|
|
exit 1
|
|
;;
|
|
esac
|
|
exit 0
|
|
fi
|
|
|
|
# Exécution normale de toutes les tâches
|
|
info "Mode: exécution complète (utilisez --only pour exécuter une seule tâche)"
|
|
|
|
# Tableau des tâches à exécuter (dans l'ordre)
|
|
local task_execution_plan=(
|
|
"backup_configs:Sauvegarde des configurations"
|
|
"update_system:Mise à jour système"
|
|
"install_tools:Installation outils sécurité"
|
|
"ssh:Configuration SSH sécurisée"
|
|
"ufw:Configuration pare-feu UFW"
|
|
"fail2ban:Configuration Fail2ban"
|
|
"sysctl:Configuration paramètres kernel"
|
|
"password_policy:Politique mots de passe"
|
|
"limits:Limites système"
|
|
"disable_services:Désactivation services"
|
|
"clamav:Configuration antivirus"
|
|
"aide:Configuration détection intrusion"
|
|
"auto_updates:Mises à jour automatiques"
|
|
"auditd:Configuration audit système"
|
|
"apparmor:Configuration AppArmor"
|
|
"grub:Sécurisation GRUB"
|
|
"partitions:Sécurisation partitions"
|
|
"logging:Configuration logs"
|
|
"security_scan:Scan sécurité final"
|
|
)
|
|
|
|
# Exécution des tâches
|
|
local total_tasks=${#task_execution_plan[@]}
|
|
local current_task=0
|
|
local failed_tasks=()
|
|
|
|
for task_entry in "${task_execution_plan[@]}"; do
|
|
IFS=':' read -r task_name task_description <<< "$task_entry"
|
|
current_task=$((current_task + 1))
|
|
|
|
# Vérifier si la tâche doit être ignorée
|
|
if [[ " ${skip_tasks[*]} " == *" $task_name "* ]]; then
|
|
info "[$current_task/$total_tasks] Ignoré: $task_description"
|
|
continue
|
|
fi
|
|
|
|
info "[$current_task/$total_tasks] Début: $task_description"
|
|
|
|
# Exécuter la tâche
|
|
case $task_name in
|
|
backup_configs)
|
|
run_task "$task_name" backup_configs "$force_mode" || failed_tasks+=("$task_name")
|
|
;;
|
|
update_system)
|
|
run_task "$task_name" update_system "$force_mode" || failed_tasks+=("$task_name")
|
|
;;
|
|
install_tools)
|
|
run_task "$task_name" install_security_tools "$force_mode" || failed_tasks+=("$task_name")
|
|
;;
|
|
ssh)
|
|
run_task "$task_name" configure_ssh "$force_mode" || failed_tasks+=("$task_name")
|
|
;;
|
|
ufw)
|
|
run_task "$task_name" configure_ufw "$force_mode" || failed_tasks+=("$task_name")
|
|
;;
|
|
fail2ban)
|
|
run_task "$task_name" configure_fail2ban "$force_mode" || failed_tasks+=("$task_name")
|
|
;;
|
|
sysctl)
|
|
run_task "$task_name" configure_sysctl "$force_mode" || failed_tasks+=("$task_name")
|
|
;;
|
|
password_policy)
|
|
run_task "$task_name" configure_password_policy "$force_mode" || failed_tasks+=("$task_name")
|
|
;;
|
|
limits)
|
|
run_task "$task_name" configure_limits "$force_mode" || failed_tasks+=("$task_name")
|
|
;;
|
|
disable_services)
|
|
run_task "$task_name" disable_unnecessary_services "$force_mode" || failed_tasks+=("$task_name")
|
|
;;
|
|
clamav)
|
|
run_task "$task_name" configure_clamav "$force_mode" || failed_tasks+=("$task_name")
|
|
;;
|
|
aide)
|
|
run_task "$task_name" configure_aide "$force_mode" || failed_tasks+=("$task_name")
|
|
;;
|
|
auto_updates)
|
|
run_task "$task_name" configure_auto_updates "$force_mode" || failed_tasks+=("$task_name")
|
|
;;
|
|
auditd)
|
|
run_task "$task_name" configure_auditd "$force_mode" || failed_tasks+=("$task_name")
|
|
;;
|
|
apparmor)
|
|
run_task "$task_name" configure_apparmor "$force_mode" || failed_tasks+=("$task_name")
|
|
;;
|
|
grub)
|
|
run_task "$task_name" secure_grub "$force_mode" || failed_tasks+=("$task_name")
|
|
;;
|
|
partitions)
|
|
run_task "$task_name" secure_partitions "$force_mode" || failed_tasks+=("$task_name")
|
|
;;
|
|
logging)
|
|
run_task "$task_name" configure_logging "$force_mode" || failed_tasks+=("$task_name")
|
|
;;
|
|
security_scan)
|
|
run_task "$task_name" run_security_scan "$force_mode" || failed_tasks+=("$task_name")
|
|
;;
|
|
esac
|
|
|
|
echo ""
|
|
done
|
|
|
|
# Résumé final
|
|
log "========================================"
|
|
log "EXÉCUTION TERMINÉE"
|
|
log "Date: $(date)"
|
|
log "Durée: ~$(($SECONDS / 60)) minutes"
|
|
log "========================================"
|
|
|
|
if [[ ${#failed_tasks[@]} -eq 0 ]]; then
|
|
success "Toutes les tâches ont été exécutées avec succès!"
|
|
success "Le système a été durci avec succès."
|
|
|
|
# Afficher les recommandations finales
|
|
echo ""
|
|
info "=== RECOMMANDATIONS FINALES ==="
|
|
info "1. Testez votre connexion SSH sur le port ${SSH_PORT}"
|
|
info "2. Vérifiez les règles UFW: ufw status verbose"
|
|
info "3. Testez Fail2ban: fail2ban-client status"
|
|
info "4. Redémarrez le système pour appliquer tous les changements"
|
|
info "5. Consultez le rapport de sécurité dans: ${STATE_DIR}/"
|
|
|
|
echo ""
|
|
warn "IMPORTANT: Sauvegardez vos clés SSH et mots de passe!"
|
|
warn "Le redémarrage est recommandé pour appliquer tous les changements."
|
|
|
|
else
|
|
error "Certaines tâches ont échoué: ${failed_tasks[*]}"
|
|
warn "Consultez le fichier de log: $LOG_FILE"
|
|
warn "Vous pouvez réessayer les tâches échouées avec: $0 --only <task_name>"
|
|
fi
|
|
|
|
echo ""
|
|
info "Fichier de log complet: $LOG_FILE"
|
|
info "État des tâches: $0 --status"
|
|
|
|
# Libérer le verrou
|
|
release_lock
|
|
}
|
|
|
|
# ============================================================================
|
|
# POINT D'ENTRÉE DU SCRIPT
|
|
# ============================================================================
|
|
|
|
# Vérifier que le script n'est pas sourcé
|
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
main "$@"
|
|
fi
|
|
|
|
# ============================================================================
|
|
# NOTES DE SÉCURITÉ FINALES
|
|
# ============================================================================
|
|
|
|
# Ce script effectue les actions suivantes:
|
|
# 1. Sauvegarde des configurations existantes
|
|
# 2. Mise à jour complète du système
|
|
# 3. Installation des outils de sécurité essentiels
|
|
# 4. Configuration SSH sécurisée (port personnalisé, désactivation root login)
|
|
# 5. Configuration du pare-feu UFW avec règles restrictives
|
|
# 6. Configuration Fail2ban pour prévention des attaques par force brute
|
|
# 7. Hardening des paramètres kernel via sysctl
|
|
# 8. Politique de mots de passe stricte
|
|
# 9. Limites système pour prévenir les DoS
|
|
# 10. Désactivation des services non nécessaires
|
|
# 11. Configuration antivirus ClamAV
|
|
# 12. Configuration AIDE pour détection d'intrusion
|
|
# 13. Mises à jour automatiques de sécurité
|
|
# 14. Configuration auditd pour audit système
|
|
# 15. Activation d'AppArmor
|
|
# 16. Sécurisation du bootloader GRUB
|
|
# 17. Recommandations pour sécurisation des partitions
|
|
# 18. Configuration avancée des logs
|
|
# 19. Scan de sécurité final avec génération de rapport
|
|
|
|
# AVERTISSEMENT:
|
|
# - Testez toujours dans un environnement de test avant la production
|
|
# - Assurez-vous d'avoir un accès de secours (console, KVM, etc.)
|
|
# - Conservez une sauvegarde fonctionnelle du système
|
|
# - Certaines configurations peuvent nécessiter un redémarrage |