SSD Nodes Learn Hosting plans →
Guides Matt ConnorPar Matt Connor · Mis à jour le 2026-08-22

UFW et IPv6 : pourquoi votre VPS reste exposé

Sur Ubuntu 24.04, UFW gère IPv6, mais votre pare-feu cloud ou un service peut rester exposé. Vérifiez l’accès IPv6 et fermez cette faille.

Le piège du pare-feu IPv6 en une phrase

Votre pare-feu protège IPv4. Votre VPS possède presque certainement aussi une adresse IPv6 publique, et de nombreux services y écoutent par défaut. Si votre pare-feu ne couvre qu’IPv4, ou si vous comptez sur un pare-feu cloud qui ne filtre que l’IPv4, chacun de ces services est accessible depuis l’ensemble d’Internet via IPv6, alors que la partie IPv4 semble correctement verrouillée. Vous testez un port avec curl, obtenez une connexion refusée et vous pensez être en sécurité. Un attaquant se connecte au même port via IPv6 et entre.

Ce guide explique d’où vient cette faille sur un VPS Ubuntu 24.04 standard, comment voir précisément ce que vous exposez et comment fermer cet accès. UFW n’est pas en cause ici. Sur une installation Ubuntu moderne, UFW gère déjà IPv6. L’exposition vient des couches qui l’entourent et des services dont vous ignoriez qu’ils écoutaient.

Pourquoi votre VPS utilise IPv6

Aujourd’hui, presque tous les VPS disposent d’une adresse IPv6 publique, souvent d’un préfixe /64 complet, en plus de leur adresse IPv4. Vérifiez le vôtre :

ip -6 addr show scope global
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> ...
    inet6 2001:db8:2a::1/64 scope global

Ce 2001:db8:2a::1 est routable depuis n’importe où sur Internet, exactement comme votre adresse IPv4. Regardez maintenant ce qui est en écoute :

sudo ss -tlnp
State   Recv-Q  Local Address:Port   Process
LISTEN  0       0.0.0.0:22           sshd
LISTEN  0       [::]:22              sshd
LISTEN  0       127.0.0.1:5432       postgres
LISTEN  0       [::]:8080            docker-proxy

Examinez attentivement la colonne Local Address. 0.0.0.0:22 signifie « écouter sur toutes les adresses IPv4 ». [::]:22 signifie « écouter sur toutes les adresses IPv6 ». 127.0.0.1:5432 est lié à l’interface de loopback et n’est pas public ; la ligne Postgres est donc sûre. Les deux lignes [::] répondent à l’ensemble d’Internet en IPv6, et la ligne docker-proxy correspond au service que vous oubliez avoir démarré.

La plupart des démons se lient à :: par défaut, car sous Linux, un socket :: accepte généralement aussi les connexions IPv4. Par défaut, un serveur fraîchement installé répond donc sur les deux piles, partout. Votre pare-feu est le seul élément qui bloque ces connexions. C’est pourquoi un pare-feu qui ne voit qu’une seule pile pose un véritable problème.

D’où vient réellement l’écart IPv6

Il existe quatre sources courantes. Sur une machine donnée, une seule peut être présente, ou plusieurs à la fois.

1. Un pare-feu cloud qui filtre uniquement IPv4. De nombreux pare-feu de fournisseurs et produits de groupes de sécurité ont été conçus autour d’IPv4. Ils ignorent parfois IPv6 ou nécessitent des règles IPv6 distinctes à ajouter manuellement. Si votre seul pare-feu est celui du tableau de bord du fournisseur et qu’il ne couvre pas IPv6, vos services [::] sont exposés, quelles que soient les indications concernant le port 22 en IPv4. Consultez la documentation du pare-feu de votre fournisseur et recherchez spécifiquement le terme IPv6.

2. Des règles iptables écrites manuellement sans ip6tables. La commande iptables ne modifie que les tables IPv4. IPv6 dispose d’une commande complètement distincte, ip6tables, avec ses propres règles. Si vous avez écrit un script de pare-feu rempli de lignes iptables -A INPUT ... sans écrire les règles ip6tables correspondantes, votre pare-feu IPv6 est vide. Une chaîne INPUT vide avec une stratégie ACCEPT par défaut autorise alors tout le trafic :

sudo ip6tables -L INPUT -n
Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Cette sortie montre tout le problème sur un seul écran. IPv4 est filtré, tandis qu’IPv6 accepte le trafic provenant de partout.

