Voici un audit approfondi du ruleset nftables que tu m’as envoyé, point par point avec une approche critique et rigoureuse.
🧨 1. Politiques DROP + chaînes vides = inefficaces ou incohérentes
❌ default_fwd est vide :
chain FORWARD {
policy drop;
jump global
jump default_fwd
...
reject
}
-
Effet : tout trafic de forward est bloqué par
reject. -
Attendu : des règles explicites pour permettre le passage de certains flux (ex: Docker vers Internet).
🔎 Même situation dans OUTPUT et INPUT : la logique dépend fortement de default_in et default_out.
🐳 2. Gestion Docker incomplète et parfois contradictoire
✅ Présence de chaînes Docker (DOCKER, DOCKER-FORWARD, etc.) :
Mais attention :
-
Ces chaînes sont dans une table séparée (
ip filter), ce qui ne les rend pas visibles dansinet filter, qui a la main sur les hooksFORWARD,INPUT, etc. -
Résultat : elles peuvent être déclarées, mais jamais appelées.
👎 Exemple :
table ip filter {
chain FORWARD {
hook forward ...
}
}
Mais table inet filter a aussi un hook forward. Seul un seul hook est actif par priorité ➤ le inet prend le dessus ici. Donc la table ip filter est non fonctionnelle (fantôme).
🌍 3. Sortie Internet des conteneurs Docker potentiellement bloquée
Même si la table ip nat a ceci :
oifname != "br-8203e51da77b" ip saddr 172.18.0.0/16 masquerade
→ C’est bon en théorie, mais le FORWARD est en DROP, et la chaîne default_fwd est vide ⇒ aucune règle n’autorise la sortie.
Il manque :
iifname "br-8203e51da77b" oifname != "br-8203e51da77b" accept
🌐 4. default_out = accept total, sans journalisation ni filtrage
chain default_out {
accept
}
→ Cela laisse passer tout, ce qui est OK si c’est voulu, mais :
-
Aucune granularité
-
Pas de log
-
Pas de contrôle par port
➡️ Un serveur de prod mérite mieux qu’un open bar anonyme.
🧱 5. Répétition de chaînes INPUT : conflit de priorité
Tu as deux chaînes INPUT :
-
Dans
table inet filter(priority filter) -
Dans
table inet f2b-table(priority filter -1)
⚠️ Ça fonctionne, mais la logique est déroutante :
-
On ne voit pas facilement qui s’exécute avant quoi.
-
Cela fragilise le modèle mental du pare-feu.
➡️ Une seule chaîne INPUT, bien structurée, devrait suffire.
🔥 6. Pas de RETURN dans les jump
Partout dans les chaînes FORWARD, INPUT, etc. :
jump global
jump default_fwd
sans return derrière, donc les règles suivantes continuent à s’exécuter, même si le jump match.
→ Risque : règles en double, rejets fantômes, logs imprévisibles.
🐞 7. Chaîne global présente mais peu utile sans return
chain global {
ip saddr @blackhole_v4 drop
ip daddr @blackhole_v4 drop
...
}
Bien vu 👌, mais son effet est passif :
-
Pas de
log -
Pas de
comment -
Pas de différenciation
INPUT/OUTPUT/FORWARD
❗ 8. Table ip6 nat presque vide, mais avec un jump DOCKER
chain PREROUTING {
fib daddr type local counter packets 0 bytes 0 jump DOCKER
}
Mais la chaîne DOCKER en ip6 nat est vide.
➡️ C’est inoffensif, mais ça alourdit le ruleset inutilement.
🧼 9. Redondance + complexité inutile = dette technique
Tu as :
-
table inet filter -
table ip filter(pour Docker) -
table inet f2b-table -
table ip nat -
table ip6 nat -
table ip6 filter -
table ip raw
Sans hiérarchie claire, ni commentaire, ni documentation. Résultat :
-
Impossible à maintenir pour un collègue
-
Effets de bord quasi garantis
✅ Recommandations concrètes
| Objectif | Action recommandée |
|---|---|
| 🔥 Clarifier la structure | Centraliser les règles dans table inet filter |
| 🐳 Docker | Ajouter iifname/oifname explicites en FORWARD |
| 🔁 NAT | Garder seulement table ip nat, supprimer ip6 nat si inutilisé |
| 🔒 Fail2Ban | Intégrer directement ses règles dans INPUT principale |
| 🧹 Nettoyage | Supprimer ip6 filter, raw, etc. sauf besoin réel |
| 📜 Transparence | Ajouter log, counter et comment partout |
| 🧪 Débogage | Ajouter log prefix AVANT tout reject |