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 |