Déploiement de l’IPv6 sur Spirio.fr

Déploiement de l’IPv6 sur Spirio.fr

Introduction

Cela fait des années que l’on entend parler de l’adressage par IPv6 : pour certains, c’est une évolution naturelle, pour d’autres, une transition complexe à éviter. Personnellement, je n’ai jamais eu besoin d’avoir cette compétence au niveau professionnel, je l’ai donc remise à « demain », qui l’a remise à « plus tard », qui l’a remise à « un jour », et qui l’a remise à « jamais ». Des années plus tard, après une longue après-midi pluvieuse en mars, je me suis dit « Allez, c’est maintenant ! ».

Dans cet article, vous allez voir la liste des modifications que j’ai dû effectuer sur l’infrastructure pour rendre spirio.fr et ses services accessible en IPv6. A ma grande surprise, cela s’est avéré simple pour la plupart des services. Seul AdGuardHome a nécessité que je comprenne un peu mieux le fonctionne du protocole IPv6 et de l’implémentation dans Nftables de la gestion de l’IPv6. Bonne lecture 😃

Disclaimer
Je ne suis pas un expert réseau, ni un expert tout cours d’ailleurs. J’ai modifié les différentes configurations pour que la fonctionne, il existe probablement de meilleure façon de faire.

Les grands changements IPv6

N’ayant que peu de connaissance en IPv6, je me suis au début tourné vers un LLM pour m’aider à rédiger cette section. J’ai au final abandonné, car le texte qu’il m’a donné me semblait vide et peu qualitatif. A la place, si vous souhaitez en savoir plus sur ces grands changements, je vous invite consulter ces sites :

que je trouve bien fait, je compte sur vous pour revenir finir de lire l’article. 😆

Activation de l’adressage IPv6 sur le serveur

Première étape, on regarde la connectivité du serveur, et notamment le fait qu’une adresse IPv6 soit rattachée à une interface et que celle-ci réponde au ping.

ip a
2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000                                                     
    link/ether                                                                                         
    altname enp0s3                                                                                                                                                     
    inet 37.59.122.154/32 metric 100 scope global dynamic ens3                                                                                          
       valid_lft 52261sec preferred_lft 52261sec                                                                                                        
    inet6 2001:41d0:305:2100::b5fa/128 scope global                                                                                                     
       valid_lft forever preferred_lft forever

OVH attribue une adresse IP par défaut à tous ces VPS. On peut voir que l’adresse IPv6 attribuée à l’interface publique est 2001:41d0:305:2100::b5fa. Vérifions maintenant si la connectivité depuis l’extérieur est fonctionnelle.

ping 2001:41d0:305:2100::b5fa
PING 2001:41d0:305:2100::b5fa(2001:41d0:305:2100::b5fa) 56 data bytes
64 bytes from 2001:41d0:305:2100::b5fa: icmp_seq=1 ttl=48 time=12.9 ms
64 bytes from 2001:41d0:305:2100::b5fa: icmp_seq=2 ttl=48 time=13.2 ms

Le serveur répond, good ! Petite information utile à savoir, si votre machine ne possède pas d’adresse IPv6, vous ne pourrez pas requêter un serveur en IPv6. Assurez-vous bien d’avoir une interface avec adresse IPv6 routable.

La configuration pare-feu

Initialement, je n’ai pas eu à faire de modification sur le pare-feu Nftables qui est déployé sur le serveur. Les règles présentes dans la section « table inet filter » s’appliquent à la fois pour l’adressage IPv4 et IPv6. Les seules modifications que j’ai dû effectuer concernaient le NAT prerouting et le postrouting pour le DNS AdGuardHome que j’héberge sur le serveur.

Afin que les requêtes DNS arrivent bien au conteneur, j’ai mis en place du NAT sur ports 53 TCP/UDP et TCP:853 vers l’adresse IP du conteneur Podman. Je ne pense pas que cela la bonne façon de faire, mais c’était la plus simple et efficace au vu de l’historique. Dans Nftables, les tables ne peuvent être à la fois en IPv4 et en IPv6. J’ai créé une table spécifique à l’adressage IPv6 afin que les paquets qui arrivent sur les ports 53 TCP/UDP et 853 UDP soient directement nattés vers l’adresse IPv6 du conteneur podman AdGuardHome. Cela nous donne cette configuration à ajouter à Nftables

