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 :

SymptomWhat it meansCheck first
dig answers, getent failslookup chain, not the packet pathnsswitch line and resolvectl status
Timeout rather than NXDOMAINprivate resolver unreachable through the tunnelresolvectl dns tun0, route to that address
NXDOMAINan authority answered and does not know the namezone owner, endpoint region
FQDN works, short name failssearch suffix mismatchresolvectl domain on the tun link
getent works, app failsapp cache or its own resolverrestart 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.

Articles similaires