NAT en épingle: pourquoi le point de terminaison VPN échoue depuis l'intérieur
Un client local qui utilise l'adresse publique quitte le LAN et y revient. Quand le routeur ne fait pas de hairpin, l'extérieur fonctionne et l'intérieur non.
Un serveur VPN qui répond depuis un réseau mobile et échoue depuis le canapé n'est généralement pas un problème de certificat. Le client à l'intérieur du LAN demande au routeur de traduire un paquet dont la destination est l'adresse publique du routeur lui-même, ce qui signifie que le paquet doit sortir et revenir aussitôt. Cette manœuvre s'appelle le hairpinning, et beaucoup de routeurs soit ne l'implémentent pas, soit ne le font que lorsqu'une case est cochée. C'est le succès du test depuis l'extérieur qui rend la chose déroutante, car il prouve que l'écouteur et la règle de redirection sont corrects.
Le paquet sort et revient
Suivez le paquet. Le client résout le nom public, se connecte à l'adresse publique, et le NAT de destination du routeur réécrit la destination vers le serveur interne. La réponse du serveur est adressée à l'adresse LAN du client, elle repart donc par l'interface LAN du routeur sans jamais traverser la règle NAT qui aurait retraduit la source.
Maintenant le client reçoit un paquet d'une adresse à laquelle il n'a jamais rien envoyé, et le jette. Cette asymétrie est toute la panne.
La RFC 4787 décrit le comportement attendu d'un NAT dans ce cas, et la version courte est qu'il est facultatif en pratique même là où il est spécifié en théorie. Certains routeurs nomment le réglage NAT loopback, d'autres hairpin NAT ou NAT reflection, et certains n'exposent rien du tout. Un routeur qui en est dépourvu n'est pas mal configuré; il fait la chose ordinaire, et le correctif appartient à ailleurs. Ne concluez pas d'un test local raté que la règle de redirection publique est fausse.
Vérification 1: testez les deux chemins séparément
Depuis l'extérieur du LAN, connectez-vous avec le même profil qu'utiliserait un vrai utilisateur et confirmez que la session s'établit. Depuis l'intérieur, connectez-vous à l'adresse publique avec le même profil et notez l'erreur exacte et l'heure.
Un test local précis conserve le nom d'hôte et ne remplace que l'adresse: curl -v --resolve vpn.example.com:443:203.0.113.10 https://vpn.example.com/ envoie la requête à l'adresse publique tout en présentant le bon nom de serveur. Cela sépare un problème de résolution de nom d'un problème de traduction, faciles à confondre quand le même nom fonctionne depuis un café. Notez si le client échoue à la connexion TCP ou plus tard en TLS, car le premier indique la traduction et le second l'identité.
Vérification 2: lisez ce que voit le serveur
L'observation la plus utile est la vue du serveur. Lancez une capture sur l'hôte VPN filtrée sur l'adresse LAN du client et sur celle du routeur, puis répétez la tentative locale. Si rien n'arrive, le paquet n'a pas survécu à la traduction.
Si une connexion arrive depuis l'adresse LAN du routeur plutôt que depuis celle du client, le hairpinning a lieu et la source a été réécrite. C'est une configuration qui fonctionne, pas une panne.
Vérification 3: corrigez sans toucher au chemin externe
Il existe trois formes prises en charge, par ordre croissant de rayon d'action. La plus étroite est le DNS partagé: publiez un enregistrement interne pour que le même nom de serveur résolve vers l'adresse interne pour les clients du LAN, ce qui évite entièrement le chemin public. La suivante est d'activer l'option documentée de loopback ou de reflection du routeur, si elle existe, et de ne l'appliquer qu'à ce service. La plus large est une règle source-NAT sur le routeur qui réécrit le trafic issu du LAN vers le serveur afin que la réponse revienne par le routeur; sur un routeur Linux, c'est une règle MASQUERADE dans POSTROUTING qui correspond à l'adresse et au port du serveur. Les trois laissent le chemin externe intact, et c'est la propriété à préserver.
N'élargissez pas une redirection de port sans rapport pour que le chemin local ressemble au chemin externe, et ne déplacez pas le service vers un autre port pour contourner le problème. Les deux changements modifient ce que le monde extérieur peut atteindre pour corriger quelque chose que seul le monde intérieur voit.
Le DNS partagé a un piège qui mérite d'être nommé. Remplacer un nom d'hôte par une IP arbitraire change ce que le client valide, donc l'enregistrement interne doit toujours correspondre au nom du certificat.
Vérification 4: vérifiez les deux chemins ensuite
Répétez la tentative externe depuis l'extérieur du LAN, répétez la tentative interne avec le même profil, et confirmez que le journal du serveur montre deux sessions réussies distinctes plutôt qu'un succès et un silence. Vérifiez ensuite que les autres clients atteignent toujours l'écouteur et que les routes vers les ressources privées sont inchangées.
Gardez la trace courte: quelle adresse chaque client a utilisée, ce que le serveur a vu et laquelle des trois corrections a été appliquée. Les exigences de hairpinning de la RFC 4787 sont la référence de ce qu'on attend d'un NAT ici, et elles expliquent pourquoi un routeur dépourvu de la fonction se comporte normalement. Une panne locale que le monde extérieur ne peut pas reproduire est une question de traduction jusqu'à preuve du contraire, alors testez-la comme telle.