Partager :
Surface d'attaque · iptables
iptables : la règle DROP est listée, mais le trafic passe (Debian 13, iptables-nft)
Publié le · HardStacked · 12 min
Tu as posé une règle iptables -j DROP. iptables -L l'affiche, en tête de chaîne. Ton application dit pourtant que l'autre machine est joignable : l'« iptables rule not working » classique. Quelqu'un a posé exactement cette question sur unix.stackexchange (question 762671, 2023-11-29, « Ubuntu 22.04 iptables command not working », 11 225 vues quand nous l'avons lue le 2026-10-25, aucune réponse acceptée). Deux serveurs, trois interfaces chacun, un DROP par paire, et cette phrase :
« when I applied the rule, for a brief moment my app reports the neighbor server is unreachable and suddenly it will change to reachable, something is overriding the rule, am unsure. »
En français : l'application dit brièvement que le voisin est injoignable, puis joignable, « quelque chose écrase la règle, je ne suis pas sûr ». Rien n'écrase la règle. Nous avons reconstruit la situation sur une machine virtuelle Debian 13 et mesuré. La règle fonctionne. Elle ne regarde simplement pas le paquet que tu crois, et iptables -L cache les deux colonnes qui te le disent.
01. Le verdict en trois lignes
iptables -L sans -v cache l'interface (in) et le compteur de paquets. Ces deux colonnes décident de tout. Si le compteur de ton DROP reste figé pendant que le trafic passe, le paquet n'a jamais touché ta règle : il est arrivé par un autre lien, ou quelque chose plus haut l'a accepté.
La seule réponse sous la question (score 1, non acceptée) dit « On Ubuntu 22.04 nftables is used by default » et propose de revenir à iptables-legacy avec update-alternatives. Nous l'avons suivie. Elle ne répare rien : elle ajoute un deuxième pare-feu que ton iptables ne montre plus.
Trois commandes disent la vérité : iptables -V, iptables -L -v -n, nft list ruleset.
02. Le labo : une machine, deux liens
Tout a tourné le 2026-10-25 sur une machine virtuelle, Debian 13.7, noyau 6.12.113+deb13-arm64, avec l'iptables v1.8.11 (nf_tables) d'origine, celui par défaut de Debian 13. Un serveur srv et un client cli vivent dans deux network namespaces, reliés par deux paires veth (adresses de labo). Les règles vivent dans srv : ton pare-feu n'est pas touché, pas besoin de deuxième machine.
Commandes fournies sans garantie : essaie-les sur une machine jetable ou un snapshot.
État de départ de l'alternative, que nous rétablissons à la fin :
état de départ
$ update-alternatives --query iptables | grep -E '^(Status|Value):'
Status: auto
Value: /usr/sbin/iptables-nft
Le labo lui-même. Le </dev/null compte : un nc en arrière-plan qui lit le terminal se fige.
$ sudo ip netns add srv
$ sudo ip netns add cli
$ sudo ip link add s1 netns srv type veth peer name c1 netns cli
$ sudo ip link add s2 netns srv type veth peer name c2 netns cli
$ sudo ip -n srv addr add 10.25.1.1/24 dev s1; sudo ip -n cli addr add 10.25.1.2/24 dev c1
$ sudo ip -n srv addr add 10.25.2.1/24 dev s2; sudo ip -n cli addr add 10.25.2.2/24 dev c2
$ for l in s1 s2 lo; do sudo ip -n srv link set $l up; done; for l in c1 c2 lo; do sudo ip -n cli link set $l up; done
$ sudo ip netns exec srv nc -lk 9000 </dev/null >/dev/null 2>&1 &
nc -z -w 2 affiche exit=0 si la connexion réussit, exit=1 si elle expire après 2 secondes (la signature d'un DROP). Témoin, sans règle :
témoin : sans règle
$ sudo ip netns exec cli nc -z -w 2 10.25.1.1 9000; echo exit=$?
Connection to 10.25.1.1 9000 port [tcp/*] succeeded!
exit=0
$ sudo ip netns exec cli nc -z -w 2 10.25.2.1 9000; echo exit=$?
Connection to 10.25.2.1 9000 port [tcp/*] succeeded!
exit=0
03. La règle, et ce que -L cache
Même forme que chez l'auteur, insérée en tête. Ça bloque.
la règle de la question
$ sudo ip netns exec srv iptables -I INPUT -s 10.25.1.2 -d 10.25.1.1 -i s1 -j DROP
$ sudo ip netns exec cli nc -z -w 2 10.25.1.1 9000; echo exit=$?
exit=1
La liste, puis la liste avec -v :
iptables -L, puis -v
$ sudo ip netns exec srv iptables -L INPUT -n
Chain INPUT (policy ACCEPT)
target prot opt source destination
DROP all -- 10.25.1.2 10.25.1.1
$ sudo ip netns exec srv iptables -L INPUT -v -n
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
2 120 DROP all -- s1 * 10.25.1.2 10.25.1.1
La première liste dit « drop 10.25.1.2 vers 10.25.1.1 », point. La seconde dit : seulement sur s1, deux paquets jusqu'ici. Le man d'iptables : -v « makes the list command show the interface name, the rule options (if any), and the TOS masks. The packet and byte counters are also listed ».
04. Même source, même destination, autre lien
Nous forçons le client à joindre 10.25.1.1 par son autre lien, c2, en gardant la même adresse source 10.25.1.2. ip route get prouve le chemin. La route reste en place : la section 05 en a besoin.
même source, autre lien
$ sudo ip -n cli route add 10.25.1.1/32 dev c2 src 10.25.1.2
$ sudo ip netns exec cli ip route get 10.25.1.1
10.25.1.1 dev c2 src 10.25.1.2 uid 0
cache
$ sudo ip netns exec srv sysctl net.ipv4.conf.all.rp_filter net.ipv4.conf.s2.rp_filter
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.s2.rp_filter = 2
$ sudo ip netns exec cli nc -z -w 2 10.25.1.1 9000; echo exit=$?
Connection to 10.25.1.1 9000 port [tcp/*] succeeded!
exit=0
$ sudo ip netns exec srv iptables -L INPUT -n
Chain INPUT (policy ACCEPT)
target prot opt source destination
DROP all -- 10.25.1.2 10.25.1.1
$ sudo ip netns exec srv iptables -L INPUT -v -n
Chain INPUT (policy ACCEPT 4 packets, 216 bytes)
pkts bytes target prot opt in out source destination
2 120 DROP all -- s1 * 10.25.1.2 10.25.1.1
Joint, avec la règle en place. Le compteur du DROP est figé à 2, et celui de la policy est passé de 0 à 4. Les paquets ont contourné la règle et sont tombés sur la policy.
Pourquoi il arrive ? Linux accepte un paquet adressé à n'importe laquelle de ses adresses, peu importe l'interface d'entrée. La RFC 1122 section 3.3.4.2 décrit les deux modèles et dit : « (A) A host MAY silently discard an incoming datagram whose destination address does not correspond to the physical interface through which it is received ». La RFC ne dit pas quel modèle suit Linux. Notre banc, lui, le montre : il accepte. Ici rp_filter vaut 0 pour all et 2 (loose) pour s2 : le noyau n'a pas jeté le paquet à la validation de la source. Le mode strict (rp_filter=1) pourrait le jeter. Non mesuré.
Une règle dit « cette source, sur cette interface ». La même source sur l'autre interface est un autre paquet.
05. Les trois commandes, puis le correctif
iptables -V dit à quel moteur tu parles. nft list ruleset est le juge : il montre tout ce que le noyau applique, peu importe l'outil qui l'a posé.
iptables -V, nft list ruleset
$ sudo ip netns exec srv iptables -V
iptables v1.8.11 (nf_tables)
$ sudo ip netns exec srv nft list ruleset
table ip filter {
chain INPUT {
type filter hook input priority filter; policy accept;
ip saddr 10.25.1.2 ip daddr 10.25.1.1 iifname "s1" counter packets 2 bytes 120 drop
}
}
Ta règle iptables est une règle nftables, compteur et iifname "s1" compris. Le man de xtables-nft le dit : « Adding a rule will result in that rule being added to the nf_tables kernel subsystem instead. » Le diagnostic « nftables est le défaut, donc iptables ne marche pas » est donc faux : iptables-nft filtre, dès notre premier test.
Le correctif : bloque le voisin par adresse, toutes ses adresses, sans -i. On vide d'abord la chaîne, on garde la route par c2 pour prouver le chemin croisé, puis on la retire.
le correctif : par adresse, sans -i
$ sudo ip netns exec srv iptables -F INPUT
$ sudo ip netns exec srv iptables -I INPUT -s 10.25.1.2 -j DROP
$ sudo ip netns exec srv iptables -I INPUT -s 10.25.2.2 -j DROP
$ sudo ip netns exec cli nc -z -w 2 10.25.1.1 9000; echo exit=$?
exit=1
$ sudo ip netns exec cli ip route get 10.25.1.1
10.25.1.1 dev c2 src 10.25.1.2 uid 0
cache
$ sudo ip -n cli route del 10.25.1.1/32 dev c2
$ sudo ip netns exec cli nc -z -w 2 10.25.1.1 9000; echo exit=$?
exit=1
$ sudo ip netns exec cli nc -z -w 2 10.25.2.1 9000; echo exit=$?
exit=1
$ sudo ip netns exec srv iptables -L INPUT -v -n
Chain INPUT (policy ACCEPT 4 packets, 216 bytes)
pkts bytes target prot opt in out source destination
2 120 DROP all -- * * 10.25.2.2 0.0.0.0/0
4 240 DROP all -- * * 10.25.1.2 0.0.0.0/0
Tout est bloqué, le chemin croisé compris, et les deux compteurs ont bougé. Deux résultats de plus, mesurés dans une autre passe, sans blocs. Une règle ajoutée avec -A derrière un ACCEPT ne voit jamais rien : sur notre banc, l'ACCEPT en ligne 1 a compté 4 paquets et le DROP en ligne 2 en a compté 0. Et un DROP inséré en tête coupe aussi une connexion déjà ouverte : un ping continu a reçu 10 réponses sur 30, coupé net, et un flux TCP a été coupé pareil.
iptables -Z remet les compteurs à zéro avant un test, pour voir lequel bouge. Si tu tiens à l'interface, une règle par interface et par adresse (non testé).
06. « Reviens à iptables-legacy » : ce que ça fait vraiment
Nous avons fait ce que dit cette réponse, les deux règles DROP toujours en place :
retour à iptables-legacy
$ sudo update-alternatives --set iptables /usr/sbin/iptables-legacy
update-alternatives: using /usr/sbin/iptables-legacy to provide /usr/sbin/iptables (iptables) in manual mode
$ sudo ip netns exec srv iptables -V
iptables v1.8.11 (legacy)
$ sudo ip netns exec srv iptables -L INPUT -v -n
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
$ sudo ip netns exec cli nc -z -w 2 10.25.1.1 9000; echo exit=$?
exit=1
La liste est vide et le trafic est toujours bloqué. Les règles nftables sont toujours là, iptables ne les voit plus. ACCEPT legacy en tête, pour laisser passer :
un ACCEPT legacy en tête
$ sudo ip netns exec srv iptables -I INPUT -j ACCEPT
$ sudo ip netns exec cli nc -z -w 2 10.25.1.1 9000; echo exit=$?
exit=1
$ sudo ip netns exec srv iptables -L INPUT -v -n
Chain INPUT (policy ACCEPT 2 packets, 120 bytes)
pkts bytes target prot opt in out source destination
2 120 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0
$ sudo ip netns exec srv iptables-nft -L INPUT -v -n
# Warning: iptables-legacy tables present, use iptables-legacy to see them
Chain INPUT (policy ACCEPT 4 packets, 216 bytes)
pkts bytes target prot opt in out source destination
2 120 DROP all -- * * 10.25.2.2 0.0.0.0/0
8 480 DROP all -- * * 10.25.1.2 0.0.0.0/0
Toujours bloqué. Un paquet traverse les deux pare-feux, legacy et nftables, et un ACCEPT d'un côté ne le sauve pas d'un DROP de l'autre. L'ACCEPT legacy a compté 2 paquets, le DROP nftables est passé de 4 à 8. Seul indice : la ligne # Warning d'iptables-nft. Chaque outil ne montre que son côté.
Retour avec --auto, pas --set : on retrouve exactement l'état de départ (Status: auto), pas un choix manuel.
retour avec --auto
$ sudo ip netns exec srv iptables -F INPUT
$ sudo update-alternatives --auto iptables
update-alternatives: using /usr/sbin/iptables-nft to provide /usr/sbin/iptables (iptables) in auto mode
$ sudo ip netns exec srv iptables -V
iptables v1.8.11 (nf_tables)
Sur un Raspberry Pi, le conseil ne s'applique même pas
Même banc sur un Raspberry Pi Zero 2 W sous Debian 13.7, noyau 6.18.50+rpt-rpi-v8. Les résultats d'iptables-nft sont identiques à ceux de la machine virtuelle. Legacy échoue :
Raspberry Pi : iptables-legacy
$ sudo ip netns exec srv iptables-legacy -I INPUT -s 10.25.1.2 -d 10.25.1.1 -i s1 -j DROP
modprobe: FATAL: Module ip_tables not found in directory /lib/modules/6.18.50+rpt-rpi-v8
iptables v1.8.11 (legacy): can't initialize iptables table `filter': Table does not exist (do you need to insmod?)
Sur ce noyau, « reviens à iptables-legacy » n'est pas un correctif applicable.
07. Le cas de l'auteur, la portée, le démontage
Son symptôme, injoignable un instant puis joignable, colle avec la section 04 : une application qui bascule sur un autre lien entre les deux serveurs, et des règles liées à une interface et à une source. Avec trois interfaces par machine, c'est l'hypothèse la plus plausible, et elle reste une hypothèse. Nous n'avons pas reproduit la brève coupure, et sa vraie cause nous est inconnue.
Pour trancher sur ses serveurs : iptables -L -v -n avant le test et après, iptables -Z entre les deux, et regarder quel compteur bouge. Ses règles OUTPUT (-o) suivent la même logique, non bancées.
Mesuré : la machine virtuelle Debian 13.7 (noyau 6.12.113+deb13-arm64) et le Raspberry Pi Zero 2 W (noyau 6.18, build Raspberry Pi). Non mesuré : Ubuntu 22.04 (même outil iptables-nft, mais nous ne l'avons pas testé), rp_filter en mode strict, les règles OUTPUT, et la vraie cause sur les serveurs de l'auteur.
Le démontage, mesuré : plus de namespace, plus de veth, l'alternative revenue en auto, et aucune table legacy laissée sur la machine (la dernière ligne compte le Warning : 0).
$ sudo ip netns pids srv | xargs -r sudo kill
$ sudo ip netns del srv; sudo ip netns del cli
$ sudo ip netns list | wc -l
0
$ ip -br link | grep -c -E '^(s|c)[12]@' ; true
0
$ update-alternatives --query iptables | grep -E '^(Status|Value):'
Status: auto
Value: /usr/sbin/iptables-nft
$ sudo iptables -L -n 2>&1 | grep -c Warning ; true
0
Le même écart, entre ce qu'un outil liste et ce que le noyau fait, se retrouve dans un fail2ban qui dit l'adresse bannie pendant qu'elle se connecte encore et dans un ufw qui s'affiche actif sans une seule règle chargée.
Partager :