adguardhome= fd00:10:xx::x
table ip6 nat {
        chain prerouting {
                type nat hook prerouting priority 0; policy accept;

                # AdguardHome DNS IPv6 nat
                iifname ens3 ip6 daddr $spirio.frAAAA udp dport 53 dnat $adguardhome:53
                iifname ens3 ip6 daddr $spirio.frAAAA tcp dport 53 dnat $adguardhome:53
                iifname ens3 ip6 daddr $spirio.frAAAA tcp dport 853 dnat $adguardhome:853
        }

        chain postrouting {
                type nat hook postrouting priority 100; policy accept;
                oifname ens3 ip6 saddr $spirio.frAAAA masquerade
        }
}

Nftables comprend les adresses IPv6 dans le format standard ainsi qu’entouré de crochet. Je trouve que lorsqu’il est fait mention d’un port pour du NAT par exemple, on gagne en lisibilité à les mettre. Ce qui donne par exemple, fd00:10:xx::x:53 qui devient [fd00:10:xx::x]:53.

Le reverse Proxy HTTPD

Les modifications ne sont pas nécessaires, Apache HTTPD avec la configuration par défaut sous Debian, écoute en IPv4 et IPv6 ainsi que les VirtualHosts. Merci à la personne pour son commentaire !

On va maintenant regarder du côté du reverse proxy. De base, celui-ci n’écoute qu’en IPv4. Comme on souhaite maintenir la connectivité IPv4, on va lui demander de gérer à la fois le protocole IPv4 et IPv6. J’utilise HTTPD de la fondation Apache. Je l’utilise plus par habitude qu’autre chose, à choisir, je m’orienterai plus vers Nginx car il semble plus performant aujourd’hui. Pour ce qui est en capacité à écouter à IPv6, Apache HTTPD permet en modifiant le VirtualHost d’écouter à la fois en IPv4 et en IPv6. Voici les modifications à effectuer.

<VirtualHost *:80>                                                                                                                              
# Devient
<VirtualHost *:80 [::]:80>                                                                                                                              

<VirtualHost *:443>
# Devient
<VirtualHost *:443 [::]:443>

# Redémarrage du serveur
sudo systemctl restart apache2

Vérifions que le logiciel HTTPD écoute bien en IPv6.

ss -6plantu|grep apache2

tcp   LISTEN 0      511                *:443              *:*    users:(("apache2",pid=365186,fd=6),("apache2",pid=365185,fd=6),("apache2",pid=365128,fd=6),("apache2",pid=354280,fd=6))
tcp   LISTEN 0      511                *:80               *:*    users:(("apache2",pid=365186,fd=4),("apache2",pid=365185,fd=4),("apache2",pid=365128,fd=4),("apache2",pid=354280,fd=4))

Apache HTTPD opère en tant que reverse proxy et redirige les requêtes IPv4 et IPv6 vers des conteneurs Podman qui tournent en IPv4. La question qui se pose désormais est de savoir si Apache HTTPD est capable de convertir une requêtes en IPv6 vers un conteneur en IPv4. La réponse est oui ! 🥳 Cela signifie la quasi-totalité des services proposés par spirio.fr fonctionnent en IPv6 désormais. Ils fonctionnent, oui, mais l’adresse IPv6 qui leur est attribuée est-elle annoncée ?

La modification des enregistrements DNS

Spirio.fr et ses services étaient jusqu’alors annoncés uniquement en IPv4. Concrétement, cela signifie que le serveur DNS ne répondait que pour les requêtes DNS de type A. Afin qu’ils soient accessibles en IPv6, il est nécessaire d’ajouter une entrée DNS de type AAAA au domaine et sous-domaines afin que lorsqu’une requête DNS de type AAAA les concernent, l’adresse IPv6 du serveur soit retourné.

Afin de simplifier la gestion des adresses IP, j’utilise un enregistrement de type CNAME. Cela permet de n’avoir à spécifier l’adresse IP qu’une seule fois et d’avoir des liens symboliques vers ce seul enregistrement, ici l’enregistrement server. Factuellement, les requêtes DNS de tous les types d’enregistrements DNS sont transmises à l’adresse IP du CNAME.

# Enregistrements déjà présents
server      3600 IN A     37.59.122.154
dns          60   IN CNAME server.spirio.fr.
geoquest     3600 IN CNAME server.spirio.fr.
it-tools     3600 IN CNAME server.spirio.fr.
omnitools    3600 IN CNAME server.spirio.fr.
pdf          3600 IN CNAME server.spirio.fr.

# Enregistrement ajoutés pour permettre le fonctionnement en IPv6
             3600 IN AAAA  2001:41d0:305:2100::b5fa