3. Docker publie directement les ports en contournant votre pare-feu. Lorsque vous exécutez docker run -p 8080:80, Docker insère ses propres règles avant celles d’UFW. Un port publié reste donc accessible même lorsque ufw status indique que ce port est refusé. Avec les versions modernes de Docker, cela s’applique également à IPv6. Pourquoi Docker contourne UFW et comment filtrer correctement les ports des conteneurs explique le mécanisme et les solutions. Consultez les bases de Docker Compose sur un VPS pour voir comment ces ports publiés sont déclarés.

4. UFW avec IPv6 désactivé. UFW gère bien IPv6, mais uniquement si cette fonction est activée. Vérifiez ce paramètre :

grep IPV6 /etc/default/ufw

Les versions modernes d’Ubuntu incluent IPV6=yes. UFW applique donc chaque règle aux deux piles réseau. Si vous voyez IPV6=no, généralement avec une ancienne image ou un ancien guide, toutes les règles UFW que vous avez écrites ne s’appliquent qu’à IPv4 et IPv6 n’est pas géré.

Voir exactement ce que vous exposez

Ne devinez pas. Mesurez depuis l’extérieur. Commencez par lister les ports en écoute et notez tous ceux qui sont liés à :: :

sudo ss -tlnp | grep '::'

Ensuite, depuis une autre machine, connectez-vous à l’adresse IPv6 publique du serveur et testez un port que vous pensez fermé :

curl -6 -v http://[2001:db8:2a::1]:8080/

Si une page ou une bannière s’affiche, le port est ouvert en IPv6. Un port fermé renvoie Connection refused ou provoque un dépassement de délai. Ces deux échecs ne fournissent pas le même signal, et la différence entre un refus et un dépassement de délai indique si l’hôte a répondu en refusant la connexion ou si un pare-feu a silencieusement abandonné votre paquet. Pour obtenir une vue complète, analysez l’adresse IPv6 avec nmap depuis l’extérieur du serveur :

nmap -6 2001:db8:2a::1

Chaque port que nmap indique comme ouvert en IPv6 est accessible depuis l’ensemble d’Internet, quel que soit le résultat de votre analyse IPv4. Comparez les analyses IPv4 et IPv6 côte à côte pour trouver rapidement l’écart : tout port ouvert sur -6 mais fermé en IPv4 correspond à un service que votre pare-feu ne protège pas.

Combler l’écart

Faites couvrir les deux piles réseau par UFW et refusez les connexions par défaut. Vérifiez le changement, puis définissez une politique entrante qui refuse tout par défaut et n’autorisez que ce dont vous avez besoin :

sudo sed -i 's/^IPV6=no/IPV6=yes/' /etc/default/ufw
sudo ufw default deny incoming
sudo ufw allow 22/tcp
sudo ufw enable
sudo ufw status verbose

Si UFW était déjà actif lorsque vous avez activé IPV6=yes, le changement ne prend effet qu’après l’exécution de sudo ufw reload.

ufw status liste chaque règle deux fois : une fois normalement et une fois avec le suffixe (v6). Lorsque les lignes (v6) apparaissent, UFW filtre IPv6 :

22/tcp                     ALLOW IN    Anywhere
22/tcp (v6)                ALLOW IN    Anywhere (v6)

Si vous gérez iptables manuellement, reproduisez chaque règle dans ip6tables, ou passez à nftables. Ses tables inet couvrent IPv4 et IPv6 au même endroit et éliminent cette catégorie d’erreurs. Une seule table de filtrage nftables inet est la solution la plus propre lorsque vous écrivez vous-même les règles. Si votre VPS utilise Rocky ou AlmaLinux plutôt qu’Ubuntu, UFW n’est pas disponible à configurer et firewalld est l’interface que vous gérez à la place. Il applique ses règles de zone aux deux piles à la fois.

Liez à loopback les services qui ne doivent pas être publics. Une base de données, un panneau d’administration ou un endpoint de métriques n’a généralement pas besoin d’une adresse publique. Liez-le à 127.0.0.1 et ::1 afin qu’il n’écoute jamais directement sur une adresse routable. Pour Postgres, définissez listen_addresses = 'localhost'. Pour un serveur d’application, liez-le à 127.0.0.1 et placez un reverse proxy devant. Fermer le listener est préférable à son filtrage par le pare-feu, car il n’y a alors rien à atteindre.

Ne faites pas confiance à UFW pour protéger les ports publiés par Docker. Publiez les ports des conteneurs sur une adresse précise plutôt que sur toutes les interfaces, par exemple -p 127.0.0.1:8080:80, afin que le port ne soit accessible que depuis l’hôte et depuis ce que vous exposez volontairement avec un proxy. Lorsqu’un conteneur doit réellement être public, placez-le derrière un reverse proxy Traefik et publiez uniquement le proxy, pas chaque application.

