Richard Dern

Free, IPv6 et OPNsense

Free, IPv6 et OPNsense

Contexte

Bien conscient de la pénurie d’adresses IPv4 depuis très longtemps, cela fait des années que j’essayais de mettre en place IPv6 sur mon réseau, sans que j’y parvienne. Et très honnêtement, c’est largement à cause de ma méconnaissance des implications techniques d’IPv6 qui me faisait craindre pour la sécurité de mon réseau si je m’y prenais mal.

J’ai finalement décidé de donner les clés de mon château (OPNsense) à ChatGPT, qui s’est occupé de tout pour moi 1. Et je ne regrette pas un instant de l’avoir fait : il a tout mis en place, et tout fonctionne parfaitement bien.

Objectifs

Mes objectifs sont de fournir la double connectivité IPv4 et IPv6 aux clients de mon LAN, et que mes serveurs et services soient joignables en IPv4 ou IPv6.

Mon réseau

Je le détaille dans cet article, mais en substance :

J’ai installé le plugin caddy sur OPNsense : il fait office de reverse-proxy pour tout service que je veux exposer. Les services qui ne passent pas par un serveur web font l’objet de règles de NAT.

Mise en place

Par souci de confidentialité, j’ai anonymisé les adresses IP et les identifiants matériels dans les extraits et les captures d’écran.

Obtention de l’adresse de lien local depuis l’interface d’OPNsense. C’est la deuxième adresse de la ligne IPv6 Addresses, qui commence toujours par fe80.

Freebox

Dans l’interface de configuration de la Freebox, on active le Next Hop IPv6 du premier préfixe secondaire en indiquant l’adresse lien local du routeur.

Une adresse IPv6 de lien-local (préfixe fe80::/10) est une adresse valable uniquement sur le lien où elle a été émise, et qui n’est pas routée sur Internet. Une machine a une adresse de lien-local par interface, ce qui explique pourquoi on parle de l’adresse lien-local de l’interface WAN.

Un préfixe IPv6 (notation .../64) désigne un bloc d’adresses, l’équivalent d’un réseau en IPv4.

Le Next Hop (le “prochain saut”) revient à indiquer à la Freebox vers quel voisin elle doit envoyer les paquets destinés à ce préfixe.

On reporte alors cette adresse de lien local dans le premier Préfixe secondaire de la Freebox

Mon erreur était de renseigner l’adresse lien local de l’interface WAN de mon routeur comme Next Hop du préfixe principal, et l’adresse lien local de l’interface LAN de mon routeur dans le Next Hop du premier préfixe secondaire. Je me suis laissé influencer par ce post, dont je n’ai lu que les images et pas le texte…

Il n’y a rien de plus à faire sur la Freebox.

En pratique, je devrais activer le serveur DHCPv6 pour que mon serveur se voit attribuer une adresse fixe, mais pour le moment, je veux juste m’assurer que tout fonctionne avant d’activer des options.

OPNsense

C’est à partir de là que ChatGPT a pris le contrôle de mon routeur.

Je tiens à souligner que je l’ai laissé en totale autonomie après lui avoir donné mes instructions, et qu’à aucun moment je n’ai subi de déconnexion intempestive. Il a assuré, du début à la fin, en procédant à des sauvegardes avant chaque commande potentiellement destructive. Ma session de codex me permettait de voir en direct toutes les commandes qu’il exécutait, d’où la confiance que je lui ai accordée. Les commandes utilisées pour la vérification sont données plus loin à titre informatif, mais ces vérifications peuvent aussi être effectuées directement dans l’interface web de OPNsense.

Je précise aussi que je ne sauve pas des vies : si mon blog est hors-ligne pendant une grosse demi-heure (le temps de restaurer ma configuration initiale), ça ne va gêner personne, en particulier en plein milieu de la nuit.

Activation IPv6 WAN/LAN

Dans Interfaces, [WAN], on active le mode SLAAC pour l’interface WAN.

SLAAC (Stateless Address Autoconfiguration) est le mécanisme standard d’auto-configuration IPv6 à partir des messages Router Advertisement. Je le choisis sur le WAN car la Freebox annonce déjà ce qu’il faut (route par défaut et préfixe), et cela simplifie le diagnostic tant que la connectivité de base n’est pas validée.

