Pourquoi les noms d’hôtes internes se résolvent avec dig mais échouent avec curl sur un VPN
dig répond, mais curl signale qu’il ne peut pas résoudre l’hôte. Comparez les deux chaînes de résolution, puis corrigez nsswitch, le DNS par lien ou un suffixe de recherche erroné.
Votre ordinateur portable est sur le VPN, dig service.internal.example répond avec une adresse en 10.x, et curl se termine toujours par Could not resolve host. Le tunnel n’est pas le problème : il a transporté la requête dig sans se plaindre. Ce qui change, c’est qui pose la question. dig envoie une requête à un serveur que vous nommez, tandis que curl passe par la chaîne de résolution du système d’exploitation, et cette chaîne a ses propres règles sur le serveur à interroger.
Deux résolutions pour le même nom
dig est un stub resolver à part entière. Il lit /etc/resolv.conf, choisit un serveur de noms, envoie une seule requête A ou AAAA et affiche la réponse. Il ignore complètement nsswitch et ne sait rien de l’état par lien que systemd-resolved ou NetworkManager conservent en dehors de ce fichier, ce qui explique qu’une réponse dig puisse être correcte sans rien vous dire de ce que votre application verra.
Les applications appellent getaddrinfo, et getaddrinfo parcourt une chaîne plus longue : l’ordre des modules de nsswitch, puis le module qui traite le nom, puis le résolveur construit à partir de la configuration de lien courante. Sur une machine systemd, la requête part vers 127.0.0.53 et systemd-resolved choisit un serveur amont par lien selon le domaine de routage attaché à l’interface tun.
Deux commandes séparent ces couches sans deviner :
getent ahosts service.internal.example
resolvectl query service.internal.example
getent emprunte le même chemin que curl, donc un échec de getent à côté d’un dig qui fonctionne localise la panne dans la politique de résolution plutôt que dans le routage des paquets. resolvectl query affiche aussi l’interface et l’amont qui a fourni la réponse, ce qui montre si le lien du tunnel ou le lien physique a gagné la décision.
Lire le résolveur que l’application utilise réellement
/etc/resolv.conf est généré sur la plupart des postes de travail, et un client VPN a trois façons d’interagir avec lui : ajouter un serveur de noms en tête, écraser le fichier, ou confier ses serveurs à NetworkManager en laissant le démon le réécrire. Seul le troisième schéma préserve le DNS par lien, parce que les serveurs restent attachés à une interface au lieu d’être aplatis dans une liste globale.
Sur les systèmes NetworkManager, le résultat dépend du backend dans /etc/NetworkManager/NetworkManager.conf :
[main]
dns=systemd-resolved
Avec dns=default, les serveurs du client sont fusionnés dans resolv.conf dans l’ordre qui l’emporte, et une résolution privée peut encore être envoyée au résolveur public. Avec un backend qui comprend les liens, systemd-resolved ou dnsmasq, les serveurs du tunnel restent limités au tunnel et seuls les noms correspondant à son domaine de routage y vont. Cette limitation est ce que signifie le split DNS en pratique, et c’est la pièce qui manque le plus souvent quand un client prétend le prendre en charge.
Lisez l’état avant de modifier quoi que ce soit :
resolvectl status
resolvectl dns tun0
Vous voulez voir l’interface tun listée avec un serveur DNS et un domaine de routage pour le suffixe privé, affiché sous la forme ~internal.example. Aucun serveur sur le lien signifie que le client n’a rien poussé. Un serveur sans domaine signifie que les résolutions y sont toutes envoyées ou aucune, selon le client, et ni l’un ni l’autre n’est du split DNS.
Là où nsswitch interrompt la résolution
Une ligne de poste standard ressemble à ceci :
hosts: files mdns4_minimal [NOTFOUND=return] resolve [!UNAVAIL=return] dns myhostname
mdns4_minimal gère le DNS multicast et revendique tout nom se terminant par .local. Un service Kubernetes comme api.infra.svc.cluster.local se termine aussi par .local. Quand le module mDNS signale NOTFOUND, l’action [NOTFOUND=return] met fin à toute la résolution au lieu de continuer vers le module dns, donc le nom n’est jamais envoyé au résolveur du VPN. dig contourne entièrement le fichier, et c’est exactement pourquoi les deux outils ne sont pas d’accord.
C’est le contraste le plus net de ce problème, et il tient à un seul mot dans un fichier de configuration plus ancien que votre VPN. Passer l’action à [NOTFOUND=continue] conserve mDNS pour les imprimantes et les appareils locaux tout en laissant DNS voir le nom. Remplacer la ligne par hosts: files dns fonctionne aussi et c’est plus brutal : cela supprime la résolution .local pour l’imprimante que vous utilisez réellement.
Les noms qui n’existent que dans un seul réseau
Les zones privées ne font autorité qu’à un seul endroit. Un nom AWS tel que ip-10-0-4-31.eu-west-1.compute.internal est résolu par le résolveur VPC de cette région, et seulement tant que votre point de terminaison VPN se trouve dans la même région que la ressource. Connectez-vous à un point de terminaison de Francfort, interrogez un nom compute.internal d’Irlande, et aucun réglage local n’aidera : la zone qui détient l’enregistrement n’est pas joignable depuis là.
Les domaines de recherche produisent l’image inversée. Un simple service est complété par le suffixe que le résolveur juge applicable, et avec un lien VPN le domaine local peut l’emporter sur celui de l’entreprise. Interrogez avec un point final pour éliminer l’incertitude, getent ahosts service.internal.example., et si cela fonctionne alors que le nom court échoue, la correction est le domaine de recherche sur le tunnel, et non l’adresse du serveur.
Les clients gérés ajoutent une troisième couche au-dessus du résolveur du système. Un profil VPN Windows peut porter sa propre politique de résolution de noms, des règles par application et une table NRPT qui s’applique même quand le tunnel est coupé. Les applications avec leur propre cache DNS, courantes dans les runtimes .NET et Java, conservent un échec obsolète longtemps après la réparation de la chaîne. Les clients Apple configurent le split DNS via des domaines correspondants dans les réglages DNS plutôt que via resolv.conf, et Chromium ou Firefox en mode DNS sécurisé peuvent contourner le résolveur système pour les noms publics. La référence Microsoft sur la résolution de noms VPN décrit le côté Windows de cette couche de politique.
Décider quelle couche corriger
Partez du symptôme vers la couche au lieu d’essayer les correctifs dans l’ordre :
| Symptom | What it means | Check first |
|---|---|---|
| dig answers, getent fails | lookup chain, not the packet path | nsswitch line and resolvectl status |
| Timeout rather than NXDOMAIN | private resolver unreachable through the tunnel | resolvectl dns tun0, route to that address |
| NXDOMAIN | an authority answered and does not know the name | zone owner, endpoint region |
| FQDN works, short name fails | search suffix mismatch | resolvectl domain on the tun link |
| getent works, app fails | app cache or its own resolver | restart the app, check secure DNS |
Deux lignes portent l’essentiel. Un délai d’attente est un problème de joignabilité : le résolveur privé ne répond pas à travers le tunnel, donc vérifiez la route vers son adresse et si le serveur DNS du lien est bien celui que vous croyez. NXDOMAIN est un problème d’autorité : quelque chose a répondu et ne connaît pas le nom, ce qui pointe vers la zone, la région, ou un résolveur qui n’aurait jamais dû recevoir la requête.
La dernière ligne est celle que les gens manquent parce que les deux moitiés ressemblent à un succès. Un nom court qui se résout différemment du FQDN est un décalage de suffixe, et reconnecter le tunnel ne changera pas le suffixe poussé par le client.
Capturez l’état avant de modifier un fichier de configuration, afin de pouvoir dire si la modification a changé quelque chose :
resolvectl status > before.txt
getent ahosts service.internal.example >> before.txt
Exécutez les vérifications dans cet ordre et arrêtez-vous à la première qui échoue. getent et resolvectl query doivent concorder ; si getent échoue alors que dig répond, restez dans la chaîne de résolution et ne touchez pas au routage. resolvectl status doit afficher un serveur DNS et un domaine de routage sur l’interface tun. nsswitch ne doit pas s’arrêter trop tôt sur NOTFOUND pour le suffixe testé. Le nom doit faire autorité dans le réseau auquel votre point de terminaison est rattaché, testé avec un point final. L’application ne doit pas garder son propre cache ni un contournement DNS sécurisé.
Ne corrigez que la première couche défaillante, puis relancez les mêmes commandes. Deux changements à la fois vous empêchent de dire lequel a compté, et la panne suivante repart de zéro.