Ajoutez les règles IPv6 au pare-feu de votre fournisseur, ou reconnaissez qu’il ne protège pas votre trafic IPv6 et laissez UFW ou nftables sur l’hôte s’en charger à la place.

Vérifiez que le port est réellement fermé

Réalisez à nouveau le même test externe après vos modifications :

curl -6 -v http://[2001:db8:2a::1]:8080/
nmap -6 2001:db8:2a::1

Le port qui répondait auparavant devrait maintenant refuser la connexion ou expirer, et nmap devrait l’indiquer comme filtré ou fermé. Si un port est toujours ouvert, reprenez les quatre sources précédentes : un service toujours lié à :: sans règle en amont, une règle Docker placée avant UFW, ou un pare-feu du fournisseur qui n’a jamais pris en charge IPv6.

Le plus sûr reste de ne pas exposer du tout les services sensibles sur Internet. Placez SSH et les panneaux d’administration derrière un VPN WireGuard et filtrez leurs ports afin qu’ils ne répondent que sur le tunnel. La question de l’exposition IPv6 ne se pose alors plus pour ces services. Pour ralentir les scans par force brute qui ciblent les services restant publics, ajoutez Fail2ban devant SSH à un pare-feu configuré par défaut pour tout refuser.

Si la notion de port est nouvelle pour vous, commencez par lire ce qu’est un port et comment les services écoutent.

FAQ

UFW bloque-t-il IPv6 par défaut ?

Sur une installation Ubuntu 24.04 récente, oui. UFW lit IPV6=yes depuis /etc/default/ufw et applique chaque règle à IPv4 et IPv6. La commande ufw status affiche les règles IPv6 avec le suffixe (v6). Le problème survient lorsque IPV6=no (à cause d’une ancienne image ou d’un ancien tutoriel), lorsque vous utilisez un pare-feu fourni par l’hébergeur qui ne filtre que l’IPv4, ou lorsque Docker publie un port en contournant UFW. Vérifiez ce paramètre avec grep IPV6 /etc/default/ufw.

Comment vérifier ce que mon VPS expose en IPv6 ?

Exécutez sudo ss -tlnp et repérez chaque listener dont l’adresse locale commence par [::]. Cela signifie que le service répond sur toutes les interfaces IPv6. Depuis une autre machine, testez directement l’adresse IPv6 publique du serveur avec curl -6 -v http://[YOUR:IPV6::ADDR]:PORT/, ou analysez-la avec nmap -6 YOUR:IPV6::ADDR. Tout port ouvert lors de l’analyse IPv6 mais fermé en IPv4 constitue une faille de configuration.

Pourquoi puis-je accéder au port de mon conteneur Docker alors qu’UFW indique qu’il est bloqué ?

Docker insère ses propres règles de pare-feu avant celles d’UFW lorsque vous publiez un port avec -p. Le port publié reste donc accessible, même si ufw status l’indique comme refusé. Cela se produit en IPv4, et aussi en IPv6 lorsque la prise en charge d’IPv6 de Docker est activée. Publiez le port sur une adresse précise, comme -p 127.0.0.1:8080:80, ou placez le conteneur derrière un reverse proxy et ne publiez que le proxy.

Ai-je toujours besoin d’un pare-feu IPv6 si mon pare-feu IPv4 est correctement configuré ?

Oui. IPv4 et IPv6 sont deux piles réseau distinctes, avec des règles de pare-feu distinctes. Un ensemble parfait de règles IPv4 ne filtre pas le trafic IPv6. Si votre VPS possède une adresse IPv6 publique, ce qui est le cas de presque tous les VPS, tout service qui écoute sur :: reste accessible en IPv6 tant qu’une règle de pare-feu IPv6 ou une écoute sur loopback ne le bloque pas.

Comment faire écouter un service uniquement en IPv4, ou uniquement sur localhost ?

Définissez l’adresse d’écoute du service dans sa propre configuration. Utilisez 127.0.0.1 pour l’écoute sur la loopback IPv4 uniquement, ou 0.0.0.0 pour toutes les adresses IPv4 sans listener IPv6. Postgres utilise listen_addresses, SSH utilise ListenAddress, et la plupart des serveurs applicatifs proposent un paramètre host ou bind. Vérifiez le résultat avec sudo ss -tlnp et contrôlez que Local Address n’affiche plus [::].