server       3600 IN AAAA  2001:41d0:305:2100::b5fa

Une fois l’enregistrement des modifications réalisées et les enregistrements DNS propagés à la plupart des résolveurs publiques, les requêtes DNS de type AAAA pour spirio.fr et ses services répondront l’adresse IPv6 spécifier. Vérifions en requêtant le résolveur 9.9.9.9 de Quad9.

dig +short @9.9.9.9 spirio.fr AAAA dns.spirio.fr AAAA geoquest.spirio.fr AAAA it-tools.spirio.fr AAAA pdf.spirio.fr AAAA

2001:41d0:305:2100::b5fa
server.spirio.fr.
2001:41d0:305:2100::b5fa
server.spirio.fr.
2001:41d0:305:2100::b5fa
server.spirio.fr.
2001:41d0:305:2100::b5fa
server.spirio.fr.
2001:41d0:305:2100::b5fa

On récupère bien l’adresse en IPv6 ainsi que le CNAME pour les sous-domaines. Cela fonctionne. 👍

AdGuardHome

Contrairement aux autres services hébergés sur https://spirio.fr, les requêtes IPv6 DNS TCP:53/UDP:53 et UDP:853 sont nattés directement vers le conteneur AdGuardHome. Cela nécessite que le conteneur ait une adresse IPv6 et écoute sur les ports TCP:53/UDP:53 et TCP:853.

Podman

Le déploiement des Podman se base sur des fichiers de configuration Quadlet. Chaque conteneur a un réseau qui lui est propre et qui l’isole des autres conteneurs. En IPv4, il existe différentes plages réseaux non-adressables sur internet.

