Un déménagement. Une idée simple quand on y pense: il suffit de déplacer des choses d’un point A vers un point B. Dans les faits, des objets à ranger dans des cartons en vu du transport, des meubles à vider, à déplacer, de l’aide à trouver (un meuble même vide, pèse son poids), le tout à planifier et à ordonnancer pour terminer à temps. Et bien sûr, au milieu de tout ça, des ordinateurs, switchs et router, bref, l’infrastructure qui héberge mon lot de service à arrêter, déplacer et à reconnecter ailleurs, possiblement dans un nouveau réseau. Au moment où j’écris ces lignes, pas loin de 24 mois se sont écoulés depuis que j’ai eu à déplacer mon homelab.
Occupé par l’organisation de mon déménagement, je n’avais pas pris le temps d’anticiper le déplacement de mon infra, en la migrant temporairement dans une machine virtuelle chez un hébergeur. Bien qu’ayant automatisé la majeure partie du déploiement, je savais que cela nécessiterait plusieurs vérifications, notamment que les processus de sauvegarde se soient bien remis en place. Même chose pour le renouvellement des certificats, sans parler des adaptations nécessaires sur les scripts ansible, si j’en avais profité pour passer sur une version plus récente de Debian. Bref, pas suffisamment de temps à dédier au sujet. J’ai donc attendu jusqu’au dernier moment, avant d’arrêter mon homelab et de déplacer ses composants, avec bien entendu, un temps d’indisponibilité dans l’intervalle, jusqu’à reconnecter le tout dans le nouveau réseau.
La bonne nouvelle, c’est que tout a redémarré correctement, pas de casse pendant le déplacement, pas de stockage corrompu, tout a fonctionné à merveille. La mauvaise, c’est que le réseau d’arrivée comportait des nouveautés ! Le réseau que je quittais était entièrement sous mon contrôle. En effet, j’avais réussi à utiliser mon propre router à la place de la livebox du FAI et je disposais alors d’une IPv4 dédiée. Dynamique, mais relativement fixe; avec un dynamique DNS bien configuré pour pouvoir contacter mes services, même en cas de changement d’IP. En changeant de réseau, je me retrouvais à devoir exposer mon homelab derrière une box de FAI et, oh surprise, j’ai vu arriver de l’IPv6, et découvert que le point d’entrée dans le réseau, la box du FAI, ne disposait pas d’une adresse IPv4 pour son seul usage, mais d’une IPv4 partagée entre plusieurs box. Cette technique portant le nom de CGNAT pour Carrier-Grade Network Address Translation.
Impossible donc de contacter mes services en v4, j’ai adapté mes plans pour faire fonctionner le tout en v6. Après quelques essais, la connectivité vers mon homelab était rétablie. Tout aurait pu s’arrêter là si je n’avais pas constaté, quelques jours plus tard, que mes services étaient inaccessibles depuis un réseau que j’utilisais régulièrement durant cette période. La raison la plus probable: pas d’IPv6 sur ce réseau et aucun mécanisme de compatibilité entre v6 et v4. Mes cours de réseaux sur IPv6 semblaient bien loin. Ni une, ni deux, j’ai donc enfourché mon renard de feu à la recherche de solutions à mon problème.
Après plusieurs lectures pour comprendre ce fameux CGNAT, je suis arrivé à la conclusion qu’il allait me falloir un point relais capable de recevoir du trafic en IPv4 et de le transférer vers mon réseau via IPv6. Je me suis alors souvenu que Google, dans sa plateforme cloud, propose des network load balancers intervenant au niveau de la couche TCP, soit bien plus bas que le proxy dont je me sers dans mon infrastructure pour faire de la terminaison SSL sur du trafic HTTPS. En continuant mes recherches autour de la notion de proxy passthrough, j’ai découvert que je pouvais configurer un serveur HAProxy, de telle sorte qu’il puisse recevoir du trafic HTTPS en IPv4 et IPv6 et le rediriger vers une adresse IPv6, sans que celui-ci ait besoin de toucher à la couche HTTPS, et donc sans avoir à reconfigurer les certificats de mes noms de domaines au niveau de ce proxy. Une fois connu, cela semble évident.
Au final, la solution aura été relativement simple et rapide à mettre en place. Commander un petit VPS disposant d’une IPv4, modifier les DNS pour faire pointer les noms de domaines vers ce serveur pour le trafic en v4, installer HAProxy et le configurer en mode proxy passthrough. Pour le trafic en v6, on peut au choix utiliser le même proxy, ou déclarer directement l’IPv6 du serveur cible dans les DNS. Cette solution m’aura donc permis de rétablir rapidement l’accès à mes services quel que soit le réseau (v4 ou v6), et surtout, avec pratiquement pas de modification du côté de mon infrastructure auto-hébergé.
Pour terminer, voici la configuration utilisée au niveau de HAProxy (/etc/haproxy/haproxy.cfg) pour mettre en œuvre la solution trouvée:
global
log /dev/log local0
log /dev/log local1 notice
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin
stats timeout 30s
user haproxy
group haproxy
daemon
# Default SSL material locations
ca-base /etc/ssl/certs
crt-base /etc/ssl/private
# See: https://ssl-config.mozilla.org/#server=haproxy&server-version=2.0.3&config=intermediate
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5000
timeout client 50000
timeout server 50000
errorfile 400 /etc/haproxy/errors/400.http
errorfile 403 /etc/haproxy/errors/403.http
errorfile 408 /etc/haproxy/errors/408.http
errorfile 500 /etc/haproxy/errors/500.http
errorfile 502 /etc/haproxy/errors/502.http
errorfile 503 /etc/haproxy/errors/503.http
errorfile 504 /etc/haproxy/errors/504.http
frontend www_https
bind *:443 v4v6
mode tcp
option tcplog
default_backend backend_servers
backend backend_servers
mode tcp
# balance roundrobin
option ssl-hello-chk
# TODO: replace ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff with the target IPv6
server server1 [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:443 verify none
frontend http_in
bind :::80 v4v6
option forwardfor
default_backend http_back
backend http_back
# TODO: replace ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff with the target IPv6
server server2 [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:80
Sources: