Actualiser system_hardening_optimized.sh

This commit is contained in:
Johnny 2026-01-05 21:13:23 +00:00
parent 844395e911
commit 171957aca5
1 changed files with 642 additions and 130 deletions

View File

@ -45,6 +45,7 @@ readonly DEFAULT_UMASK="027"
: "${AUTO_YES:=no}"
: "${AUTO_CLEANUP_SSH:=no}"
: "${AUTO_CHANGE_ROOT_PWD:=yes}"
: "${AUTO_SKIP_DEBSUMS_CONTAINER:=yes}"
TOTAL_STEPS=33
CURRENT_STEP=1
@ -198,12 +199,141 @@ skip_step() {
}
detect_container() {
grep -qE "lxc|container" /proc/self/cgroup 2>/dev/null || \
[[ -f /.dockerenv ]] || [[ -d /dev/lxc ]]
# -----------------------------------------------------------------
# DÉTECTION DE TOUS TYPES DE CONTENEURS (LXC, Docker, systemd-nspawn, etc.)
# Retourne 0 si dans un conteneur, 1 sinon
# -----------------------------------------------------------------
# 1. FICHIERS SPÉCIFIQUES
[[ -f /.dockerenv ]] && { log_message "Conteneur détecté: /.dockerenv" "DETECT"; return 0; }
[[ -d /dev/lxc ]] && { log_message "Conteneur détecté: /dev/lxc" "DETECT"; return 0; }
[[ -f /.lxcpid ]] && { log_message "Conteneur détecté: /.lxcpid" "DETECT"; return 0; }
# 2. VARIABLES D'ENVIRONNEMENT (utilisation de ${var:-} pour éviter "unbound variable")
[[ -n "${container:-}" ]] && { log_message "Conteneur détecté: variable \$container=$container" "DETECT"; return 0; }
[[ -n "${DOCKER_HOST:-}" ]] && { log_message "Conteneur détecté: variable \$DOCKER_HOST" "DETECT"; return 0; }
[[ -n "${KUBERNETES_SERVICE_HOST:-}" ]] && { log_message "Conteneur détecté: Kubernetes" "DETECT"; return 0; }
# 3. CGROUPS (compatible v1 et v2)
if [[ -f /proc/1/cgroup ]]; then
# Lecture de /proc/1/cgroup (plus fiable que /proc/self/cgroup)
local cgroup_content
cgroup_content=$(cat /proc/1/cgroup 2>/dev/null || echo "")
# Modèles de conteneurs dans cgroups
if echo "$cgroup_content" | grep -qE "(docker|lxc|kubepods|containerd|podman|buildkit|systemd-nspawn|libpod)"; then
log_message "Conteneur détecté: cgroups pattern" "DETECT"
return 0
fi
# Cgroups v2: format "0::/"
if echo "$cgroup_content" | grep -q "^0::/"; then
# Vérifier si le chemin n'est pas un chemin système standard
local cgroup_path
cgroup_path=$(echo "$cgroup_content" | grep "^0::/" | cut -d: -f3)
# Les chemins typiques des conteneurs
if [[ "$cgroup_path" == "/" ]] ||
[[ "$cgroup_path" == /user.slice* ]] ||
[[ "$cgroup_path" == /system.slice* ]]; then
# Pour cgroups v2, vérifier l'isolation des namespaces
if [[ -r /proc/1/ns/pid ]] && [[ -r /proc/self/ns/pid ]]; then
local pid1_inode
local self_inode
pid1_inode=$(stat -Lc '%i' /proc/1/ns/pid 2>/dev/null)
self_inode=$(stat -Lc '%i' /proc/self/ns/pid 2>/dev/null)
# Si les inodes sont différents, on est dans un namespace PID isolé (conteneur)
if [[ -n "$pid1_inode" ]] && [[ -n "$self_inode" ]] && [[ "$pid1_inode" != "$self_inode" ]]; then
log_message "Conteneur détecté: namespace PID isolé (cgroups v2)" "DETECT"
return 0
fi
fi
else
# Chemin non standard -> probablement un conteneur
log_message "Conteneur détecté: cgroups v2 path non standard: $cgroup_path" "DETECT"
return 0
fi
fi
fi
# 4. SYSTEMD-DETECT-VIRT (outil natif)
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")
log_message "Conteneur détecté: systemd-detect-virt=$virt" "DETECT"
return 0
;;
esac
fi
# 5. VÉRIFICATION DES PROCESSUS ET NAMESPACES
# Vérifier l'isolation des namespaces (méthode avancée)
if [[ -r /proc/1/ns/pid ]] && [[ -r /proc/self/ns/pid ]]; then
local ns_pid1
local ns_self
ns_pid1=$(readlink /proc/1/ns/pid 2>/dev/null)
ns_self=$(readlink /proc/self/ns/pid 2>/dev/null)
if [[ -n "$ns_pid1" ]] && [[ -n "$ns_self" ]] && [[ "$ns_pid1" != "$ns_self" ]]; then
log_message "Conteneur détecté: namespace PID différent (PID1: $ns_pid1, Self: $ns_self)" "DETECT"
return 0
fi
fi
# 6. VÉRIFICATION DES PROCESSUS DE GESTION DE CONTENEURS
if ps -eo comm 2>/dev/null | grep -qE "^(containerd|dockerd|runc|crun|podman|CRIO)$"; then
# Si un gestionnaire de conteneurs tourne, vérifier si nous sommes dans un conteneur
if [[ "$(unshare --pid --fork --mount-proc readlink /proc/1/exe 2>/dev/null)" != "/sbin/init" ]]; then
log_message "Conteneur détecté: gestionnaire de conteneurs actif" "DETECT"
return 0
fi
fi
# 7. VÉRIFICATION DE L'INIT (PID 1)
local init_cmd
init_cmd=$(ps -p 1 -o comm= 2>/dev/null || cat /proc/1/comm 2>/dev/null)
case "$init_cmd" in
"systemd"|"init"|"upstart")
# Init système normal - probablement pas un conteneur
;;
"containerd-shim"|"runc"|"docker-init"|"tini")
log_message "Conteneur détecté: init spécial: $init_cmd" "DETECT"
return 0
;;
esac
# 8. FICHIERS SPÉCIFIQUES À CERTAINS CONTENEURS
[[ -f /run/.containerenv ]] && { log_message "Conteneur détecté: /run/.containerenv" "DETECT"; return 0; }
[[ -d /run/host ]] && {
# /run/host existe souvent dans les conteneurs systemd-nspawn
if ! mountpoint -q /run/host 2>/dev/null; then
log_message "Conteneur détecté: /run/host présent" "DETECT"
return 0
fi
}
# 9. VOLUMES DOCKER SPÉCIFIQUES (optionnel)
if [[ -d /var/lib/docker ]] && mountpoint -q /var/lib/docker 2>/dev/null; then
# /var/lib/docker est monté - pourrait être un daemon Docker
log_message "Docker détecté via /var/lib/docker monté" "DETECT"
# Note: Ceci détecte le daemon Docker, pas forcément qu'on est dans un conteneur
fi
# Aucune détection positive
log_message "Aucun conteneur détecté" "DETECT"
return 1
}
detect_lxc() {
grep -q "lxc" /proc/self/cgroup 2>/dev/null || [[ -d /dev/lxc ]]
# Détection spécifique 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() {
@ -649,6 +779,88 @@ get_ssh_port_to_use() {
# FONCTIONS DE DURCISSEMENT (COMPLÈTES)
# ==============================================================================
configure_banners() {
local step_name="configure_banners"
if check_step_done "$step_name"; then
skip_step "${STEP_DESCRIPTIONS[$step_name]}"
return 0
fi
print_step "Configuration des bannières légales"
# Bannière pour les connexions locales (login)
local issue_banner="
╔══════════════════════════════════════════════════════════════════╗
║ SYSTÈME SÉCURISÉ ║
║ ║
║ Accès strictement réservé aux personnes autorisées. ║
║ Toute tentative d'accès non autorisé est strictement interdite ║
║ et fera l'objet de poursuites judiciaires. ║
║ ║
║ Toutes les activités sur ce système sont surveillées et ║
║ enregistrées conformément à la réglementation en vigueur. ║
║ ║
║ Date et heure: \$(date)
║ Système: \$(hostname) (\$(uname -o) \$(uname -r))
╚══════════════════════════════════════════════════════════════════╝
"
# Bannière pour SSH (issue.net)
local issue_net_banner="
╔══════════════════════════════════════════════════════════════════╗
║ SYSTÈME SÉCURISÉ ║
║ ║
║ ATTENTION: Accès restreint aux utilisateurs autorisés ║
║ ║
║ Vous accédez à un système informatique protégé. ║
║ Toute utilisation non autorisée est interdite et ║
║ peut faire l'objet de poursuites. ║
║ ║
║ En vous connectant, vous acceptez que votre session ║
║ soit surveillée et enregistrée. ║
║ ║
║ Système: \$(hostname)
║ Adresse IP: \$(who am i | awk '{print \$5}' | sed 's/[()]//g')
╚══════════════════════════════════════════════════════════════════╝
"
# Bannière pour motd (Message of the Day)
local motd_banner="
========================================================================
SYSTÈME SÉCURISÉ
------------------------------------------------------------------------
Ce système est configuré pour une haute sécurité.
Toutes les connexions sont tracées et enregistrées.
Dernière mise à jour sécurité: $(date +%Y-%m-%d)
Connexions actives: $(who | wc -l)
Uptime: $(uptime -p)
========================================================================
"
# Appliquer les bannières
print_info "Configuration de /etc/issue (login local)..."
echo "$issue_banner" > /etc/issue
chmod 644 /etc/issue
print_info "Configuration de /etc/issue.net (SSH)..."
echo "$issue_net_banner" > /etc/issue.net
chmod 644 /etc/issue.net
print_info "Configuration de /etc/motd (Message du jour)..."
echo "$motd_banner" > /etc/motd
chmod 644 /etc/motd
# Désactiver les motd dynamiques si existants
if [[ -d /etc/update-motd.d ]]; then
print_info "Désactivation des motd dynamiques..."
chmod -x /etc/update-motd.d/* 2>/dev/null || true
fi
print_success "Bannières légales configurées"
mark_step_done "$step_name"
}
install_security_tools() {
local step_name="install_security_tools"
if check_step_done "$step_name"; then
@ -1401,72 +1613,148 @@ harden_ssh() {
backup_file "/etc/ssh/sshd_config"
local sshd_config="/etc/ssh/sshd_config"
local ssh_port=$(get_ssh_port_to_use)
# Fonction utilitaire pour mettre à jour les paramètres SSH
update_ssh_param() {
local param="$1"
local value="$2"
sed -i "/^#*${param}[[:space:]]/d" "$sshd_config"
echo "$param $value" >> "$sshd_config"
}
# Configuration de base
update_ssh_param "Port" "$ssh_port"
# Si c'est un nouveau port et pas en LXC, garder le port 22 temporairement
if [[ "$ssh_port" != "22" ]] && ! detect_lxc; then
update_ssh_param "Port" "22"
update_ssh_param "Port" "$ssh_port"
print_info "Port 22 maintenu temporairement avec port $ssh_port"
fi
# Authentification
if [[ "$AUTO_DISABLE_ROOT_LOGIN" == "yes" ]]; then
update_ssh_param "PermitRootLogin" "no"
print_info "Connexion root SSH désactivée"
# DÉTERMINER LE PORT SSH À UTILISER
local ssh_port="22" # Par défaut
if detect_lxc; then
print_info "Conteneur LXC détecté - utilisation du port 22"
ssh_port="22"
else
update_ssh_param "PermitRootLogin" "prohibit-password"
print_info "Connexion root SSH autorisée uniquement par clé"
ssh_port="$AUTO_SSH_PORT"
# Valider que le port est un nombre entre 1 et 65535
if ! [[ "$ssh_port" =~ ^[0-9]+$ ]] || [ "$ssh_port" -lt 1 ] || [ "$ssh_port" -gt 65535 ]; then
print_warning "Port SSH invalide ($ssh_port) - utilisation du port 22"
ssh_port="22"
fi
fi
# Paramètres de sécurité
update_ssh_param "PasswordAuthentication" "no"
update_ssh_param "PubkeyAuthentication" "yes"
update_ssh_param "PermitEmptyPasswords" "no"
update_ssh_param "X11Forwarding" "no"
update_ssh_param "MaxAuthTries" "3"
update_ssh_param "ClientAliveInterval" "300"
update_ssh_param "ClientAliveCountMax" "2"
update_ssh_param "LoginGraceTime" "60"
update_ssh_param "MaxSessions" "2"
update_ssh_param "TCPKeepAlive" "no"
update_ssh_param "AllowAgentForwarding" "no"
update_ssh_param "AllowTcpForwarding" "no"
update_ssh_param "LogLevel" "VERBOSE"
update_ssh_param "Compression" "no"
update_ssh_param "Protocol" "2"
update_ssh_param "UseDNS" "no"
update_ssh_param "IgnoreRhosts" "yes"
update_ssh_param "HostbasedAuthentication" "no"
update_ssh_param "PrintLastLog" "yes"
update_ssh_param "StrictModes" "yes"
print_info "Configuration SSH avec le port: $ssh_port"
# Algorithmes modernes
cat >> "$sshd_config" << 'EOF'
# Algorithmes de chiffrement modernes
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,umac-128-etm@openssh.com
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256
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
# CRÉER UNE CONFIGURATION SSH SÉCURISÉE
# D'abord créer un fichier temporaire
local temp_config=$(mktemp)
# Construire la configuration ligne par ligne pour éviter les problèmes de formatage
{
echo "# ============================================================================"
echo "# Configuration SSH sécurisée - Générée automatiquement $(date)"
echo "# ============================================================================"
echo ""
# Section Ports
echo "# Ports d'écoute"
if [[ "$ssh_port" != "22" ]] && ! detect_lxc; then
echo "Port 22"
echo "Port $ssh_port"
echo "# Port 22 maintenu temporairement pour la transition"
else
echo "Port $ssh_port"
fi
echo ""
echo "# Adresse d'écoute"
echo "ListenAddress 0.0.0.0"
echo "ListenAddress ::"
echo ""
echo "# Authentification"
if [[ "$AUTO_DISABLE_ROOT_LOGIN" == "yes" ]]; then
echo "PermitRootLogin no"
print_info "Connexion root SSH désactivée"
else
echo "PermitRootLogin prohibit-password"
print_info "Connexion root SSH autorisée uniquement par clé"
fi
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 ""
echo "# Sécurité"
echo "Protocol 2"
echo "StrictModes yes"
echo "UseDNS no"
echo "IgnoreRhosts yes"
echo "HostbasedAuthentication no"
echo "PrintLastLog yes"
echo ""
echo "# Redirections"
echo "X11Forwarding no"
echo "AllowAgentForwarding no"
echo "AllowTcpForwarding no"
echo "TCPKeepAlive no"
echo ""
echo "# Performances et logs"
echo "Compression no"
echo "LogLevel VERBOSE"
echo ""
echo "# Bannière"
echo "Banner /etc/issue.net"
echo ""
echo "# Algorithmes de chiffrement modernes"
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"
echo ""
echo "# ============================================================================"
} > "$temp_config"
# VÉRIFIER LE FICHIER TEMPORAIRE
print_info "Vérification du fichier temporaire..."
if ! sshd -t -f "$temp_config" 2>&1; then
print_error "Configuration temporaire invalide - affichage du contenu :"
cat -n "$temp_config"
print_info "Tentative avec configuration minimaliste..."
# Configuration minimaliste pour LXC
cat > "$temp_config" << EOF
Port 22
Protocol 2
PermitRootLogin prohibit-password
PasswordAuthentication no
PubkeyAuthentication yes
PermitEmptyPasswords no
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no
ChallengeResponseAuthentication no
UsePAM yes
PrintLastLog yes
TCPKeepAlive no
ClientAliveInterval 300
ClientAliveCountMax 2
EOF
# Bannière
update_ssh_param "Banner" "/etc/issue.net"
if ! sshd -t -f "$temp_config" 2>&1; then
print_error "Configuration minimaliste aussi invalide"
rm -f "$temp_config"
return 1
fi
fi
# Test et application
print_info "Test de la configuration SSH..."
if sshd -t 2>&1 | tee -a "$LOG_FILE"; then
# COPIER LA CONFIGURATION
cp "$temp_config" "$sshd_config"
chmod 600 "$sshd_config"
chown root:root "$sshd_config"
rm -f "$temp_config"
# AFFICHER LES PREMIÈRES LIGNES POUR VÉRIFICATION
print_info "Premières lignes de la configuration :"
head -n 15 "$sshd_config" | cat -n
# TEST FINAL ET APPLICATION
print_info "Test final de la configuration SSH..."
if sshd -t -f "$sshd_config" 2>&1 | tee -a "$LOG_FILE"; then
print_success "Configuration SSH valide"
print_info "Redémarrage du service SSH..."
@ -1476,12 +1764,10 @@ EOF
if [[ "$ssh_port" != "22" ]] && ! detect_lxc; then
print_warning "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_warning "⚠ Port SSH: $ssh_port (22 temporairement actif)"
if [[ "$AUTO_CLEANUP_SSH" == "yes" ]] || auto_confirm; then
print_info "Nettoyage automatique du port 22 programmé"
else
print_warning "Testez: ssh -p $ssh_port $(whoami)@$(hostname -I | awk '{print $1}')"
fi
print_warning "Testez: ssh -p $ssh_port $(whoami)@localhost"
print_warning "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
else
print_info "Port SSH: $ssh_port"
fi
else
print_error "Échec redémarrage SSH - restauration..."
@ -1490,7 +1776,9 @@ EOF
return 1
fi
else
print_error "Configuration SSH invalide"
print_error "Configuration SSH invalide après copie"
print_info "Contenu complet du fichier :"
cat -n "$sshd_config"
cp "${BACKUP_DIR}/sshd_config" /etc/ssh/sshd_config
return 1
fi
@ -1498,31 +1786,6 @@ EOF
mark_step_done "$step_name"
}
configure_banners() {
local step_name="configure_banners"
if check_step_done "$step_name"; then
skip_step "${STEP_DESCRIPTIONS[$step_name]}"
return 0
fi
print_step "Configuration des bannières légales"
local banner_text="╔════════════════════════════════════════════════════════════╗
║ SYSTÈME SÉCURISÉ ║
║ ║
║ Accès réservé aux personnes autorisées uniquement. ║
║ Toute tentative d'accès non autorisée est interdite ║
║ et sera tracée et poursuivie selon la loi. ║
║ ║
║ Les activités sont surveillées et enregistrées. ║
╚════════════════════════════════════════════════════════════╝"
echo "$banner_text" | tee /etc/issue /etc/issue.net /etc/motd > /dev/null
print_success "Bannières légales configurées"
mark_step_done "$step_name"
}
configure_firewall_ports() {
local step_name="configure_firewall_ports"
if check_step_done "$step_name"; then
@ -1840,51 +2103,131 @@ verify_packages_integrity() {
print_step "Vérification de l'intégrité des paquets"
print_info "Vérification avec debsums..."
debsums -s 2>&1 | tee -a "$LOG_FILE" || print_warning "Certains paquets ont des erreurs"
print_success "Vérification d'intégrité terminée"
mark_step_done "$step_name"
}
# Vérifier si debsums est installé
if ! command -v debsums > /dev/null 2>&1; then
print_warning "debsums n'est pas installé - installation..."
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq debsums 2>/dev/null || {
print_error "Impossible d'installer debsums"
mark_step_done "$step_name"
return 0
}
fi
configure_automatic_updates() {
local step_name="configure_automatic_updates"
if check_step_done "$step_name"; then
skip_step "${STEP_DESCRIPTIONS[$step_name]}"
# Vérifier si debsums peut s'exécuter
if ! debsums --help > /dev/null 2>&1; then
print_warning "debsums ne fonctionne pas correctement"
mark_step_done "$step_name"
return 0
fi
print_step "Configuration des mises à jour automatiques"
if detect_container; then
print_info "Conteneur détecté - filtrage des faux positifs courants..."
# Configuration unattended-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}ESM:${distro_codename}";
};
Unattended-Upgrade::Origins-Pattern {
"origin=Debian,codename=${distro_codename},label=Debian-Security";
};
Unattended-Upgrade::AutoFixInterruptedDpkg "true";
Unattended-Upgrade::MinimalSteps "true";
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Automatic-Reboot "false";
Unattended-Upgrade::Automatic-Reboot-Time "04:00";
EOF
# Liste des fichiers communément modifiés dans les conteneurs (faux positifs)
local ignore_patterns=(
"/lib/systemd/system/container-getty@.service"
"/etc/hosts"
"/etc/resolv.conf"
"/etc/hostname"
"/etc/mtab"
"/etc/localtime"
"/etc/timezone"
"/etc/machine-id"
"/var/lib/dbus/machine-id"
)
# Configuration périodique
cat > /etc/apt/apt.conf.d/10periodic << 'EOF'
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "7";
APT::Periodic::Unattended-Upgrade "1";
EOF
# Construire l'expression grep pour ignorer ces fichiers
local grep_pattern=$(printf "|%s" "${ignore_patterns[@]}")
grep_pattern=${grep_pattern:1} # Enlever le premier |
# Exécuter debsums en filtrant les faux positifs
local debsums_output
debsums_output=$(debsums -s 2>&1 || true)
if [[ -n "$debsums_output" ]]; then
# Filtrer les faux positifs et les erreurs de permission
local filtered_output
filtered_output=$(echo "$debsums_output" | grep -vE "$grep_pattern" | grep -v "Permission denied" || true)
if [[ -n "$filtered_output" ]]; then
echo "$filtered_output" | tee -a "$LOG_FILE"
# Compter les vraies erreurs
local real_errors
real_errors=$(echo "$filtered_output" | wc -l)
if [[ $real_errors -gt 0 ]]; then
print_warning "$real_errors paquet(s) avec des erreurs réelles"
# Lister les paquets problématiques
echo ""
echo "Paquets concernés:"
echo "$filtered_output" | awk -F: '{print $1}' | sort -u
# Proposer une réparation en mode interactif
if ! auto_confirm; then
echo ""
read -p "Voulez-vous tenter de réparer les paquets corrompus ? (o/N): " -r repair
if [[ "$repair" =~ ^[Oo]$ ]]; then
print_info "Réparation des paquets..."
local packages_to_reinstall
packages_to_reinstall=$(echo "$filtered_output" | awk -F: '{print $1}' | sort -u | tr '\n' ' ')
if [[ -n "$packages_to_reinstall" ]]; then
apt-get install --reinstall -y $packages_to_reinstall 2>&1 | tee -a "$LOG_FILE" || \
print_warning "Certains paquets n'ont pas pu être réinstallés"
fi
fi
fi
else
print_success "Aucune erreur réelle détectée (faux positifs filtrés)"
fi
else
print_success "Aucune modification détectée (après filtrage)"
fi
else
print_success "Aucune modification détectée"
fi
else
# Système hôte - vérification complète
local debsums_output
debsums_output=$(debsums -s 2>&1 || true)
# Filtrer les erreurs de permission
local filtered_output
filtered_output=$(echo "$debsums_output" | grep -v "Permission denied" || true)
if [[ -n "$filtered_output" ]]; then
echo "$filtered_output" | tee -a "$LOG_FILE"
print_warning "Certains paquets ont des erreurs"
# Lister les paquets problématiques
echo ""
echo "Paquets concernés:"
echo "$filtered_output" | awk -F: '{print $1}' | sort -u
# Proposer une réparation
if ! auto_confirm; then
echo ""
read -p "Voulez-vous tenter de réparer ces paquets ? (o/N): " -r repair
if [[ "$repair" =~ ^[Oo]$ ]]; then
print_info "Réparation des paquets..."
local packages_to_reinstall
packages_to_reinstall=$(echo "$filtered_output" | awk -F: '{print $1}' | sort -u | tr '\n' ' ')
if [[ -n "$packages_to_reinstall" ]]; then
apt-get install --reinstall -y $packages_to_reinstall 2>&1 | tee -a "$LOG_FILE" || \
print_warning "Certains paquets n'ont pas pu être réinstallés"
fi
fi
fi
else
print_success "Tous les paquets sont intacts"
fi
fi
print_success "Mises à jour automatiques configurées"
mark_step_done "$step_name"
}
configure_aide_cron() {
local step_name="configure_aide_cron"
if check_step_done "$step_name"; then
@ -2281,6 +2624,175 @@ cleanup_ssh_port() {
fi
}
configure_automatic_updates() {
local step_name="configure_automatic_updates"
if check_step_done "$step_name"; then
skip_step "${STEP_DESCRIPTIONS[$step_name]}"
return 0
fi
print_step "Configuration des mises à jour automatiques"
# Vérifier si unattended-upgrades est installé
if ! dpkg -l | grep -q "^ii.*unattended-upgrades"; then
print_info "Installation de unattended-upgrades..."
DEBIAN_FRONTEND=noninteractive apt-get update -qq
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq unattended-upgrades apt-listchanges needrestart 2>/dev/null || {
print_warning "Échec installation complète - continuation avec ce qui est disponible"
}
fi
# Configuration principale de unattended-upgrades
backup_file "/etc/apt/apt.conf.d/50unattended-upgrades"
cat > /etc/apt/apt.conf.d/50unattended-upgrades << 'EOF'
// Configuration des mises à jour automatiques sécurisées
Unattended-Upgrade::Allowed-Origins {
"${distro_id}:${distro_codename}";
"${distro_id}:${distro_codename}-security";
"${distro_id}:${distro_codename}-updates";
"${distro_id}ESM:${distro_codename}";
"${distro_id}ESM-Apps:${distro_codename}";
};
// Options de comportement
Unattended-Upgrade::AutoFixInterruptedDpkg "true";
Unattended-Upgrade::MinimalSteps "true";
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Remove-New-Unused-Dependencies "true";
Unattended-Upgrade::Automatic-Reboot "false";
Unattended-Upgrade::Automatic-Reboot-Time "04:00";
Unattended-Upgrade::Automatic-Reboot-WithUsers "false";
// Nettoyage automatique
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
// Notifications par email (si postfix/sendmail est installé)
Unattended-Upgrade::Mail "root";
Unattended-Upgrade::MailOnlyOnError "true";
Unattended-Upgrade::MailReport "on-change";
// Téléchargement et installation
Unattended-Upgrade::Download-Upgradeable-Packages "true";
Unattended-Upgrade::DevRelease "auto";
// Mise à jour du GRUB si nécessaire
Unattended-Upgrade::Update-Boot-App "true";
EOF
# Configuration périodique pour APT (ne pas essayer de sauvegarder si le fichier n'existe pas)
if [[ -f "/etc/apt/apt.conf.d/10periodic" ]]; then
backup_file "/etc/apt/apt.conf.d/10periodic"
fi
cat > /etc/apt/apt.conf.d/10periodic << 'EOF'
// Planification des tâches APT automatiques
APT::Periodic::Update-Package-Lists "1"; // Mettre à jour les listes quotidiennement
APT::Periodic::Download-Upgradeable-Packages "1"; // Télécharger les paquets upgradables
APT::Periodic::AutocleanInterval "7"; // Nettoyer le cache tous les 7 jours
APT::Periodic::Unattended-Upgrade "1"; // Exécuter unattended-upgrade
APT::Periodic::Verbose "0"; // Mode silencieux
APT::Periodic::RandomSleep "300"; // Délai aléatoire pour éviter la charge simultanée
EOF
# Configuration pour apt-listchanges (uniquement si le fichier existe)
if [[ -f "/etc/apt/listchanges.conf" ]]; then
backup_file "/etc/apt/listchanges.conf"
sed -i 's/^frontend=.*/frontend=mail/' /etc/apt/listchanges.conf 2>/dev/null || true
sed -i 's/^which=.*/which=both/' /etc/apt/listchanges.conf 2>/dev/null || true
sed -i 's/^email_address=.*/email_address=root/' /etc/apt/listchanges.conf 2>/dev/null || true
sed -i 's/^confirm=.*/confirm=0/' /etc/apt/listchanges.conf 2>/dev/null || true
else
# Créer une configuration minimale si le fichier n'existe pas
cat > /etc/apt/listchanges.conf << 'EOF' 2>/dev/null || true
[apt]
frontend=mail
email_address=root
confirm=0
save_seen=/var/lib/apt/listchanges.db
which=both
EOF
fi
# Configuration pour needrestart (gestion conditionnelle)
if [[ -f "/etc/needrestart/needrestart.conf" ]]; then
backup_file "/etc/needrestart/needrestart.conf"
sed -i 's/^#\$nrconf{restart}.*/\$nrconf{restart} = '"'a'"';/' /etc/needrestart/needrestart.conf 2>/dev/null || true
sed -i 's/^#\$nrconf{override_rc}.*/\$nrconf{override_rc} = '"'0'"';/' /etc/needrestart/needrestart.conf 2>/dev/null || true
else
# Créer le répertoire et le fichier si nécessaire
mkdir -p /etc/needrestart/ 2>/dev/null || true
cat > /etc/needrestart/needrestart.conf << 'EOF' 2>/dev/null || true
# Configuration de needrestart
$nrconf{restart} = 'a'; # Redémarrer automatiquement les services
$nrconf{override_rc} = 0; # Ne pas modifier les scripts rc
$nrconf{html_browser} = ''; # Pas de navigateur HTML
EOF
fi
# Configuration du timer systemd (optionnel)
if command -v systemctl >/dev/null 2>&1; then
# Créer le répertoire pour les overrides
mkdir -p /etc/systemd/system/apt-daily.timer.d/ 2>/dev/null || true
# Ne pas essayer de sauvegarder un fichier qui n'existe pas
if [[ -f "/etc/systemd/system/apt-daily.timer" ]]; then
backup_file "/etc/systemd/system/apt-daily.timer"
fi
# Créer l'override
cat > /etc/systemd/system/apt-daily.timer.d/override.conf << 'EOF' 2>/dev/null || true
[Timer]
OnCalendar=
OnCalendar=*-*-* 3:00
RandomizedDelaySec=1h
EOF
systemctl daemon-reload 2>/dev/null || true
fi
# Activer le service
print_info "Activation du service unattended-upgrades..."
if systemctl is-enabled unattended-upgrades >/dev/null 2>&1; then
systemctl restart unattended-upgrades 2>&1 | tee -a "$LOG_FILE" || true
print_info "Service unattended-upgrades redémarré"
else
systemctl enable unattended-upgrades 2>&1 | tee -a "$LOG_FILE" || true
systemctl start unattended-upgrades 2>&1 | tee -a "$LOG_FILE" || true
print_info "Service unattended-upgrades activé et démarré"
fi
# Vérification simple
sleep 2
if systemctl is-active unattended-upgrades >/dev/null 2>&1; then
print_success "Service unattended-upgrades actif"
else
print_warning "Service unattended-upgrades inactif (démarrage différé)"
fi
# Tester la configuration (optionnel)
if command -v unattended-upgrade >/dev/null 2>&1; then
print_info "Test de configuration unattended-upgrades..."
unattended-upgrade --dry-run --debug 2>&1 | grep -i "checking\|result" | head -5 | tee -a "$LOG_FILE" || true
fi
# Afficher un résumé
echo ""
print_info "Configuration des mises à jour automatiques:"
echo " ✓ Configuration unattended-upgrades appliquée"
echo " ✓ Planification quotidienne configurée"
echo " ✓ Mises à jour de sécurité activées"
echo " ✓ Nettoyage automatique activé"
echo ""
print_success "Mises à jour automatiques configurées"
mark_step_done "$step_name"
}
# ==============================================================================
# FONCTIONS PRINCIPALES
# ==============================================================================