112 lines
5.2 KiB
Bash
Executable File
112 lines
5.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# 01-hardening.sh — T2.1 : Durcissement Ubuntu Server 26.04 du G9
|
|
# Idempotent. Exécuter en root (sudo).
|
|
#
|
|
# Garde-fous : G6 (réseau LAN-only), G7 (rollback), G8 (journalisation).
|
|
#
|
|
# Fait :
|
|
# - SSH : clés uniquement (Pas de mot de passe), port par défaut 22
|
|
# - ufw : actif, par défaut deny incoming / allow outgoing
|
|
# - unattended-upgrades : activé
|
|
# - fail2ban : actif
|
|
#
|
|
# Rollback :
|
|
# - Re-activer PasswordAuthentication yes dans /etc/ssh/sshd_config
|
|
# - ufw disable ; restaurer les règles sauvegardées avant le reset
|
|
# (`cp /etc/ufw/before.rules.bak.<epoch> /etc/ufw/before.rules`, idem user.rules, puis `ufw reload`)
|
|
# - systemctl disable --now fail2ban unattended-upgrades
|
|
#
|
|
# ⚠️ Maintenance : la liste de ports ouverts (TCP/UDP, LAN-only) est **en dur** dans ce script
|
|
# et doit rester **alignée sur `compose/*/docker-compose.yml`** (source des ports publiés).
|
|
# Ajouter une brique = ajouter son port ici, sinon le service sera injoignable depuis la LAN.
|
|
|
|
set -euo pipefail
|
|
|
|
log() { echo -e "\n[hardening] $*"; }
|
|
journal() {
|
|
local action="$1" raison="$2" resultat="$3" rollback="$4"
|
|
printf '| %s | 2 | hardening : %s | %s | %s | %s |\n' "$(date '+%Y-%m-%d %H:%M')" "$action" "$raison" "$resultat" "$rollback" >> "$(dirname "$0")/../journal.md"
|
|
}
|
|
|
|
[[ $EUID -eq 0 ]] || { echo "ERREUR : exécuter en root (sudo)"; exit 1; }
|
|
|
|
# --- 1. SSH : clés uniquement ---
|
|
log "SSH : désactivation de l'authentification par mot de passe"
|
|
SSHD=/etc/ssh/sshd_config
|
|
|
|
# Guard anti-lockout (G1) : couper PasswordAuthentication sans clé installée = porte SSH fermée.
|
|
RUN_USER="${SUDO_USER:-$USER}"
|
|
RUN_HOME="$(getent passwd "$RUN_USER" | cut -d: -f6)"
|
|
if ! grep -qsE '^(ssh-|ecdsa-|sk-)' "$RUN_HOME/.ssh/authorized_keys" 2>/dev/null; then
|
|
echo "ERREUR : aucune clé publique dans $RUN_HOME/.ssh/authorized_keys pour '$RUN_USER'."
|
|
echo "Installe d'abord une clé (ssh-copy-id), puis relance. Rien n'a été modifié."
|
|
exit 1
|
|
fi
|
|
cp -n "$SSHD" "$SSHD.bak.$(date +%s)" 2>/dev/null || true
|
|
sed -ri 's/^#?PasswordAuthentication.*/PasswordAuthentication no/' "$SSHD"
|
|
sed -ri 's/^#?PermitRootLogin.*/PermitRootLogin prohibit-password/' "$SSHD"
|
|
sed -ri 's/^#?X11Forwarding.*/X11Forwarding no/' "$SSHD"
|
|
# Drop-ins sshd_config.d : la PREMIÈRE occurrence gagne (Include en tête de sshd_config)
|
|
# → désactiver le mot de passe aussi dans les drop-ins (ex. 50-cloud-init.conf).
|
|
for dropin in "$SSHD".d/*.conf; do
|
|
[[ -f "$dropin" ]] || continue
|
|
cp -n "$dropin" "$dropin.bak.$(date +%s)" 2>/dev/null || true
|
|
sed -ri 's/^#?PasswordAuthentication.*/PasswordAuthentication no/' "$dropin"
|
|
done
|
|
systemctl restart ssh
|
|
journal "SSH keys-only activé" "Durcissement SSH (G6)" "PasswordAuthentication no, PermitRootLogin prohibit-password" "Re-activer PasswordAuthentication yes dans sshd_config"
|
|
|
|
# --- 2. ufw ---
|
|
log "ufw : activation (deny incoming / allow outgoing)"
|
|
# G8 : les règles existantes sont sauvegardées AVANT le reset (ufw --force reset est destructif).
|
|
ufw_bak="/etc/ufw/before.rules.bak.$(date +%s)"
|
|
cp -a /etc/ufw/before.rules "$ufw_bak" 2>/dev/null || true
|
|
cp -a /etc/ufw/user.rules "/etc/ufw/user.rules.bak.$(date +%s)" 2>/dev/null || true
|
|
log "règles ufw sauvegardées : $ufw_bak"
|
|
ufw --force reset >/dev/null
|
|
ufw default deny incoming
|
|
ufw default allow outgoing
|
|
ufw allow OpenSSH
|
|
# Services LAN-only (LAN-only, l'exposition WAN passe par l'edge — S9.2/S9.3).
|
|
# Listes paramétrables (défaut = G9 .24) : J5040 edge = TCP_LAN="53 3000 8081"
|
|
# UDP_LAN="53" WAN_TCP="80 443" WAN_UDP="51820".
|
|
TCP_LAN="${TCP_LAN:-2342 3000 3100 5678 5984 8008 8009 8080 8081 8096 9980}"
|
|
UDP_LAN="${UDP_LAN:-2456 2457 2458}"
|
|
WAN_TCP="${WAN_TCP:-443 80}"
|
|
WAN_UDP="${WAN_UDP:-}"
|
|
for port in $TCP_LAN; do
|
|
ufw allow from 192.168.1.0/24 to any port "$port" proto tcp
|
|
done
|
|
for port in $UDP_LAN; do
|
|
ufw allow from 192.168.1.0/24 to any port "$port" proto tcp
|
|
ufw allow from 192.168.1.0/24 to any port "$port" proto udp
|
|
done
|
|
# Edge : ports exposés WAN (443 service + 80 redirect ACME/HTTP→HTTPS ; 51820 = WireGuard).
|
|
for port in $WAN_TCP; do
|
|
ufw allow "$port/tcp" comment "edge WAN ($port)"
|
|
done
|
|
for port in $WAN_UDP; do
|
|
ufw allow "$port/udp" comment "edge WAN ($port)"
|
|
done
|
|
ufw --force enable
|
|
journal "ufw activé" "Firewall LAN-only (G6)" "deny incoming, allow outgoing, OpenSSH + TCP_LAN=[$TCP_LAN] + UDP_LAN=[$UDP_LAN] (LAN-only) + WAN_TCP=[$WAN_TCP] WAN_UDP=[$WAN_UDP]" "ufw disable"
|
|
|
|
# --- 3. unattended-upgrades ---
|
|
log "unattended-upgrades : activation"
|
|
apt-get update -qq
|
|
apt-get install -y -qq unattended-upgrades
|
|
dpkg-reconfigure -f noninteractive unattended-upgrades
|
|
systemctl enable --now unattended-upgrades
|
|
journal "unattended-upgrades activé" "Mises à jour de sécurité auto (G7)" "installé + activé" "systemctl disable --now unattended-upgrades"
|
|
|
|
# --- 4. fail2ban ---
|
|
log "fail2ban : installation + activation"
|
|
apt-get install -y -qq fail2ban
|
|
cp -n "$(dirname "$0")/../configs/fail2ban.local.conf" /etc/fail2ban/jail.local
|
|
systemctl enable --now fail2ban
|
|
journal "fail2ban activé" "Anti brute-force SSH (G7)" "installé + activé" "systemctl disable --now fail2ban"
|
|
|
|
log "Terminé. Vérif : ss -tlnp | grep :22, ufw status, systemctl is-active fail2ban unattended-upgrades"
|
|
echo "AVANT de couper ton accès SSH, ouvre une 2e session et teste 'ssh <user>@G9' avec ta clé."
|