Dans Interfaces, [LAN], on configure une IPv6 statique pour l’interface LAN dans le préfixe secondaire. Traditionnellement, c’est le préfixe IPv6, suivi par ::1 (un peu comme 10.0.0.1 en IPv4).

Et on applique la configuration.

Router Advertisements (RA) sur LAN

Dans Services, Router Advertisements, on active RA pour le LAN.

Les Router Advertisements (RA) sont des messages envoyés par le routeur aux clients pour annoncer la passerelle par défaut, les préfixes disponibles, et les indicateurs liés à DHCPv6. Sans RA, un client ne peut pas apprendre sa route par défaut IPv6, même si un serveur DHCPv6 est présent.

Les modes de RA d’OPNsense servent essentiellement à indiquer aux clients s’ils doivent utiliser SLAAC, DHCPv6, ou une combinaison des deux. Ils correspondent à des combinaisons de drapeaux standards des RA, notamment AdvManagedFlag (le drapeau M) et AdvOtherConfigFlag (le drapeau O), ainsi que le drapeau “autonomous” associé au préfixe qui autorise SLAAC. La documentation d’OPNsense récapitule précisément ces modes et leurs drapeaux.

Dans un premier temps, on le configure en Unmanaged. Une fois DHCPv6 (Kea) activé et le DNS correctement publié, je basculerai en Assisted afin que RA et DHCPv6 puissent cohabiter sans ambiguïté (expliqué plus bas).

DHCPv6 (Kea) sur LAN

Dans Services, Kea DHCP, Kea DHCPv6, on active DHCPv6 dans Kea pour le LAN.

On enregistre le subnet (celui du préfixe pour lequel on a ajouté un Next Hop dans la Freebox).

Je prévois ici d’utiliser l’adresse ...::53 pour mon serveur DNS, avec fallback sur le serveur DNS du routeur (...::1).

Réservation IPv6 stable pour le serveur DNS

À ce stade, j’ai demandé à ChatGPT de fiabiliser l’adresse IPv6 de mon serveur DNS. Il s’agit de récupérer son DUID depuis les baux déjà attribués dynamiquement et de l’ajouter aux réservations. C’est le même principe que pour IPv4, sauf que le DUID est plus fiable que l’adresse MAC.

Après un redémarrage de mon pi-hole (dns-1), j’obtiens bien l’adresse en ...::53 qui a été configurée dans les baux statiques.

Mise à jour du RA

Dans Services, Router Advertisements, on peut maintenant passer au mode Assisted.

Vérifications techniques

Connectivité IPv6 depuis dns-1

dns-1 a bien reçu l’IPv6 réservée :

inet6 ...::53/128 scope global dynamic noprefixroute
Résolution DNS via dns-1 en IPv6

Depuis OPNsense :

drill @...::53 one.one.one.one AAAA

Résultat attendu : réponse NOERROR avec des AAAA (ex. : Cloudflare).

Vérifier les annonces RA
grep -nE 'RDNSS|AdvManagedFlag|AdvOtherConfigFlag' /var/etc/radvd.conf

Résultat attendu :

Vérifier les annonces DHCPv6
sed -n '30,55p' /usr/local/etc/kea/kea-dhcp6.conf

Résultat attendu :

Vérifier les règles IPv6 WAN (phase LAN uniquement)

Ici, “phase LAN uniquement” signifie que je valide d’abord l’IPv6 sortant pour les machines du LAN, sans encore exposer de services en IPv6 vers Internet. Je m’attends donc à ne voir, sur WAN, que les règles indispensables au fonctionnement côté routeur, et aucune règle pass destinée à des services.

pfctl -sr | awk 'tolower($0) ~ /pass/ && $0 ~ / on igc1 / && tolower($0) ~ /inet6/ {print}'

Résultat attendu : uniquement les règles DHCPv6 client.

À ce stade, n’importe quel client devrait pouvoir faire un test d’IPv6 et obtenir un 10/10.

Publication d’un site web en IPv6

Créer le AAAA public

Ajouter une entrée AAAA chez mon registrar (OVH) avec l’adresse IP globale de l’interface WAN obtenue depuis la Freebox. Cette adresse IP est dans le préfixe principal (celui dont le Next Hop reste vide).

Ouvrir le firewall WAN en IPv6 pour le web

Ajouter l’équivalent IPv6 des règles IPv4 existantes, autrement dit :