RFC 1918 nameIP address rangeNumber of addressesLargest CIDR block (subnet mask)Host ID sizeMask bitsClassful descriptionNote 1(https://en.wikipedia.org/wiki/Private_network#cite_note-3)
24-bit block10.0.0.0 – 10.255.255.2551677721610.0.0.0/8 (255.0.0.0)24 bits8 bitssingle class A network
20-bit block172.16.0.0 – 172.31.255.2551048576172.16.0.0/12 (255.240.0.0)20 bits12 bits16 contiguous class B networks
16-bit block192.168.0.0 – 192.168.255.25565536192.168.0.0/16 (255.255.0.0)16 bits16 bits256 contiguous class C networks

source Wikipedia Il existe la même chose en IPv6.

RFC 4193 BlockPrefix/LGlobal ID (random)Subnet IDNumber of addresses in subnet
48 bits16 bits64 bits
fd00::/8fdxx:xxxx:xxxxyyyy18446744073709551616

source Wikipedia Ce qui donnera par exemple comme adresse de réseau : fd00:10:0::/64 et comme adresse IPv6 pour le conteneur : fd00:10:0::2/64.

Modifions maintenant le fichier .network afin d’activer l’adressage IPv6 et lui attribuer l’adresse réseau IPv6.

[Unit]
Description=Adguardhome Container Network

[Network]
Subnet=10.80.00.1/24
DisableDNS=true

IPv6=true
Subnet=fd00:10:80::/64
[Install]
WantedBy=default.target

Modifions maintenant le fichier .container afin d’activer l’adressage IPv6 et lui attribuer l’adresse réseau IPv6.

# adguardhome.container
...

[Container]                                                                                                                                             
IP6=fd00:10:80::2

...

Il ne reste plus qu’à recharger la configuration, redémarrer le conteneur et vérifier qu’il dispose bien d’une interface IPv6.

sudo systemctl daemon-reload
sudo systemctl restart adguardhome

podman inspect adguardhome|grep GlobalIPv6Address
   "GlobalIPv6Address": "",
			 "GlobalIPv6Address": "fd00:10:80::2",

podman network inspect systemd-adguardhome|grep ipnet
	"ipnet": "10.80.0.1/24",
	"ipnet": "fd00:10:80::1/64",

Configuration d’AdGuardHome

Initialement, j’avais configuré AdGuardHome afin qu’il écoute sur l’interface du conteneur, à savoir 10.80.0.2. AdGuardHome peut écouter sur plusieurs interface, la documentation l’explique très bien. Initialement, je l’avais configuré pour écouter spécifiquement sur l’interface avec l’adresse IP du conteneur. Au lieu d’ajouter l’interface IPv6, ce qui fonctionne, j’ai préféré mettre l’adresse 0.0.0.0 qui permet d’écouter sur toutes les interfaces. Cela simplifie la configuration et la rend plus résiliente aux changements d’adresses IP du conteneur. Ce qui nous donne :

  # AdGuardHome.yaml
  bind_hosts:                                                                                                                                           
    - 10.80.0.2
# Devient
  bind_hosts:                                                                                                                                           
    - 0.0.0.0

On redémarre le conteneur AdGuardHome puis on regarde si le conteneur écoute bien sur les deux interfaces en IPv4 et en IPv6.

sudo systemctl restart adguardhome

# IPv4
nmap 10.80.0.2 -p 53,443,853
PORT    STATE SERVICE
53/tcp  open
443/tcp open        https
853/tcp open domain-s

nmap 10.80.0.2 -p U:53
53/udp open  domain

# IPv6
nmap -6 fd00:10:80::1 -p 53,443,853
PORT    STATE SERVICE
53/tcp  open
443/tcp open        https
853/tcp open domain-s

nmap -sU -6 fd00:10:80::1 -p 53
53/tcp  open

Vérifications de la résolution DNS

On va maintenant vérifier que le résolveur DNS fonctionne et répond aux requêtes DNS en TCP/UDP/DOH/DOT.

# Résolution DNS en UDP:53 en IPv6
dig -6 +short @2001:41d0:305:2100::b5fa google.fr A
172.217.22.35

# Résolution DNS en TCP:53 en IPv6
dig -6 +short +tcp @2001:41d0:305:2100::b5fa google.fr A
172.217.22.35

# Résolution DNS en DOH TCP:443 en IPv6
kdig +short @2001:41d0:305:2100::b5fa +tls-ca +tls-host=dns.spirio.fr google.fr
172.217.22.35

# Résolution DNS en DOT TCP:853 en IPv6
dig -6 +short +tls @2001:41d0:305:2100::b5fa google.fr A
172.217.22.35

Cela fonctionne. 🥳

Conclusion

Les services accessibles sur https://spirio.fr répondent désormais aux requêtes en IPv6 ! Il existe surement d’autres actions à réaliser afin que tout soit conforme, mais comme le mieux est l’ennemi du bien, je pense m’arrêter là pour le moment. Fait rigolo, j’ai passé plus de temps à rédiger cet article qu’à mettre en place l’adressage en IPv6. J’espère que cet article vous aura plu. Au plaisir de lire vos commentaires.

Spirio


Commentaires

2 réponses à “Déploiement de l’IPv6 sur Spirio.fr”

  1. Avatar de
    Anonyme

    Bonjour,

    Pour un non expert, c’est plutôt bien 😉

    Trois petites remarques cependant.

    À ma connaissance nftables n’exige pas de mettre les adresses Ipv6 entre crochets.

    Apache2 écoute par défaut en IPv4 et en IPv6 sur la plupart de distributions et la modification de hôtes virtuels est inutile (cf la directive globale Listen 80 dans ports.conf sous Debian)

    Je ne vois pas la configuration des enregistrement DNS pour mettre en correspondance ton nom de domaine et ton adresse IPv6 (et leur vérification avec p.ex. dig +short AAAA spirio.fr)

    1. Bonjour,

      À ma connaissance nftables n’exige pas de mettre les adresses Ipv6 entre crochets.

      Je viens de tester et tu as raison, Nftables ne l’exige pas. Avant mes tests, j’avais désactivé l’adressage IPv6 au niveau du noyau, je pense que l’erreur que j’avais venait de là. Pour les adresses IPv6 sur le NAT, je trouve que cela facilite la lecture de mettre le crochet, notamment vis à vis du port. Je trouve qu’écrire [fd00:10:xx::x]:53 permet plus facilement identifié le port que fd00:10:xx::x:53 même si les deux écritures sont valides.

      Apache2 écoute par défaut en IPv4 et en IPv6 sur la plupart de distributions et la modification de hôtes virtuels est inutile (cf la directive globale Listen 80 dans ports.conf sous Debian)

      J’ai regardé sur un serveur sans modification des VirtualHost et le serveur Apache HTTPD écoute bien également en IPv6, et l’absence de modification du VirtualHost ne l’empêche pas de fonctionner en IPv6 .Merci !

      Je ne vois pas la configuration des enregistrement DNS pour mettre en correspondance ton nom de domaine et ton adresse IPv6 (et leur vérification avec p.ex. dig +short AAAA spirio.fr)

      J’ai oublié de le mentionner, mais j’ai bien fait les ajouts des champs DNS de type AAAA.

      Merci pour ton commentaire très instructif ! Je vais modifier l’article en conséquence.

Répondre à Anonyme Annuler la réponse

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *