Partager :
Surface d'attaque · ufw
ufw block ping : « deny incoming » et le serveur répond quand même (et le piège IPv6)
Publié le · HardStacked · 12 min
Tu as activé ufw. ufw status verbose affiche Default: deny (incoming). Tu pinges le serveur depuis une autre machine et il répond, à chaque fois. Quelqu'un l'a demandé sur askubuntu (question 1504520, 2024-02-23, 15 072 vues au 2026-10-21, aucune réponse acceptée) et a posé son but en une phrase :
« For security, I'd like to hide my server from ping requests », soit « pour la sécurité, je voudrais cacher mon serveur des requêtes ping ».
Tu cherches ufw deny ping, puis ufw block ping, et ufw n'a pas d'option pour ça. sudo ufw deny proto icmp from any répond ERROR: Unsupported protocol 'icmp', et c'est là que les gens collent un sed trouvé sur internet.
Ce sed marche en IPv4. Ton serveur continue de répondre au ping en IPv6, et personne ne dit ce que le sed a touché d'autre. Nous avons tout mesuré le 2026-10-21. Les commandes sont plus bas.
01. Le verdict en trois lignes
ufw ne laisse pas passer le ping par erreur. Il l'accepte exprès, dans /etc/ufw/before.rules à la ligne 37, un fichier lu avant tes propres règles. Aucune commande ufw n'affiche cette ligne et aucune ne la change. Le correctif tient en deux lignes commentées, une pour IPv4 et une pour IPv6, puis sudo ufw reload.
Les réponses en ligne oublient une des deux lignes ou en commentent trop. Sur notre banc, commenter toutes les lignes ICMPv6 d'entrée a rendu SSH injoignable en IPv6, alors que le port 22 était autorisé.
Trois voyants mentent ici : Default: deny (incoming) pendant que le ping passe, un ufw status identique que le ping soit accepté ou non, et un deny proto icmp qui n'existe pas.
02. Le banc : un client et un serveur sur la même machine
Tout a été mesuré le 2026-10-21 sur un Raspberry Pi Zero 2 W sous Debian 13 trixie, ufw 0.36.2-9, noyau 6.18 (build Raspberry Pi). Une seule machine. Le « client » vit dans un network namespace, relié au serveur par une paire veth, avec des adresses de labo 10.215.0.1 (serveur) et 10.215.0.2 (client), fd00:215::1 et fd00:215::2 en IPv6. Son trafic traverse le pare-feu d'entrée comme celui d'un vrai client : pas besoin d'une deuxième machine.
Ces commandes sont fournies telles quelles, sans garantie. Teste d'abord sur une machine non critique ou sur un instantané.
mise en place du banc
$ sudo ip netns add client
$ sudo ip link add veth-host type veth peer name veth-client
$ sudo ip link set veth-client netns client
$ sudo ip addr add 10.215.0.1/24 dev veth-host
$ sudo ip -6 addr add fd00:215::1/64 dev veth-host nodad
$ sudo ip link set veth-host up
$ sudo ip -n client addr add 10.215.0.2/24 dev veth-client
$ sudo ip -n client -6 addr add fd00:215::2/64 dev veth-client nodad
$ sudo ip -n client link set veth-client up
Le témoin est le port TCP 8080, où rien n'écoute. ufw coupé, la connexion est refusée (exit=1) : le chemin marche. ufw actif, on attend le silence (exit=124) : ufw jette le paquet. Preuve qu'ufw filtre pendant que le ping passe.
03. ufw coupé, puis ufw actif
État A, ufw désactivé :
état A : ufw désactivé
$ sudo ufw disable
Firewall stopped and disabled on system startup
$ sudo ip netns exec client ping -c 3 -W 2 10.215.0.1 | tail -2
3 packets transmitted, 3 received, 0% packet loss, time 2050ms
$ sudo ip netns exec client timeout 5 bash -c "</dev/tcp/10.215.0.1/8080"; echo "exit=$?"
bash: connect: Connection refused
exit=1
État B, deny incoming, seul 22/tcp autorisé (statut abrégé) :
état B : deny incoming, 22/tcp autorisé
$ sudo ufw default deny incoming
$ sudo ufw allow 22/tcp
$ sudo ufw --force enable
$ sudo ufw status verbose
Default: deny (incoming), allow (outgoing), disabled (routed)
Mêmes tests :
état B : mêmes tests
$ sudo ip netns exec client ping -c 3 -W 2 10.215.0.1 | tail -2
3 packets transmitted, 3 received, 0% packet loss, time 2053ms
$ sudo ip netns exec client ping -6 -c 3 -W 2 fd00:215::1 | tail -2
3 packets transmitted, 3 received, 0% packet loss, time 2026ms
$ sudo ip netns exec client timeout 5 bash -c "</dev/tcp/10.215.0.1/8080"; echo "exit=$?"
exit=124
Ping 3/3 dans les deux familles, témoin TCP muet : ufw filtre, le ping non. La commande qui aurait dû régler ça :
état B : deny proto icmp
$ sudo ufw deny proto icmp from any
ERROR: Unsupported protocol 'icmp'
Où le ping est-il accepté ? L'ordre des chaînes le dit :
ordre des chaînes, INPUT
$ sudo iptables -S INPUT
-P INPUT DROP
-A INPUT -j ufw-before-logging-input
-A INPUT -j ufw-before-input
-A INPUT -j ufw-after-input
ufw-before-input est atteinte en premier, et un ACCEPT met fin au parcours. Alors où sont les règles que tu as tapées ? Tout au bout de cette même chaîne :
où vivent tes règles
$ sudo iptables -S ufw-before-input | tail -1
-A ufw-before-input -j ufw-user-input
$ sudo iptables -S ufw-user-input
-N ufw-user-input
-A ufw-user-input -p tcp -m tcp --dport 22 -j ACCEPT
Ton allow 22/tcp vit dans ufw-user-input, et cette chaîne n'est atteinte que par la dernière ligne de ufw-before-input. Ce que ufw-before-input accepte avant ne rencontre jamais tes règles. Et plus haut dans cette chaîne :
ICMP dans ufw-before-input
$ sudo iptables -S ufw-before-input | grep icmp
-A ufw-before-input -p icmp -m icmp --icmp-type 3 -j ACCEPT
-A ufw-before-input -p icmp -m icmp --icmp-type 11 -j ACCEPT
-A ufw-before-input -p icmp -m icmp --icmp-type 12 -j ACCEPT
-A ufw-before-input -p icmp -m icmp --icmp-type 8 -j ACCEPT
Type 3 : destination-unreachable, 11 : time-exceeded, 12 : parameter-problem, 8 : echo-request. Quatre types acceptés, pas seulement le ping. La section 05 y revient. La source :
la source des règles
$ sudo grep -n "echo-request" /etc/ufw/before.rules /etc/ufw/before6.rules
/etc/ufw/before.rules:37:-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT
/etc/ufw/before.rules:43:-A ufw-before-forward -p icmp --icmp-type echo-request -j ACCEPT
/etc/ufw/before6.rules:49:-A ufw6-before-input -p icmpv6 --icmpv6-type echo-request -j ACCEPT
La ligne 37 est le ping IPv4, la ligne 49 de before6.rules le ping IPv6, la ligne 43 le jumeau FORWARD (deux lignes IPv6 de plus coupées). Ces fichiers viennent d'ufw : nous avons comparé le 0.36.2-9 de Debian à l'amont (launchpad), les lignes -A sont identiques. Même correctif sur Ubuntu, non mesuré là-bas.
04. Le correctif, avec la preuve
Deux lignes, pas plus. sed -i.bak garde une copie de chaque fichier :
le correctif : deux lignes
$ sudo sed -i.bak '/ufw-before-input -p icmp --icmp-type echo-request/s/^/#/' /etc/ufw/before.rules
$ sudo sed -i.bak '/ufw6-before-input -p icmpv6 --icmpv6-type echo-request/s/^/#/' /etc/ufw/before6.rules
$ sudo grep -n "input.*echo-request" /etc/ufw/before.rules /etc/ufw/before6.rules
/etc/ufw/before.rules:37:#-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT
/etc/ufw/before6.rules:49:#-A ufw6-before-input -p icmpv6 --icmpv6-type echo-request -j ACCEPT
$ sudo ufw reload
Firewall reloaded
Pas de redémarrage, ufw reload suffit. Mêmes tests :
après le correctif : mêmes tests
$ sudo ip netns exec client ping -c 3 -W 2 10.215.0.1 | tail -2
3 packets transmitted, 0 received, 100% packet loss, time 2041ms
$ sudo ip netns exec client ping -6 -c 3 -W 2 fd00:215::1 | tail -2
3 packets transmitted, 0 received, 100% packet loss, time 2040ms
$ sudo ip netns exec client timeout 5 bash -c "</dev/tcp/10.215.0.1/8080"; echo "exit=$?"
exit=124
Ping 0/3 dans les deux familles, témoin TCP toujours muet. ufw status verbose après le reload est identique mot pour mot à celui de l'état B (dix lignes comparées). Il ne dit rien de l'ICMP, il ne peut donc pas confirmer ton travail.
Pour défaire, remets les copies et recharge :
pour défaire
$ sudo mv /etc/ufw/before.rules.bak /etc/ufw/before.rules
$ sudo mv /etc/ufw/before6.rules.bak /etc/ufw/before6.rules
$ sudo ufw reload
Firewall reloaded
$ sudo ip netns exec client ping -c 3 -W 2 10.215.0.1 | tail -2
3 packets transmitted, 3 received, 0% packet loss, time 2037ms
Ping IPv4 de retour à 3/3 (IPv6 aussi dans la capture complète). Si ta machine route du trafic pour d'autres (passerelle, VPN), la ligne 43 de before.rules est la même règle en FORWARD. Ni testée ni touchée.
05. Ce que les deux réponses d'askubuntu ratent
La première réponse (score 1, 2024-02-23) écrit : « By default, ufw blocks all ICMP requests except pings. » Le listing de la section 03 dit autre chose : quatre types ICMP sont acceptés en entrée. Son motif sed, s/^\s*-A\ ufw-before-input\ -p\ icmp\ --icmp-type.*ACCEPT\s*/#\ \0/g, les attrape tous les quatre et les commente, puis fait pareil dans ufw-before-forward. Motif lu, pas exécuté. La deuxième réponse (score 0, 2025-03-26) ne commente que echo-request, écrit « maybe reboot » et « I tried it, and it worked for me ». Ça marche, en IPv4, sans redémarrage.
Aucune ne parle d'IPv6, toutes deux ne touchent que before.rules. Même chose chez nous :
ce que font les deux réponses : before.rules seul
$ sudo sed -i.bak '/ufw-before-input -p icmp --icmp-type echo-request/s/^/#/' /etc/ufw/before.rules
$ sudo ufw reload
Firewall reloaded
$ sudo ip netns exec client ping -c 3 -W 2 10.215.0.1 | tail -2
3 packets transmitted, 0 received, 100% packet loss, time 2051ms
$ sudo ip netns exec client ping -6 -c 3 -W 2 fd00:215::1 | tail -2
3 packets transmitted, 3 received, 0% packet loss, time 2036ms
IPv4 0/3, IPv6 3/3. Le serveur répond toujours en IPv6, et celui qui a appliqué ça ne le sait pas.
L'erreur inverse maintenant : appliquer l'idée de la première réponse à IPv6, donc commenter toutes les lignes ICMPv6 d'entrée. D'abord on remet before.rules en place, puis le témoin : règles d'origine, SSH joignable en IPv6. Le cache de voisins du client est vidé avant chaque mesure (explication plus bas) :
témoin : règles d'origine, SSH en IPv6
$ sudo mv /etc/ufw/before.rules.bak /etc/ufw/before.rules
$ sudo ufw reload
Firewall reloaded
$ sudo ip -n client -6 neigh flush all
$ sudo ip netns exec client timeout 5 bash -c "</dev/tcp/fd00:215::1/22"; echo "exit=$?"
exit=0
Avant de toucher à quoi que ce soit, regarde deux des lignes que ça va viser. En IPv6, une machine trouve l'adresse matérielle de son voisin avec des messages ICMPv6, neighbor solicitation et advertisement, acceptés ici :
lignes neighbor de before6.rules
$ sudo grep -n "neighbor" /etc/ufw/before6.rules
52:-A ufw6-before-input -p icmpv6 --icmpv6-type neighbor-solicitation -m hl --hl-eq 255 -j ACCEPT
53:-A ufw6-before-input -p icmpv6 --icmpv6-type neighbor-advertisement -m hl --hl-eq 255 -j ACCEPT
Puis la version transposée :
toutes les lignes ICMPv6 d'entrée commentées
$ sudo sed -i.bak -E 's/^-A ufw6-before-input -p icmpv6 .*-j ACCEPT/#&/' /etc/ufw/before6.rules
$ sudo grep -c "^#-A ufw6-before-input -p icmpv6" /etc/ufw/before6.rules
25
$ sudo ufw reload
Firewall reloaded
$ sudo ip -n client -6 neigh flush all
$ sudo ip netns exec client ping -6 -c 3 -W 2 fd00:215::1 | tail -2
3 packets transmitted, 0 received, 100% packet loss, time 2026ms
$ sudo ip netns exec client timeout 5 bash -c "</dev/tcp/fd00:215::1/22"; echo "exit=$?"
exit=124
25 lignes commentées, ping 0/3, et SSH en IPv6 donne exit=124 alors que le port 22 est autorisé. Nous les avons commentées d'un coup, sans isoler lesquelles comptent. Les lignes 52 et 53 montrées plus haut en font partie.
C'est notre lecture de la panne : sans ces messages, le client ne trouve pas l'adresse matérielle du serveur, et plus rien n'est livré, SSH compris. Nous ne l'avons pas isolé ligne par ligne. Le neigh flush all vide le cache du client : sans lui, SSH marche tant que l'entrée en cache vit et tu conclus que rien n'a cassé. Remets le fichier d'origine et SSH revient :
before6.rules d'origine remis
$ sudo mv /etc/ufw/before6.rules.bak /etc/ufw/before6.rules
$ sudo ufw reload
Firewall reloaded
$ sudo ip -n client -6 neigh flush all
$ sudo ip netns exec client timeout 5 bash -c "</dev/tcp/fd00:215::1/22"; echo "exit=$?"
exit=0
La première réponse commente aussi destination-unreachable. La RFC 1191 décrit à quoi il sert : un routeur qui ne peut pas faire suivre un paquet portant le bit DF « will discard them and return ICMP Destination Unreachable messages with a code meaning 'fragmentation needed and DF set' », et à la réception « the source host reduces its assumed PMTU for the path. » Ce sed casse-t-il alors la découverte du MTU du chemin ? Probablement pas pour tes propres connexions, et la raison se trouve plus haut dans le même fichier. La ligne 25 de before.rules, avant toute ligne ICMP, est -A ufw-before-input -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT. Le manuel d'iptables (iptables-extensions, iptables 1.8.11) définit RELATED comme un paquet « associated with an existing connection, such as an FTP data transfer or an ICMP error », soit lié à une connexion existante, comme un transfert de données FTP ou une erreur ICMP. Un message « fragmentation needed » qui concerne une de tes connexions est une telle erreur : la ligne 25 le laisse entrer avant que les lignes commentées soient lues. C'est notre lecture du manuel, nous ne l'avons pas mesuré. Ce que fait sûrement la première réponse, c'est commenter quatre règles pour en bloquer une, sans gain que nous ayons trouvé pour les trois autres.
06. Cacher le ping cache-t-il le serveur ?
Non. Le manuel de nmap, section sur la découverte d'hôtes (nmap.org/book/man-host-discovery.html, lu le 2026-10-21) : -Pn « skips the host discovery stage altogether », et « causes Nmap to attempt the requested scanning functions against every target IP address specified. »
Notre banc le montre. Réapplique le correctif de la section 04, l'état où tu veux finir, et teste SSH cette fois :
le correctif à nouveau, SSH cette fois
$ sudo sed -i.bak '/ufw-before-input -p icmp --icmp-type echo-request/s/^/#/' /etc/ufw/before.rules
$ sudo sed -i.bak '/ufw6-before-input -p icmpv6 --icmpv6-type echo-request/s/^/#/' /etc/ufw/before6.rules
$ sudo ufw reload
Firewall reloaded
$ sudo ip netns exec client ping -c 3 -W 2 10.215.0.1 | tail -2
3 packets transmitted, 0 received, 100% packet loss, time 2041ms
$ sudo ip netns exec client timeout 5 bash -c "</dev/tcp/10.215.0.1/22"; echo "exit=$?"
exit=0
$ sudo ip netns exec client ping -6 -c 3 -W 2 fd00:215::1 | tail -2
3 packets transmitted, 0 received, 100% packet loss, time 2035ms
$ sudo ip netns exec client timeout 5 bash -c "</dev/tcp/fd00:215::1/22"; echo "exit=$?"
exit=0
Ping 0/3 en IPv4 et en IPv6, et SSH joignable dans les deux avec exit=0. Un ping bloqué réduit le bruit des balayages naïfs, rien de plus. Ce qui protège le serveur, ce sont les ports fermés et la politique deny incoming, déjà en place avant qu'on touche à quoi que ce soit. Sur ce que lit vraiment l'écran de statut : le pare-feu qui rapporte vert en ne bloquant rien.
Le client de labo se retire d'une seule commande. Supprimer le namespace supprime aussi les deux bouts de la paire veth, et le correctif reste en place :
retirer le client de labo
$ sudo ip netns del client
$ ip link show veth-host
Device "veth-host" does not exist.
07. Mesuré, lu, non mesuré
Mesuré le 2026-10-21 : Debian 13 trixie, ufw 0.36.2-9, noyau 6.18 Raspberry Pi, IPv4 et IPv6, client en namespace. Lu seulement : les fichiers ufw de l'amont, le motif sed de la première réponse, la RFC 1191, le manuel d'iptables (RELATED). Non mesuré : Ubuntu, la ligne FORWARD, la panne PMTUD.
Partager :