Vous poussez un changement nftables, vous pouvez nft list ruleset et le voir, le trafic se comporte pendant cinq minutes… puis la réalité revient. Après un redémarrage c’est pire : votre « autoriser SSH » a disparu, Docker fait des pirouettes interprétatives avec votre NAT, et la direction demande pourquoi le « simple ajustement du pare-feu » s’est transformé en safari de parties prenantes.
Ce n’est généralement pas une « instabilité de nftables ». C’est presque toujours l’ordre de chargement, des gestionnaires en conflit, ou un jeu de règles techniquement chargé mais fonctionnellement contourné. Réglons-le de manière définitive.
Ce que signifie réellement « les règles ne fonctionnent pas »
Quand quelqu’un dit « nftables ne fonctionne pas », cela se résume généralement à l’un de ces modes d’échec réels :
- Jeu de règles pas du tout chargé après le démarrage ou après un redémarrage de service. Vous regardez un jeu de règles vide ou un placeholder par défaut.
- Jeu de règles chargé, mais écrasé plus tard par un autre acteur (Docker, firewalld, un agent cloud, une unité iptables-restore, votre job CI).
- Jeu de règles chargé, mais non appliqué parce qu’il est dans le mauvais hook, la mauvaise famille (ip vs inet), le mauvais type de chaîne ou la mauvaise priorité.
- Les règles correspondent, mais votre attente est erronée (état conntrack, chemin NAT, politique de routage, bridge vs chemin routé, trafic local vs forwardé).
- Le chemin noyau vous contourne à cause d’offloads, programmes XDP, hypothèses VRF/policy routing, ou d’un netns différent de celui que vous pensez filtrer.
L’astuce consiste à arrêter de se battre avec votre modèle mental et à interroger le système en fonctionnement. Debian 13 est une plateforme propre pour nftables—jusqu’à ce que vous exécutiez accidentellement deux orchestrateurs de pare-feu en même temps, ou que vous comptiez sur « ça a été chargé une fois dans une session shell donc ça persiste ».
Blague n°1 : Les pare-feux sont comme les réunions—si vous ne contrôlez pas qui est invité, quelqu’un réécrira l’ordre du jour juste après votre départ.
Faits et contexte à connaître (pour arrêter de deviner)
- nftables a remplacé iptables comme frontal netfilter à long terme sur Linux il y a des années ; iptables peut être un wrapper de compatibilité sur nft (iptables-nft) ou utiliser l’ancien backend (iptables-legacy).
- Debian a historiquement supporté les deux backends car les flottes de production ne migrent pas en un week-end. Cela signifie que vous pouvez par erreur avoir une vue iptables « fonctionnelle » qui ne reflète pas ce que nft voit.
- La famille
inetest un cadeau pratique : un seul jeu de règles peut couvrir IPv4 et IPv6. Le piège est que certains exemples anciens utilisent encoreipetip6séparément, et les mélanger sans intention peut créer des lacunes. - Les priorités de chaîne comptent car plusieurs chaînes de base peuvent s’attacher au même hook (input/forward/output/prerouting/postrouting) et s’exécuter par ordre de priorité. « Ma règle existe » n’implique pas « ma règle s’exécute en premier ».
- Docker programme historiquement des règles de pare-feu/NAT automatiquement et peut le faire via les interfaces iptables. Avec iptables-nft, cela finit toujours dans nftables—mais pas forcément à l’endroit où vous l’attendez.
- firewalld n’est pas « juste une interface » ; c’est un gestionnaire d’état qui réaffirmera ses règles désirées. Si vous éditez manuellement les règles nft sur un hôte géré par firewalld, il finira par être en désaccord avec vous et gagnera.
- Les jeux de règles nftables sont atomiques lorsqu’ils sont chargés depuis un fichier : les erreurs de parse entraînent généralement « rien changé », ce qui est bien—sauf si vous regardez le mauvais fichier et pensez qu’il a été chargé.
- Netfilter est par espace de noms réseau. Les conteneurs et certains gestionnaires de services peuvent s’exécuter dans leur propre netns. Vous pourriez charger des règles dans le namespace root alors que le trafic qui vous intéresse vit ailleurs.
- Le démarrage systemd de Debian est parallèle. L’ordre est explicite, pas magique. Si votre pare-feu dépend d’interfaces, de modules ou de sysctls, vous devez encoder cet ordre ou vous obtiendrez un « ça marche sur mon redémarrage ».
Mode opératoire de diagnostic rapide
Ceci est la séquence « arrêter l’hémorragie » que j’utilise quand quelqu’un signale « le changement de pare-feu n’a pas été appliqué » et que l’horloge est bruyante.
Première étape : prouvez quelles règles sont réellement actives
- Exécutez
nft list rulesetet sauvegardez-le. S’il est vide ou manque vos tables, vous déboguez la mauvaise chose. - Vérifiez si vous regardez le namespace root. Si vous avez des conteneurs ou des espaces de noms réseau, vérifiez où le trafic se trouve.
- Confirmez quel cadre est en charge : service nftables, firewalld, Docker, une unité iptables-restore, ou un agent de gestion de configuration.
Deuxième étape : trouvez qui a écrit les règles en dernier
- Utilisez les journaux systemd : recherchez les rechargements nftables, les démarrages firewalld, les redémarrages docker et les services iptables-restore.
- Vérifiez les horodatages des fichiers de jeu de règles. S’ils sont corrects mais que les règles en live diffèrent, quelqu’un les a écrasées après le chargement.
Troisième étape : validez le chemin du paquet et le hook/la priorité
- Est-ce
inputouforward? Le trafic local vs routé embrouille tout le monde tôt ou tard. - Le NAT se produit-il plus tôt que vous ne le pensez ? Correspondiez-vous aux adresses originales ou traduites ?
- Y a-t-il plusieurs chaînes de base sur le même hook ? Inspectez les priorités et politiques.
Quatrième étape : décidez d’une source unique de vérité
- Soit : nftables pur avec un seul fichier de jeu de règles et une unité systemd.
- Soit : firewalld comme gestionnaire.
- Soit : un orchestrateur de plus haut niveau (CNI Kubernetes, etc.).
Mélanger les gestionnaires est la façon la plus sûre d’obtenir « les règles ne fonctionnent pas », sauf que les règles fonctionnent—juste pas les vôtres.
Ordre de chargement : systemd, réseau, et pourquoi le timing compte
Sur Debian 13, le cas le plus fréquent de « ça a marché manuellement mais pas au démarrage » est une simple course : le service nftables se charge avant l’environnement qu’il suppose exister.
Que signifie « environnement » ici ?
- Interfaces : si vos règles font référence à
iifname "ens192"mais que l’interface est renommée par udev plus tard, vous pouvez avoir un décalage au démarrage. - Modules noyau : certains comportements de match/conntrack/NAT dépendent de modules disponibles. Habituellement auto-chargés, parfois non, surtout pour des protocoles inhabituels.
- Sysctls : forwarding, rp_filter, bridge-nf-call-iptables, etc. Ceux-ci peuvent changer le trafic qui atteint même les hooks netfilter.
- Gestionnaires réseau : ifupdown, systemd-networkd, NetworkManager—chacun a son propre timing et ses points d’accroche.
L’ordre systemd est explicite. Si vous ne le spécifiez pas, vous obtenez le « meilleur effort ». En production, le meilleur effort est la cause des cheveux blancs.
Conseils d’opinion
- Gardez nftables aussi tôt que possible pour des politiques default-drop input, mais n’écrivez pas de règles spécifiques aux interfaces qui dépendent de renommages tardifs.
- Si vous devez dépendre des interfaces, ordonnez nftables après que le réseau soit up (ou au moins après que udev soit stabilisé) et admettez que le trafic du démarrage précoce est moins contrôlé.
- Privilégiez les correspondances par adresses/sous-réseaux plutôt que par noms d’interface quand c’est possible, surtout sur des hôtes à adressage prévisible.
Conflits : iptables, firewalld, Docker et compagnie
Les conflits ne sont pas philosophiques. Ce sont littéralement plusieurs processus qui écrivent dans le même jeu de règles netfilter, parfois via des APIs différentes, avec des hypothèses et des comportements de reload différents.
iptables vs nftables : le miroir à deux faces
Sur Debian, iptables peut fonctionner en deux modes :
- iptables-nft : les commandes iptables programment des règles nftables en coulisse.
- iptables-legacy : les commandes iptables programment l’ancien backend xtables, séparé de nftables.
Si vous ne savez pas lequel est actif, vous pouvez facilement « corriger » le pare-feu avec le mauvais outil puis vous demander pourquoi nftables n’a pas changé. Ou l’inverse : nftables paraît correct, mais c’est iptables-legacy qui filtre réellement.
firewalld : le dictateur poli
Le rôle de firewalld est de maintenir un état désiré. Si vous éditez directement les règles nft sur un système géré par firewalld, vous écrivez du graffiti sur un tableau blanc qui sera effacé au prochain rafraîchissement.
Docker : utile jusqu’à ce que ça ne le soit plus
Docker assure généralement la connectivité des conteneurs en injectant des règles NAT et filter. Selon les paramètres et versions, il peut :
- créer ses propres chaînes et y sauter
- changer les défauts de forwarding
- réappliquer des règles au redémarrage du démon (qui peut arriver pendant des mises à jour, rotations de logs ou sous pression de ressources)
Si vous exécutez des politiques nftables strictes sur un hôte Docker, vous devez planifier la programmation des règles par Docker. L’approche correcte est généralement de vous intégrer à lui (autoriser ses chaînes mais les contraindre), pas de prétendre qu’il n’existe pas.
Blague n°2 : Docker et les pare-feux ont un statut relationnel : « C’est compliqué », et votre fenêtre de changement est le thérapeute du couple.
Hooking, priorité de chaînes et l’illusion de « ma règle est là »
nftables n’est pas « une grande liste unique ». Ce sont des tables, des chaînes, des hooks et des priorités. Si vous venez de la mémoire musculaire d’iptables, la structure peut ressembler à quelqu’un qui a réorganisé votre boîte à outils en tiroirs étiquetés. C’est bien—jusqu’à ce que vous mettiez la clé dans le tiroir « cuillères » et vous demandiez pourquoi rien ne serre.
Trois pièges qui ressemblent à des règles cassées
- Mauvais hook : vous bloquez dans
inputmais le trafic est forwardé à travers l’hôte, donc il toucheforwardà la place. - Mauvaise famille : vous avez écrit
table ip filtermais le trafic est IPv6, donc il reste intact.table inet filterest souvent le défaut pragmatique. - Inversion de priorité : une chaîne de base avec une priorité « meilleure » (numériquement plus basse) s’exécute avant la vôtre et ACCEPT le trafic, donc votre DROP plus tard ne se déclenche jamais.
Priorité de chaîne en pratique
Les priorités sont des entiers. Les plus bas s’exécutent plus tôt. Le NAT a des priorités conventionnelles (par exemple dstnat en prerouting, srcnat en postrouting), mais vous pouvez toujours créer des chaînes de base concurrentes. Si un agent vendeur installe une chaîne de base au même hook avec une priorité plus tôt, votre politique soignée peut devenir décorative.
Quand vous diagnosez, ne lisez pas seulement votre propre fichier de config. Inspectez le jeu de règles en live et identifiez toutes les chaînes de base attachées à chaque hook. Ensuite raisonnez sur l’ordre.
Une citation à garder sur votre écran
paraphrased idea
de John Allspaw : la fiabilité est une propriété du système, pas des composants individuels se comportant comme prévu.
Tâches pratiques : commandes, sorties, décisions (la checklist production)
Vous vouliez des commandes. Voici des commandes. Chaque tâche inclut ce que signifie la sortie et quelle décision prendre ensuite.
Tâche 1 : Confirmer l’état du service nftables et la dernière action
cr0x@server:~$ systemctl status nftables --no-pager
● nftables.service - nftables
Loaded: loaded (/lib/systemd/system/nftables.service; enabled; preset: enabled)
Active: active (exited) since Sun 2025-12-28 09:11:08 UTC; 2h 3min ago
Docs: man:nft(8)
Process: 412 ExecStart=/usr/sbin/nft -f /etc/nftables.conf (code=exited, status=0/SUCCESS)
Main PID: 412 (code=exited, status=0/SUCCESS)
Signification : Le service a tourné et est sorti avec succès (normal pour oneshot). S’il est inactive/failed, vos règles peuvent ne jamais se charger.
Décision : Si failed, inspectez les logs et lancez un chargement manuel en mode vérification (Tâche 4). Si actif, poursuivez : quelque chose d’autre peut écraser les règles.
Tâche 2 : Inspecter le journal pour rechargements et services concurrents
cr0x@server:~$ journalctl -u nftables -u firewalld -u docker --since "today" --no-pager
Dec 28 09:11:08 server systemd[1]: Starting nftables...
Dec 28 09:11:08 server nft[412]: /etc/nftables.conf:52:16-16: Error: Could not process rule: No such file or directory
Dec 28 09:11:08 server systemd[1]: nftables.service: Main process exited, code=exited, status=1/FAILURE
Dec 28 09:11:08 server systemd[1]: Failed to start nftables.
Dec 28 09:19:43 server systemd[1]: Starting Docker Application Container Engine...
Dec 28 09:19:44 server dockerd[733]: time="2025-12-28T09:19:44.101" level=info msg="Loading containers: done."
Signification : Ici nftables a réellement échoué à cause d’une erreur de configuration (un include manquant est courant). Docker a démarré plus tard et programmera des règles quoi qu’il en soit.
Décision : Corrigez l’erreur de parse nftables avant de chasser les « conflits ». Si nft échoue au démarrage, vous vous battez avec une politique non chargée.
Tâche 3 : Vérifier quel backend iptables est sélectionné
cr0x@server:~$ update-alternatives --display iptables
iptables - auto mode
link best version is /usr/sbin/iptables-nft
link currently points to /usr/sbin/iptables-nft
link iptables is /usr/sbin/iptables
slave iptables-restore is /usr/sbin/iptables-restore
slave iptables-save is /usr/sbin/iptables-save
/usr/sbin/iptables-legacy - priority 10
/usr/sbin/iptables-nft - priority 20
Signification : Les commandes iptables manipuleront nftables. C’est bien pour un jeu de règles unifié, mais cela signifie aussi que Docker/autres utilisateurs d’iptables écrivent dans nft.
Décision : Si vous voyez legacy sélectionné sur un hôte que vous gérez avec nftables, arrêtez-vous et alignez les outils. Backends mixtes = théâtre de débogage.
Tâche 4 : Valider votre config sans l’appliquer
cr0x@server:~$ nft -c -f /etc/nftables.conf
/etc/nftables.conf:12:1-14: Warning: table ip filter is managed by iptables-nft, do not touch!
Signification : La config parse, mais vous écrivez dans une table qu’un autre outil (iptables-nft) gère aussi. Le texte d’avertissement varie, mais la situation est réelle.
Décision : Ne vous battez pas sur les mêmes noms de table. Utilisez vos propres tables/chaînes et intégrez-vous avec des jumps si nécessaire, ou arrêtez d’utiliser les outils iptables sur cet hôte.
Tâche 5 : Dumper le jeu de règles live et chercher vos empreintes
cr0x@server:~$ nft list ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
iifname "lo" accept
tcp dport 22 accept
counter drop
}
}
table ip nat {
chain PREROUTING {
type nat hook prerouting priority -100; policy accept;
}
chain POSTROUTING {
type nat hook postrouting priority 100; policy accept;
}
}
Signification : Votre table inet filter existe, la politique input est drop, SSH autorisé. La table NAT existe aussi.
Décision : Si vos règles sont absentes, vous ne chargez pas le bon fichier ou vous êtes écrasé. Si elles sont présentes mais que le trafic circule incorrectement, passez à la validation du hook/chemin et au traçage.
Tâche 6 : Identifier toutes les chaînes de base et priorités à chaque hook
cr0x@server:~$ nft -a list chains
table inet filter {
chain input { type filter hook input priority 0; policy drop; }
chain forward { type filter hook forward priority 0; policy drop; }
}
table inet docker {
chain input { type filter hook input priority -10; policy accept; }
}
Signification : Il y a deux chaînes de base sur hook input. La chaîne de Docker s’exécute plus tôt (priority -10) et accepte par défaut. Votre politique drop s’exécute plus tard et peut ne jamais voir les paquets.
Décision : Soit changez les priorités, supprimez la chaîne de base concurrente, soit intégrez-vous en faisant sauter Docker vers votre chaîne (ou l’inverse). Laisser deux chaînes de base avec des politiques conflictuelles, c’est s’exposer à des surprises.
Tâche 7 : Confirmer si firewalld gère nftables
cr0x@server:~$ systemctl is-enabled firewalld
enabled
Signification : firewalld affirmera probablement son propre état au démarrage et lors des reloads, pouvant écraser des parties du jeu de règles.
Décision : Choisissez : firewalld-managed ou nftables-managed. Si firewalld reste, cessez d’éditer nft directement et utilisez les primitives firewalld. Si nftables reste, désactivez firewalld.
Tâche 8 : Vérifier si Docker programme des règles iptables
cr0x@server:~$ grep -nE 'iptables|ip6tables' /etc/docker/daemon.json
12: "iptables": true,
13: "ip6tables": true,
Signification : Docker programmera des règles (via l’interface iptables), qui atterrissent dans nft si iptables-nft est sélectionné.
Décision : Si vous avez besoin d’un contrôle strict, envisagez de définir "iptables": false uniquement si vous êtes prêt à gérer vous-même la connectivité des conteneurs. La plupart des équipes ne le sont pas, et elles le découvrent à 2 h du matin.
Tâche 9 : Confirmer les sysctls de forwarding et bridge netfilter
cr0x@server:~$ sysctl net.ipv4.ip_forward net.ipv6.conf.all.forwarding net.bridge.bridge-nf-call-iptables
net.ipv4.ip_forward = 0
net.ipv6.conf.all.forwarding = 0
net.bridge.bridge-nf-call-iptables = 0
Signification : Le forwarding est désactivé ; le trafic ponté peut contourner les hooks de type iptables. Si vous attendiez que l’hôte route ou filtre le trafic ponté des conteneurs, votre attente est erronée.
Décision : Si cet hôte est un routeur ou effectue du forwarding de conteneurs, activez les sysctls appropriés et encoderez-les de manière persistante. S’il ne doit pas forwarder, laissez-les désactivés et ajustez votre conception.
Tâche 10 : Confirmer que vous filtrez le bon namespace
cr0x@server:~$ ip netns list
cni-3f2a9c7b-1
Signification : Il existe au moins un netns non-root. Le trafic peut y être traité, avec ses propres règles.
Décision : Si le trafic problématique est à l’intérieur de ce namespace, inspectez-le avec ip netns exec et appliquez les règles là-bas (ou ajustez le chemin politique de l’hôte).
Tâche 11 : Tracer l’évaluation des paquets pour voir quelle règle correspond
cr0x@server:~$ nft monitor trace
trace id 9b7e7f05 inet filter input packet: iif "ens192" ip saddr 203.0.113.50 ip daddr 192.0.2.10 tcp sport 51544 tcp dport 22
trace id 9b7e7f05 rule inet filter input handle 7: tcp dport 22 accept
Signification : Le paquet a touché votre chaîne input et a matché la règle d’accept SSH (handle 7). Le trace enlève les conjectures.
Décision : Si le paquet matche une règle accept/drop inattendue, corrigez l’ordre et les prédicats. S’il n’apparaît jamais, vous tracez le mauvais hook/chemin ou le trafic contourne l’hôte.
Tâche 12 : Vérifier les réécritures silencieuses par des jobs périodiques ou la gestion de config
cr0x@server:~$ systemctl list-timers --all --no-pager | grep -E 'nft|iptables|firewall'
Sun 2025-12-28 10:00:00 UTC 1h ago Sun 2025-12-28 11:00:00 UTC 1min ago firewall-sync.timer firewall-sync.service
Signification : Un timer existe et réapplique probablement l’état du pare-feu. Votre changement manuel sera reverti selon l’horaire.
Décision : Soit intégrez votre changement dans ce pipeline géré, soit désactivez le job concurrent. « Je vais juste l’éditer en live » n’est pas une stratégie, c’est une farce pour le Vous du futur.
Tâche 13 : Confirmer que vos fichiers d’inclusion existent et l’ordre de chargement dans la config
cr0x@server:~$ sed -n '1,120p' /etc/nftables.conf
#!/usr/sbin/nft -f
flush ruleset
include "/etc/nftables.d/base.nft"
include "/etc/nftables.d/nat.nft"
include "/etc/nftables.d/docker-overrides.nft"
Signification : Vous avez un jeu de règles modulaire avec un flush en haut. C’est sain tant que rien d’autre n’attend à garder ses propres règles.
Décision : Si Docker/firewalld doit coexister, ne faites pas flush ruleset inconditionnellement. Au lieu de cela, videz seulement vos tables, ou définissez un contrat d’intégration clair.
Tâche 14 : Vérifier qu’un reload n’interrompt pas les sessions SSH existantes
cr0x@server:~$ sudo nft -f /etc/nftables.conf && echo "reload ok"
reload ok
Signification : Le jeu de règles a chargé avec succès. Si votre SSH est resté, votre règle established/related fait son travail.
Décision : Si le reload coupe des sessions, votre règle stateful accept est manquante ou vous avez vidé des attentes conntrack. Corrigez avant de refaire cela à distance.
Trois mini-récits d’entreprise (et quoi en reprendre)
Mini-récit n°1 : L’incident causé par une mauvaise hypothèse
Une équipe a hérité d’un hôte Debian qui servait de jump box et de routeur léger pour un segment de labo. Ils voulaient « durcir l’ingress » et ont écrit une chaîne input nftables avec un drop par défaut et une allowlist soignée. Parfait en revue. Les tests depuis leur poste ont réussi.
Deux heures plus tard, un autre groupe a signalé que le segment de labo ne pouvait plus atteindre le cache d’artefacts. Le cache se trouvait de l’autre côté du jump box. Personne n’a lié cela au changement de pare-feu parce que « nous n’avons touché que input ».
La réalité : presque tout le trafic pertinent était forwardé à travers la machine, ne touchant jamais input. La chaîne forward par défaut était toujours ce que Docker et le bagage historique iptables avaient laissé. En d’autres termes, ils avaient sécurisé la mauvaise porte.
La correction n’a pas été héroïque. Ils ont ajouté une chaîne de base forward explicite dans table inet filter avec des autorisations entre segments et un drop par défaut, puis validé avec nft monitor trace depuis les deux côtés. Ils ont aussi consigné les hypothèses sur le chemin des paquets dans la demande de changement, ce qui semble ennuyeux jusqu’à ce que cela empêche la prochaine personne de refaire la même erreur.
Mini-récit n°2 : L’optimisation qui a mal tourné
Une équipe plateforme a décidé de « standardiser et accélérer le boot » en faisant charger nftables très tôt, avant l’initialisation réseau, sur une flotte. Leur raisonnement était raisonnable : pare-feu plus tôt, fenêtre de risque réduite. Ils ont donc fixé un ordre agressif et retiré les dépendances réseau de l’unité.
Ça a marché en staging, pour la plupart. En production, un sous-ensemble de machines avait des renommages d’interfaces prévisibles qui se terminaient plus tard au démarrage. Les règles référençaient iifname pour une interface de gestion et une interface de stockage. Le chargement précoce signifiait que ces noms n’étaient pas stables encore. Les règles compilaient, mais elles ne matchaient rien. Les hôtes ont démarré dans une réalité permissive qu’ils n’avaient pas l’intention d’avoir.
Ils ne l’ont détecté que parce qu’un ingénieur a remarqué que nft list ruleset montrait les règles attendues, pourtant le trafic n’était clairement pas filtré. Le tracing a révélé des paquets frappant l’accept par défaut dans une autre chaîne qui était censée être inaccessible.
Leçon : « charger tôt » n’est pas automatiquement « sécurisé ». C’est sécurisé seulement si ce sur quoi vous matchiez est stable tôt. Ils ont fini par déplacer les règles dépendantes d’interfaces dans des sets indexés par sous-réseaux et utiliser inet, et ils ont ordonné nftables après udev settle pour cette classe de matériel. Un peu plus tard, en fait correct.
Mini-récit n°3 : La pratique ennuyeuse mais correcte qui a sauvé la mise
Dans une autre entreprise, une équipe SRE avait une habitude ennuyeuse : chaque changement de pare-feu incluait un snapshot avant/après de nft list ruleset, plus un script de test de connectivité simple lancé depuis deux points de vue. Ils stockaient ces artefacts avec le dossier de déploiement. Personne n’en parlait aux fêtes.
Pendant une mise à jour OS routinière sur des hôtes Debian, un nouveau paquet a tiré firewalld comme dépendance pour un outil « de commodité ». Ce n’était pas malveillant ; c’était juste un défaut. firewalld s’est activé au démarrage pour une petite partie de la flotte à cause d’un comportement post-install et d’un défaut d’automatisation.
En moins d’une heure, quelqu’un a remarqué que le jeu de règles live ne correspondait pas au snapshot attendu après le reboot. Pas parce que le trafic avait déjà été cassé—parce qu’ils avaient l’habitude de comparer l’état après maintenance. Ils ont désactivé firewalld sur ces nœuds, réaffirmé nftables, et ajouté un pin de paquet explicite plus un contrôle CI qui échoue si firewalld est activé sur ce rôle.
Leçon sauveteuse : l’exploitation fiable repose surtout sur des habitudes et des garde-fous. Le débogage sophistiqué sert quand les garde-fous échouent.
Erreurs courantes : symptômes → cause racine → correctif
1) « Les règles sont présentes, mais les paquets les ignorent »
Symptôme : nft list ruleset montre vos règles de drop, mais les connexions réussissent toujours.
Cause racine : Votre règle est dans le mauvais hook (input vs forward) ou la mauvaise famille (ip vs inet), ou une chaîne de base différente s’exécute plus tôt et accepte.
Correctif : Utilisez nft -a list chains pour énumérer les chaînes de base et priorités ; utilisez nft monitor trace pour vérifier l’évaluation ; ajustez hook/priority et supprimez les chaînes de base concurrentes.
2) « Ça marche jusqu’au redémarrage, puis disparaît »
Symptôme : Après redémarrage, le jeu de règles est vide ou par défaut.
Cause racine : service nftables désactivé, fichier de config non référencé, erreur de parse au démarrage, ou un autre gestionnaire a écrasé l’état après le chargement de nftables.
Correctif : Assurez-vous de systemctl enable nftables ; validez avec nft -c -f ; vérifiez le journal pour les échecs ; auditez firewalld/Docker/unités iptables-restore et timers.
3) « Mon fichier d’inclusion se charge manuellement mais échoue dans le service »
Symptôme : nft -f manuel fonctionne ; chargement au boot échoue.
Cause racine : chemins d’inclusion relatifs, fichier manquant au démarrage, répertoire de travail différent, ou problème d’ordre où le fichier est généré plus tard.
Correctif : Utilisez des chemins absolus dans include ; assurez-vous que l’unité génératrice s’exécute avant nftables ; ajoutez Requires= et After= si nécessaire.
4) « Le réseau Docker casse quand j’active default drop »
Symptôme : Les conteneurs perdent l’accès sortant ou les ports publiés échouent.
Cause racine : Docker attend certains comportements forward/NAT ; vos règles dropent le trafic forwardé ou bloquent les flux inter-bridge ; les chaînes Docker peuvent s’exécuter avant les vôtres.
Correctif : Autorisez explicitement established/related en forward ; autorisez les interfaces bridge nécessaires et les chaînes Docker ; évitez de vider tout le jeu de règles si Docker doit gérer ses propres pièces.
5) « iptables montre une chose, nft en montre une autre »
Symptôme : iptables -S ne correspond pas à nft list ruleset.
Cause racine : backend iptables-legacy actif, ou vous mélangez des outils à travers des namespaces.
Correctif : Alignez les alternatives sur iptables-nft si vous voulez un état unifié ; cessez d’utiliser les outils legacy ; vérifiez le contexte des namespaces.
6) « Un agent vendeur continue de rétablir mes règles »
Symptôme : Vos règles s’appliquent, puis redeviennent d’origine à des intervalles étranges.
Cause racine : application périodique (timer systemd, gestion de config, agent sécurité cloud).
Correctif : Localisez le timer/service ; modifiez la configuration source-de-vérité ; ajoutez une surveillance sur la dérive des règles (hasher la sortie de nft list ruleset).
Checklists / plan pas à pas
Pas à pas : faire de nftables la source unique de vérité (recommandé pour les serveurs)
- Choisissez le gestionnaire. Si vous voulez nftables pur, désactivez firewalld et cessez d’utiliser les scripts iptables comme configuration canonique.
- Alignez le backend iptables. Utilisez iptables-nft pour que Docker et tout consommateur iptables atterrisse dans le monde nft, sauf raison forte contraire.
- Concevez la structure du jeu de règles. Préférez
table inet filterpour input/forward/output. Gardez le NAT danstable ip nat(etip6 natseulement si vous faites réellement du NAT IPv6). - Décidez si vous pouvez vider en toute sécurité. Si rien d’autre ne doit gérer les règles,
flush rulesetest propre. Si Docker doit gérer, videz uniquement vos tables. - Encodez l’ordre. Si vous dépendez de sysctls ou de fichiers générés, systemd doit le savoir.
- Vérifiez avec trace et compteurs. Ajoutez des compteurs aux règles clés et observez les incréments pendant les tests.
- Rendez le persistant. Activez l’unité nftables et conservez la config dans un chemin de fichier géré.
- Ajoutez la détection de dérive. Alertez si le hash du jeu de règles live change en dehors des fenêtres de changement.
Checklist : procédure de changement distante sûre
- Confirmez que vous avez un accès console ou hors bande (BMC, console hyperviseur).
- Assurez-vous que
ct state established,related acceptexiste avant de passer input en default-drop. - Utilisez
nft -c -favantnft -f. - Appliquez les changements pendant une session avec une seconde connexion comme canari.
- Lancez immédiatement
nft monitor tracependant le test d’un flux représentatif. - Enregistrez
nft list rulesetavant/après pour un débogage ultérieur sans recherche de coupable.
Checklist : règles de coexistence (quand vous ne pouvez pas éviter Docker/firewalld)
- Si firewalld est activé, traitez-le comme la source de vérité et configurez-le directement.
- Si Docker est activé et que vous maintenez
"iptables": true, ne videz pas l’ensemble du jeu de règles lors d’un reload. - Inspectez explicitement les priorités de chaînes pour assurer que votre politique n’est pas contournée par des chaînes de base antérieures.
- Privilégiez l’intégration avec les chaînes Docker via des jumps plutôt que de les réécrire.
FAQ
Pourquoi mes règles fonctionnent quand j’exécute nft -f manuellement mais pas après un redémarrage ?
Parce que le démarrage est une course. Votre service peut échouer à cause d’un chemin d’inclusion, se charger avant que les fichiers existent, ou être écrasé après chargement par Docker/firewalld/timers. Vérifiez d’abord systemctl status nftables et le journal.
Dois-je utiliser table inet ou des table ip/table ip6 séparées ?
Utilisez inet pour les règles de filtrage sauf raison spécifique. Cela réduit la dérive entre v4/v6 et évite les incidents « IPv6 accidentellement ouvert ».
Est-il sûr de mettre flush ruleset en tête de /etc/nftables.conf ?
Sûr seulement si nftables est le seul gestionnaire. Si Docker ou firewalld programment des règles, tout vider supprimera leurs chaînes et vous passerez l’après-midi à expliquer pourquoi les conteneurs ne trouvent pas DNS.
Comment savoir si Docker change mon pare-feu ?
Cherchez des tables/chaînes créées par Docker dans nft list ruleset et corrélez avec journalctl -u docker. Vérifiez aussi /etc/docker/daemon.json pour "iptables": true.
Puis-je faire coexister firewalld et un jeu de règles nft écrit à la main ?
Vous pouvez, mais vous ne devriez pas. firewalld réaffirmera l’état ; vos changements manuels deviennent non déterministes. Choisissez un gestionnaire par rôle d’hôte.
Pourquoi iptables -S ne correspond pas à nft list ruleset ?
Vous utilisez peut-être iptables-legacy. Vérifiez avec update-alternatives --display iptables. Vérifiez aussi les namespaces réseau—iptables dans un namespace différent montrera un état différent.
Mon SSH a été coupé pendant un reload. Qu’ai-je mal fait ?
Vous avez probablement mis input en default-drop sans autoriser ct state established,related suffisamment tôt, ou vous avez remplacé le jeu de règles sans préserver l’acceptation stateful. Corrigez l’ordre : established/related d’abord, puis loopback, puis les services autorisés, puis drop.
Comment prouver quelle règle drop un paquet ?
Utilisez nft monitor trace pendant un flux de test. Il montre la chaîne et le handle de règle qui a matché. Les compteurs aident aussi, mais trace est le plus clair.
Quelle est la manière la plus propre de prévenir la dérive des règles ?
Faites d’un système la source de vérité (fichier nftables, firewalld, ou gestion de configuration), puis ajoutez une surveillance qui hashe nft list ruleset et alerte sur les changements inattendus.
Conclusion : prochaines étapes pour éviter la réapparition
Si nftables « ne fonctionne pas » sur Debian 13, ce n’est presque jamais un mystère du noyau. C’est de l’ambiguïté côté plan de contrôle : ordre de chargement, gestionnaires multiples, ou chaînes qui s’exécutent dans un ordre que vous n’aviez pas prévu.
Faites ceci ensuite :
- Exécutez le mode opératoire de diagnostic rapide et capturez le jeu de règles live, les priorités de chaînes et la timeline du journal.
- Choisissez un seul gestionnaire de pare-feu par rôle d’hôte. Désactivez les autres ou intégrez-les explicitement.
- Éliminez les courses accidentelles : validez la config, utilisez des includes absolus, et encodez l’ordre systemd quand il y a des dépendances.
- Utilisez trace une fois par incident. C’est le moyen le plus rapide d’arrêter de débattre des théories.
- Ajoutez la détection de dérive pour découvrir les réécritures avant les utilisateurs.
L’état final que vous voulez est ennuyeux : comportement de démarrage déterministe, une source de vérité, et des changements de pare-feu qui se comportent de la même façon le mardi que pendant vos tests du vendredi.