Rien de compliqué ici, c’est rigoureusement la même chose que pour IPv4.

Recharger et vérifier

Recharger le filtre :

configctl filter reload

Vérifier les règles actives :

pfctl -sr | grep -E 'inet6 proto tcp.*port = http|inet6 proto tcp.*port = https'

Résultat attendu : deux règles pass in WAN en IPv6 pour 80 et 443.

Vérifier la résolution DNS AAAA :

drill @1.1.1.1 AAAA richard-dern.fr

Résultat attendu :

Vérifier l’accès HTTPS en IPv6 :

curl -6 -I https://richard-dern.fr

Résultat attendu : réponse HTTP/2 200 (ou redirection HTTP vers HTTPS selon la politique du site).

IRC en IPv6 : NAT ou routage ?

En IPv4, la publication d’un service passe très souvent par une redirection de port (NAT) depuis l’IP publique du routeur vers une IP privée du serveur. En IPv6, un serveur du LAN peut avoir une adresse globale routable, et la publication devient principalement une question de DNS (AAAA) et de filtrage firewall, pas de traduction d’adresse.

Autrement dit, ce qui “protège” le LAN n’est pas le NAT, mais le fait que, par défaut, rien n’est autorisé en entrée tant qu’aucune règle pass n’est ajoutée sur WAN. Dans certains cas, on peut néanmoins conserver une logique de redirection IPv6 -> IPv6 (NAT66), par exemple quand un même nom de domaine doit pointer vers le routeur pour le web (reverse-proxy) tout en exposant d’autres ports vers un hôte interne.

Dans mon cas, je veux que irc.dern.ovh pointe vers le routeur pour servir le client web via Caddy, et je redirige les ports IRC vers server-main (la machine sur laquelle le serveur IRC est hébergé).

Pré-requis côté hôte (server-main sous NixOS)

Activer IPv6 dans la config NixOS de l’hôte :

{
  networking = {
    enableIPv6 = true;
    tempAddresses = "disabled";
  };

  systemd.services.dhcpcd.serviceConfig.SystemCallFilter = lib.mkForce [
    "@system-service"
    "~@aio"
    "~@keyring"
    "~@memlock"
    "~@mount"
    "~@resources"
  ];
}

Ces lignes sont un ajustement spécifique à ma configuration, et non un prérequis générique à IPv6. Dans mon cas, dhcpcd était trop durci côté systemd, ce qui l’empêchait d’appliquer correctement la configuration réseau (adresses et routes). Forcer un SystemCallFilter compatible a permis à dhcpcd de fonctionner, tout en conservant des restrictions sur des familles d’appels système inutiles ici (comme @mount).

Mise en place

J’ai affecté un nom de domaine à l’usage d’IRC : irc.dern.ovh. Or, cet usage est double :

C’est un cas d’usage idéal pour justifier le NAT en IPv6. Donc, lorsque j’ai créé une entrée DNS AAAA pour irc.dern.ovh, je l’ai fait pointer non pas vers mon serveur IRC mais vers mon routeur, puis j’ai créé une règle de NAT pour le serveur IRC. De cette façon, le reverse-proxy répond pour servir le client web, et on NAT vers le serveur IRC quand on cherche à joindre le port approprié.

Conclusion

Je le redis : j’ai laissé les clés du château à ChatGPT. Je lui ai fait confiance, et j’ai eu raison de le faire. Il m’a épargné des jours de prises de tête pour une tâche face à laquelle j’ai souvent échoué.

En outre, au cours de nos échanges, il m’a expliqué ce que je ne comprenais pas. Et comme je suis chiant, je lui ai posé plein de questions.

Maintenant, j’ai un réseau IPv6 fonctionnel et j’ai compris ce que j’ai fait. Merci ChatGPT, et merci à ceux qui ont permis que ces informations se retrouvent dans ce qu’il m’a proposé.


  1. J’ai créé un utilisateur sur mon serveur principal à usage exclusif de ChatGPT-5.3-Codex, créé une paire de clés envoyée à mon routeur, et démarré une session codex à partir de laquelle ChatGPT pouvait se connecter en root via SSH vers le routeur. Une fois qu’il a accompli ses objectifs, il n’y avait plus qu’à supprimer l’utilisateur dédié et les clés SSH associées sur le routeur. ↩︎