First commit

This commit is contained in:
2026-07-20 11:05:07 +02:00
commit b592ab669f
159 changed files with 10294 additions and 0 deletions
+93
View File
@@ -0,0 +1,93 @@
# firewall-filters.conf — règles spécifiques aux services
# Chargé par fw.sh après les protections de base.
# Variables disponibles : $ETH_IF, $VPN_IF, $WG_IF
IP="x.x.x.x"
VPN_NET="10.8.0.0/24"
VPN_PORT="1194"
WG_NET="10.10.0.0/24"
WG_PORT="51820"
WG_WEB_PORT="51821"
PORTAINER_PORT="9000"
PORTAINER_SSL_PORT="9443"
SSH_PORT="22"
echo " [Services : SSH / HTTP / OpenVPN / Coturn / MQTT]"
# ─── SSH ──────────────────────────────────────────────────────────────────────
iptables -A INPUT -i $ETH_IF -p tcp --dport $SSH_PORT \
-m conntrack --ctstate NEW \
-m connlimit --connlimit-above 4 --connlimit-mask 32 -j LOG_DROP
iptables -A INPUT -i $ETH_IF -p tcp --dport $SSH_PORT \
-m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
# ─── HTTP / HTTPS ─────────────────────────────────────────────────────────────
iptables -A INPUT -i $ETH_IF -p tcp --dport 80 \
-m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
iptables -A INPUT -i $ETH_IF -p tcp --dport 443 \
-m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
# ─── MOSQUITTO TLS (accessible depuis internet) ───────────────────────────────
iptables -A INPUT -i $ETH_IF -p tcp --dport 8883 \
-m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
#iptables -A INPUT -i $ETH_IF -p tcp --dport 8884 \
# -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
# ─── COTURN ───────────────────────────────────────────────────────────────────
# Signaling STUN/TURN
iptables -A INPUT -i $ETH_IF -p udp --dport 3478 \
-m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
iptables -A INPUT -i $ETH_IF -p tcp --dport 3478 \
-m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
# Relai TURN UDP — hashlimit par IP source (anti-amplification)
iptables -A INPUT -i $ETH_IF -p udp --dport 49152:65535 \
-m hashlimit --hashlimit-name coturn-relay \
--hashlimit-upto 100/s --hashlimit-burst 200 \
--hashlimit-mode srcip -j ACCEPT
# ─── REDIS : DROP explicite depuis internet ────────────────────────────────────
# Redis doit rester sur 127.0.0.1 ou socket Unix — cette règle est un filet de sécurité
iptables -A INPUT -i $ETH_IF -p tcp --dport 6379 -j DROP
# ─── Transmission (décommenter si actif) ──────────────────────────────────────
#iptables -A INPUT -i $ETH_IF -p tcp --dport 51413 \
# -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
# ─── MAIL (décommenter si besoin) ─────────────────────────────────────────────
#iptables -A INPUT -i $ETH_IF -p tcp --dport 465 \
# -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
#iptables -A INPUT -i $ETH_IF -p tcp --dport 993 \
# -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
# ─── WIREGUARD ──────────────────────────────────────────────────────────────────
#iptables -A INPUT -i $ETH_IF -p udp --dport $WG_PORT \
# -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
#iptables -I INPUT -i $WG_IF -p tcp --dport $WG_WEB_PORT -j ACCEPT
#iptables -I INPUT -i $WG_IF -p tcp --dport $PORTAINER_PORT -j ACCEPT
#iptables -I INPUT -i $WG_IF -p tcp --dport $PORTAINER_SSL_PORT -j ACCEPT
# ─── DOCKER USER ──────────────────────────────────────────────────────────────────
iptables -I DOCKER-USER -i $ETH_IF -j DROP
iptables -I DOCKER-USER -i $WG_IF -j RETURN # VPN autorisé
iptables -I DOCKER-USER -i lo -j RETURN # loopback autorisé
# ─── OPENVPN ──────────────────────────────────────────────────────────────────
iptables -A INPUT -i $ETH_IF -p udp --dport $VPN_PORT \
-m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
# Accès complet au serveur depuis les clients VPN
iptables -A INPUT -i $VPN_IF -j ACCEPT
iptables -A OUTPUT -o $VPN_IF -j ACCEPT
# ─── FORWARD VPN → internet ───────────────────────────────────────────────────
iptables -A FORWARD -i $VPN_IF -o $ETH_IF \
-m conntrack --ctstate NEW,ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -i $ETH_IF -o $VPN_IF \
-m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -t nat -A POSTROUTING -s $VPN_NET -o $ETH_IF -j MASQUERADE
+291
View File
@@ -0,0 +1,291 @@
#!/bin/bash
# Firewall - v2.0 (2025)
# Debian 13 / iptables / fail2ban / OpenVPN / Mosquitto / Nginx
# ─── CONFIGURATION ────────────────────────────────────────────────────────────
ETH_IF="${ETH_IF:-eth0}" # valeur réelle fournie par .env (EnvironmentFile=, voir firewall.service) ; eth0 = filet de sécurité si absent
VPN_IF="${VPN_IF:-tun0}"
WG_IF="${WG_IF:-wg0}"
LAN_NET="${LAN_NET:-}" # voir section ANTI-SPOOF plus bas
FILTERS="/etc/firewall/firewall-filters.conf"
BAN_FILE="/etc/firewall/ban_ip.txt"
SYSCTL_FILE="/etc/sysctl.d/90-firewall.conf"
LOG_LIMIT="5/min"
LOG_BURST=10
IPT="iptables"
IP6T="ip6tables"
# ──────────────────────────────────────────────────────────────────────────────
fw_sysctl() {
cat > "$SYSCTL_FILE" << 'EOF'
# Anti-spoofing (Reverse Path Filtering)
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# ICMP
net.ipv4.icmp_echo_ignore_all = 0
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
# SYN cookies (protection SYN flood)
net.ipv4.tcp_syncookies = 1
# Pas de redirections ICMP ni de source routing
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.all.log_martians = 1
# Forwarding activé pour OpenVPN
net.ipv4.ip_forward = 1
# Timeouts anti-DoS (sans casser les perfs modernes)
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_keepalive_time = 1800
# NE PAS désactiver window_scaling ni sack : indispensables sur réseaux modernes
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_sack = 1
net.ipv4.ip_default_ttl = 64
# IPv6 — désactivé (adapter si utilisé)
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
EOF
sysctl -p "$SYSCTL_FILE" -q
echo " [sysctl appliqué depuis $SYSCTL_FILE]"
}
fw_sysctl_restore() {
cat > "$SYSCTL_FILE" << 'EOF'
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.default.rp_filter = 0
net.ipv4.ip_forward = 0
net.ipv4.tcp_fin_timeout = 60
net.ipv4.tcp_keepalive_time = 7200
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_sack = 1
net.ipv4.ip_default_ttl = 64
net.ipv6.conf.all.disable_ipv6 = 0
net.ipv6.conf.default.disable_ipv6 = 0
EOF
sysctl -p "$SYSCTL_FILE" -q
}
case "$1" in
start)
echo " [sysctl]"
fw_sysctl
echo " [FLUSH]"
$IPT -F
$IPT -X
$IPT -t nat -F
$IPT -t nat -X
$IPT -t mangle -F
$IPT -t mangle -X
echo " [POLITIQUES PAR DEFAUT : DROP]"
$IPT -P INPUT DROP
$IPT -P OUTPUT DROP
$IPT -P FORWARD DROP
# ─── CHAÎNES DE LOGGING ───────────────────────────────────────────────────
echo " [CHAÎNES LOG]"
$IPT -N LOG_DROP
$IPT -A LOG_DROP -m limit --limit $LOG_LIMIT --limit-burst $LOG_BURST \
-j LOG --log-prefix '[FW DROP] ' --log-level warning
$IPT -A LOG_DROP -j DROP
$IPT -N LOG_INVALID
$IPT -A LOG_INVALID -m limit --limit $LOG_LIMIT --limit-burst $LOG_BURST \
-j LOG --log-prefix '[FW INVALID] ' --log-level warning
$IPT -A LOG_INVALID -j DROP
$IPT -N LOG_SPOOF
$IPT -A LOG_SPOOF -m limit --limit $LOG_LIMIT --limit-burst $LOG_BURST \
-j LOG --log-prefix '[FW SPOOF] ' --log-level warning
$IPT -A LOG_SPOOF -j DROP
$IPT -N LOG_SCAN
$IPT -A LOG_SCAN -m limit --limit $LOG_LIMIT --limit-burst $LOG_BURST \
-j LOG --log-prefix '[FW SCAN] ' --log-level warning
$IPT -A LOG_SCAN -j DROP
$IPT -N LOG_FLOOD
$IPT -A LOG_FLOOD -m limit --limit $LOG_LIMIT --limit-burst $LOG_BURST \
-j LOG --log-prefix '[FW FLOOD] ' --log-level warning
$IPT -A LOG_FLOOD -j DROP
# ─── FAIL2BAN ─────────────────────────────────────────────────────────────
# Pré-création des chaînes fail2ban pour éviter les erreurs si fw démarre
# avant fail2ban. fail2ban les vide et les recrée à son démarrage.
echo " [FAIL2BAN : pré-création des chaînes]"
for chain in fail2ban-INPUT fail2ban-sshd fail2ban-nginx fail2ban-mosquitto; do
$IPT -N $chain 2>/dev/null || true
$IPT -F $chain 2>/dev/null || true
$IPT -A $chain -j RETURN
done
$IPT -I INPUT -j fail2ban-INPUT
# ─── LOOPBACK ─────────────────────────────────────────────────────────────
echo " [LOOPBACK]"
$IPT -A INPUT -i lo -j ACCEPT
$IPT -A OUTPUT -o lo -j ACCEPT
# Anti-spoof loopback depuis l'extérieur
$IPT -A INPUT ! -i lo -s 127.0.0.0/8 -j LOG_SPOOF
# ─── PAQUETS INVALIDES ────────────────────────────────────────────────────
echo " [PAQUETS INVALIDES]"
$IPT -A INPUT -m conntrack --ctstate INVALID -j LOG_INVALID
$IPT -A FORWARD -m conntrack --ctstate INVALID -j DROP
$IPT -A OUTPUT -m conntrack --ctstate INVALID -j DROP
# ─── CONNEXIONS ÉTABLIES ──────────────────────────────────────────────────
echo " [CONNEXIONS ETABLIES/LIEES]"
$IPT -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
$IPT -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# ─── ICMP ─────────────────────────────────────────────────────────────────
echo " [ICMP]"
# Ping entrant : limité par source (hashlimit > limit : par IP)
$IPT -A INPUT -p icmp --icmp-type echo-request \
-m hashlimit --hashlimit-name icmp-in --hashlimit-upto 5/s \
--hashlimit-burst 10 --hashlimit-mode srcip -j ACCEPT
# Types ICMP utiles en entrée
$IPT -A INPUT -p icmp --icmp-type echo-reply -j ACCEPT
$IPT -A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT
$IPT -A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT
$IPT -A OUTPUT -p icmp -j ACCEPT
# ─── ANTI-SPOOF (martians) ────────────────────────────────────────────────
# Écrit pour une IP publique dédiée (VPS) : toute IP source privée vue
# sur $ETH_IF y est forcément usurpée. Sur un noeud derrière une box
# résidentielle, $ETH_IF est aussi l'unique interface LAN — du trafic
# local légitime (192.168.x.x, parfois l'IP de la box elle-même via
# son NAT loopback/hairpin) y arrive normalement et ne doit pas être
# traité comme du spoofing. LAN_NET (vide par défaut, donc aucun
# changement de comportement pour un VPS) exempte ce sous-réseau
# avant la boucle ci-dessous — à ne définir que sur un noeud dans
# cette situation (ex. LAN_NET=192.168.1.0/24 dans .env).
if [ -n "$LAN_NET" ]; then
echo " [ANTI-SPOOF : LAN_NET=$LAN_NET exempté (trafic local légitime)]"
$IPT -A INPUT -i "$ETH_IF" -s "$LAN_NET" -j ACCEPT
fi
echo " [ANTI-SPOOF / MARTIANS]"
for net in 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 \
169.254.0.0/16 224.0.0.0/4 240.0.0.0/5 \
0.0.0.0/8 127.0.0.0/8; do
$IPT -A INPUT -i "$ETH_IF" -s $net -j LOG_SPOOF
done
# ─── FLAGS TCP INVALIDES ──────────────────────────────────────────────────
echo " [FLAGS TCP INVALIDES]"
$IPT -A INPUT -p tcp --tcp-flags ALL NONE -j LOG_SCAN # NULL scan
$IPT -A INPUT -p tcp --tcp-flags ALL ALL -j LOG_SCAN # XMAS scan
$IPT -A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j LOG_SCAN
$IPT -A INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j LOG_SCAN
$IPT -A INPUT -p tcp --tcp-flags FIN,RST FIN,RST -j LOG_SCAN
$IPT -A INPUT -p tcp --tcp-flags ACK,FIN FIN -j LOG_SCAN
$IPT -A INPUT -p tcp --tcp-flags ACK,PSH PSH -j LOG_SCAN
$IPT -A INPUT -p tcp --tcp-flags ACK,URG URG -j LOG_SCAN
# ─── SYN FLOOD ────────────────────────────────────────────────────────────
echo " [SYN FLOOD]"
$IPT -N syn-flood
$IPT -A INPUT -i "$ETH_IF" -p tcp --syn -j syn-flood
$IPT -A syn-flood -m limit --limit 25/s --limit-burst 50 -j RETURN
$IPT -A syn-flood -j LOG_FLOOD
# ─── FRAGMENTS ────────────────────────────────────────────────────────────
echo " [FRAGMENTS]"
$IPT -A INPUT -i "$ETH_IF" -f -j LOG_DROP
# ─── PORTS BRUITÉS : drop silencieux (évite de saturer les logs) ──────────
echo " [DROP SILENCIEUX : NetBIOS / MSSQL / RPC / BitTorrent]"
for port in 135 137 138 139 445 1433 1025 1026 1027; do
$IPT -A INPUT -i "$ETH_IF" -p tcp --dport $port -j DROP
$IPT -A INPUT -i "$ETH_IF" -p udp --dport $port -j DROP
done
$IPT -A INPUT -i "$ETH_IF" -p udp --dport 1434 -j DROP
$IPT -A INPUT -i "$ETH_IF" -p tcp --dport 6881:6889 -j DROP
$IPT -A INPUT -i "$ETH_IF" -p udp --dport 6881:6889 -j DROP
# ─── RÈGLES SERVICES ──────────────────────────────────────────────────────
echo " [FILTRAGE NET VERS SERVEUR]"
# shellcheck disable=SC1090 # chemin résolu au déploiement (install.sh), pas statique
[ -f "$FILTERS" ] && source "$FILTERS"
# ─── SORTIE SERVEUR ───────────────────────────────────────────────────────
echo " [SORTIE SERVEUR VERS NET]"
$IPT -A OUTPUT -o "$ETH_IF" -m conntrack --ctstate NEW,ESTABLISHED,RELATED -j ACCEPT
# ─── BANS IP ──────────────────────────────────────────────────────────────
if [ -f "$BAN_FILE" ]; then
echo " [BANS IP]"
# shellcheck disable=SC1090 # chemin résolu au déploiement (install.sh), pas statique
. "$BAN_FILE"
fi
# ─── IPv6 ─────────────────────────────────────────────────────────────────
echo " [IPv6 : blocage total]"
$IP6T -F 2>/dev/null || true
$IP6T -X 2>/dev/null || true
$IP6T -P INPUT DROP 2>/dev/null || true
$IP6T -P OUTPUT DROP 2>/dev/null || true
$IP6T -P FORWARD DROP 2>/dev/null || true
$IP6T -A INPUT -i lo -j ACCEPT 2>/dev/null || true
$IP6T -A OUTPUT -o lo -j ACCEPT 2>/dev/null || true
# ─── DROP FINAL ───────────────────────────────────────────────────────────
echo " [DROP FINAL]"
$IPT -A FORWARD -j DROP
$IPT -A INPUT -j LOG_DROP
$IPT -A OUTPUT -j LOG_DROP
echo " [FIREWALL ACTIF]"
;;
status)
echo "=== IPv4 filter ==="
$IPT -vn -L
echo ""
echo "=== IPv4 nat ==="
$IPT -vn -L -t nat
echo ""
echo "=== IPv6 ==="
$IP6T -vn -L 2>/dev/null || echo "(ip6tables non disponible ou IPv6 désactivé)"
;;
stop)
echo " [ARRÊT FIREWALL]"
for t in filter nat mangle raw; do
$IPT -t $t -F 2>/dev/null
$IPT -t $t -X 2>/dev/null
for p in INPUT OUTPUT FORWARD PREROUTING POSTROUTING; do
$IPT -t $t -P $p ACCEPT 2>/dev/null || true
done
done
fw_sysctl_restore
echo " [FIREWALL ARRÊTÉ - SYSTÈME OUVERT]"
;;
reload)
echo " [RELOAD]"
$0 stop
$0 start
;;
*)
echo "Usage: $0 {start|stop|reload|status}"
exit 1
;;
esac
exit 0
+25
View File
@@ -0,0 +1,25 @@
[Unit]
Description=Firewall iptables
# Démarre après la détection des interfaces réseau,
# mais avant que le trafic soit autorisé et avant fail2ban
After=network-pre.target
Before=network.target fail2ban.service
Wants=network-pre.target
# Si fail2ban redémarre, le firewall doit être actif en premier
PartOf=fail2ban.service
[Service]
Type=oneshot
RemainAfterExit=yes
EnvironmentFile=__ENV_FILE__
ExecStart=/etc/firewall/firewall-launcher.sh start
ExecStop=/etc/firewall/firewall-launcher.sh stop
ExecReload=/etc/firewall/firewall-launcher.sh reload
# Sécurité du service lui-même
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target