Si vous avez déjà vu un service parfaitement sain « échouer au hasard » selon l’endroit d’où provient la requête, vous avez rencontré le split-horizon DNS dans son habitat naturel : mal configuré, insuffisamment testé et traité comme un tour de magie.
Les symptômes sont familiers. Dans le bureau, api.example.com fonctionne. Depuis un VPN, il se met à temporiser. Depuis un pod Kubernetes, il résout vers quelque chose que vous jurez avoir supprimé. Depuis Internet, il résout « correctement », sauf que votre monitoring interne hurle maintenant. Le post-mortem d’incident dit « DNS » et tout le monde hoche la tête comme si cela expliquait quoi que ce soit.
Ce qu’est réellement le split-horizon DNS (et ce que ce n’est pas)
Le split-horizon DNS signifie que différents clients reçoivent des réponses DNS différentes pour le même nom, en fonction d’où ils proviennent (IP source, interface, clé TSIG, vue, chemin du résolveur, ou une politique explicite). C’est tout. Ce n’est pas intrinsèquement malveillant, et ce n’est pas intrinsèquement sécurisé. C’est une politique de routage pour des noms.
En entreprise, on l’utilise couramment pour :
- Retourner des adresses privées RFC1918 en interne et des adresses publiques à l’extérieur.
- Retourner des cibles différentes pour le même nom de service (load balancer interne vs CDN public).
- Cacher des noms d’hôtes internes uniquement du public (même si « cacher » n’est pas « sécuriser »).
- Soutenir des applications legacy qui codent en dur un nom d’hôte unique alors que les déploiements s’étendent sur plusieurs réseaux.
Le split-horizon n’est pas :
- Un pare-feu. Si vous comptez sur DNS pour bloquer l’accès, vous construisez une porte moustiquaire avec des fichiers de zone.
- Un mécanisme de découverte tolérant la dérive. Si les réponses « intérieures » et « extérieures » ne proviennent pas de la même vérité, elles divergeront.
- Une bonne façon de faire du traffic engineering multi-région à moins de traiter DNS comme le système lent, mis en cache et probabiliste qu’il est.
La difficulté n’est pas de faire fonctionner le split-horizon. La difficulté est de le rendre ennuyeux. Un DNS ennuyeux est un DNS fiable.
Pourquoi le split-horizon tourne mal dans les entreprises
Le split-horizon échoue quand vous perdez le contrôle de trois frontières :
1) Autoritatif vs récursif devient confus
Vos serveurs autoritatifs devraient répondre pour les zones que vous possédez. Vos résolveurs récursifs devraient récupérer et mettre en cache les réponses pour tout le reste. Dans des environnements cassés, des « serveurs DNS » font tout, se renvoient entre eux en boucles, servent des données périmées et devinent quelle vue s’applique.
2) Les clients ne requêtent pas ce que vous croyez
Les ordinateurs portables sur Wi‑Fi, les serveurs dans une VPC, les pods dans Kubernetes et les clients mobiles via VPN utilisent souvent des résolveurs différents, des domaines de recherche différents et des comportements de cache différents. Vous pouvez avoir « un design DNS » sur le papier et cinq en réalité.
3) Deux sources de vérité deviennent silencieusement vingt
« DNS interne » dans BIND. « DNS externe » chez un fournisseur géré. Un forwarder conditionnel dans Windows DNS. Une zone privée hébergée dans le cloud. Une règle de réécriture CoreDNS. Un sidecar de mise en cache stub. Un /etc/hosts édité à la main qu’on a oublié. Voilà comment vous obtenez le même nom résolu vers trois IP selon que vous interrogez avec dig, nslookup ou votre runtime applicatif.
Le split-horizon n’est pas le méchant. La complexité non maîtrisée l’est.
Quelques faits et un bref historique qui expliquent le bazar
- DNS est plus ancien que la plupart de vos applications « legacy ». Il a été standardisé dans les années 1980 pour remplacer le modèle centralisé de fichier HOSTS.TXT.
- Le cache négatif existe. Si un nom n’existe pas (NXDOMAIN), ce « non » peut aussi être mis en cache, contrôlé par les paramètres SOA de la zone.
- Les résolveurs ne sont pas tenus de tourner équitablement entre les enregistrements. Beaucoup le font, d’autres non, et certains maintiennent une réponse plus longtemps que souhaité.
- La troncature UDP est réelle. De grandes réponses DNS peuvent nécessiter une bascule vers TCP ; si le TCP 53 est bloqué, vous obtenez des échecs « aléatoires » avec DNSSEC ou de grands enregistrements TXT.
- Le terme « split-horizon » a été popularisé dans les opérations réseau d’entreprise. Il reflète l’idée de « split horizon » en routage : ne pas réannoncer des routes là d’où elles viennent.
- Les CDN ont normalisé l’idée que les réponses DNS dépendent de la localisation du client. Cela a rendu les réponses basées sur des politiques familières, même quand votre infrastructure ne peut pas supporter la charge opérationnelle.
- Les résolveurs en cache sont autorisés à plafonner les TTL. Votre TTL soigneusement choisi de 30 secondes peut devenir 300 secondes chez certains résolveurs, surtout chez les FAI grand public.
- Les domaines de recherche causent des « requêtes fantômes ». Une requête pour
apipeut devenirapi.corp.example.com, puisapi.example.com, puisapi—et le résolveur peut mettre en cache des échecs intermédiaires.
Si vous ne retenez rien d’autre : DNS est un cache distribué avec des opinions.
Playbook de diagnostic rapide (faites ceci en premier)
Voici la checklist que j’utilise quand quelqu’un dit « en interne ça marche, en externe ça ne marche pas » ou « le VPN a cassé le DNS » en attendant que vous lisiez les pensées.
Première étape : établir quel chemin de résolveur le client défaillant utilise
- Sur l’hôte défaillant, identifiez les résolveurs configurés et les domaines de recherche.
- Confirmez s’il y a un stub local (systemd-resolved, dnsmasq, nscd) et si vos requêtes atteignent bien le réseau.
- Vérifiez si le client est derrière une chaîne de forwarding (DNS VPN, résolveur VPC, forwarders on‑prem).
Deuxième étape : comparer les réponses des sources autoritatives vs des caches récursifs
- Interrogez le résolveur récursif que le client utilise.
- Interrogez directement le(s) serveur(s) autoritatif(s).
- Comparez : IPs, chaîne CNAME, TTL, et si la réponse est NXDOMAIN vs NODATA.
Troisième étape : vérifier la sélection de vue ou la logique de forwarding conditionnel
- Confirmez l’IP source que voit le serveur DNS (NAT et proxies comptent).
- Vérifiez que les vues BIND correspondent au sous‑réseau du client dans l’ordre que vous pensez.
- Contrôlez les forwarders conditionnels : ils redirigent la bonne zone vers la bonne cible, avec la récursion activée là où c’est nécessaire ?
Quatrième étape : confirmer que le transport est sain (UDP/TCP 53)
- Confirmez que UDP 53 fonctionne.
- Confirmez que TCP 53 fonctionne.
- Si DNSSEC ou de grandes réponses sont impliquées, TCP 53 n’est pas optionnel.
Cinquième étape : rendre l’état du cache visible
- Regardez les TTL que vous recevez.
- Videz intentionnellement les caches (stub client, résolveurs récursifs) et retestez.
- Si vider le cache « règle » le problème, vous avez un problème d’invalidation de cache. Félicitations : vous avez atteint le niveau boss.
Tâches pratiques : commandes, sorties, décisions (12+)
Ce sont des tâches réelles que j’exécute pendant les incidents. Chacune inclut la commande, ce que signifie la sortie et la décision suivante à prendre. Exécutez-les depuis un point de vue « fonctionnel » et un point de vue « cassé » : LAN interne, VPN, instance cloud, pod Kubernetes et hôte public.
Task 1: Voir quels résolveurs et domaines de recherche l’hôte utilise réellement
cr0x@server:~$ resolvectl status
Global
Protocols: -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
resolv.conf mode: stub
Current DNS Server: 10.10.0.53
DNS Servers: 10.10.0.53 10.10.0.54
DNS Domain: corp.example.com
Signification : Cet hôte interroge un stub local (systemd-resolved) qui transfère vers 10.10.0.53 et 10.10.0.54 et ajoute le domaine de recherche corp.example.com.
Décision : Si le client en échec utilise un serveur DNS différent de celui attendu, arrêtez les débats théoriques et testez ce résolveur directement ensuite.
Task 2: Confirmer vers quoi pointe /etc/resolv.conf (et si c’est un stub)
cr0x@server:~$ ls -l /etc/resolv.conf
lrwxrwxrwx 1 root root 39 Jan 15 09:12 /etc/resolv.conf -> ../run/systemd/resolve/stub-resolv.conf
Signification : Les requêtes vont au stub local (typiquement 127.0.0.53), pas directement aux résolveurs de l’entreprise.
Décision : Si vous dépannez, testez les deux : le stub (pour déceler les problèmes de cache local) et le résolveur en amont (pour déceler split-horizon et problèmes de forwarding).
Task 3: Interroger le nom en utilisant le même résolveur que le client
cr0x@server:~$ dig +time=2 +tries=1 api.example.com @10.10.0.53
; <<>> DiG 9.18.24 <<>> +time=2 +tries=1 api.example.com @10.10.0.53
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 4242
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; ANSWER SECTION:
api.example.com. 20 IN A 10.20.30.40
;; Query time: 12 msec
;; SERVER: 10.10.0.53#53(10.10.0.53) (UDP)
Signification : Le résolveur interne retourne une IP privée avec un faible TTL (20s). Il est récursif (ra) et la réponse est actuellement cohérente.
Décision : Si cela diffère de ce que voit « l’extérieur », le split-horizon est actif. Ensuite, confirmez les réponses autoritatives pour chaque horizon.
Task 4: Interroger le résolveur public et comparer
cr0x@server:~$ dig +time=2 +tries=1 api.example.com @1.1.1.1
; <<>> DiG 9.18.24 <<>> +time=2 +tries=1 api.example.com @1.1.1.1
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 1234
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; ANSWER SECTION:
api.example.com. 300 IN A 203.0.113.77
;; Query time: 18 msec
;; SERVER: 1.1.1.1#53(1.1.1.1) (UDP)
Signification : Le résolveur public retourne une IP publique avec un TTL plus long (300s). C’est la réponse « extérieure ».
Décision : Décidez si c’est intentionnel. Si oui, vous devez vous assurer que les deux réponses pointent vers des cibles fonctionnelles et que les clients sont classifiés de manière fiable dans le bon horizon.
Task 5: Identifier les serveurs de noms autoritatifs pour la zone
cr0x@server:~$ dig +short NS example.com
ns1.dns-provider.net.
ns2.dns-provider.net.
Signification : L’Internet public considère ns1/ns2 comme autoritatifs pour example.com.
Décision : Si le DNS interne sert une autorité différente (par exemple, un master BIND interne), vous exécutez deux autorités. Cela peut aller, mais seulement avec un contrôle de changement discipliné et de l’automatisation.
Task 6: Interroger directement les serveurs autoritatifs (bypasser les caches)
cr0x@server:~$ dig api.example.com @ns1.dns-provider.net +norecurse
; <<>> DiG 9.18.24 <<>> api.example.com @ns1.dns-provider.net +norecurse
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 9000
;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; ANSWER SECTION:
api.example.com. 300 IN A 203.0.113.77
Signification : aa signifie réponse autoritative : la zone publique a 203.0.113.77.
Décision : Si votre résolveur interne est censé surcharger cela, confirmez où cette surcharge est définie (vue BIND, zone hébergée privée, forwarder conditionnel) et si elle est aussi autoritative.
Task 7: Vérifier si une chaîne CNAME diffère entre l’intérieur et l’extérieur
cr0x@server:~$ dig +noall +answer +authority +additional api.example.com @10.10.0.53
api.example.com. 20 IN CNAME api-internal.example.com.
api-internal.example.com. 20 IN A 10.20.30.40
Signification : L’horizon interne utilise un CNAME vers un nom interne uniquement.
Décision : Assurez‑vous que la cible du CNAME est résoluble pour chaque client qui peut la voir. Si les clients VPN sont classés comme « extérieur » mais reçoivent le CNAME interne, ils échoueront d’une manière qui ressemble à une panne réseau.
Task 8: Détecter les effets des domaines de recherche (le problème « pourquoi a‑t‑il interrogé ça ? »)
cr0x@server:~$ dig +search api @10.10.0.53
; <<>> DiG 9.18.24 <<>> +search api @10.10.0.53
;; ANSWER SECTION:
api.corp.example.com. 60 IN A 10.9.8.7
Signification : Le client a demandé api et le résolveur l’a étendu en api.corp.example.com. Ce n’est peut‑être pas le service que vous souhaitiez.
Décision : Si les configs applicatives utilisent des noms courts, corrigez la config. Ne « corrigez » pas le DNS en ajoutant des enregistrements plus ambigus. L’ambiguïté évolue plus vite que les effectifs.
Task 9: Confirmer si la bascule TCP fonctionne (grandes réponses / DNSSEC / troncature)
cr0x@server:~$ dig example.com DNSKEY @1.1.1.1 +dnssec +tcp +time=2 +tries=1
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 7777
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
Signification : La requête TCP a réussi. Si UDP fonctionne mais que TCP échoue, vous obtenez des échecs intermittents quand les réponses sont trop volumineuses pour UDP sans EDNS0.
Décision : Si TCP 53 est bloqué quelque part entre le client et le résolveur/autoritatif, corrigez cela en priorité. Ne négociez pas avec les firewalls en disant « mais DNS c’est UDP ». C’est les deux.
Task 10: Observer le trafic DNS réel et l’IP source (la sélection de vue en dépend)
cr0x@server:~$ sudo tcpdump -ni eth0 port 53 -vv
tcpdump: listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
10:22:31.100001 IP 172.16.50.10.51123 > 10.10.0.53.53: 4242+ A? api.example.com. (33)
10:22:31.100220 IP 10.10.0.53.53 > 172.16.50.10.51123: 4242* 1/0/0 A 10.20.30.40 (49)
Signification : Le résolveur voit le client comme 172.16.50.10. Si vous attendiez un sous‑réseau différent (par ex. pool VPN vs pool NAT), votre correspondance de vue peut être incorrecte.
Décision : Ajustez les ACL des vues pour correspondre aux adresses que le serveur voit réellement. Ne tombez pas dans le piège de matcher sur les adresses que vous souhaiteriez voir.
Task 11: Vérifier l’ordre des vues BIND et l’attachement de la zone
cr0x@server:~$ sudo named-checkconf -p | sed -n '1,120p'
acl "internal-nets" { 10.0.0.0/8; 172.16.0.0/12; 192.168.0.0/16; };
view "internal" {
match-clients { "internal-nets"; };
recursion yes;
zone "example.com" { type master; file "/etc/bind/zones/db.example.com.internal"; };
};
view "external" {
match-clients { any; };
recursion yes;
zone "example.com" { type master; file "/etc/bind/zones/db.example.com.external"; };
};
Signification : Deux fichiers de zone différents pour le même nom de zone, sélectionnés par l’IP source du client. L’ordre des vues compte : le premier match gagne.
Décision : Confirmez que les sous‑réseaux VPN/NAT sont inclus dans internal-nets s’ils doivent recevoir des réponses internes. Sinon, vous avez trouvé votre « folie intérieur/extérieur ».
Task 12: Valider l’intégrité du fichier de zone avant d’accuser le « hasard DNS »
cr0x@server:~$ sudo named-checkzone example.com /etc/bind/zones/db.example.com.internal
zone example.com/IN: loaded serial 2026020401
OK
Signification : La zone se parse et se charge ; le serial est visible. Si cela échoue, vous pouvez servir une version plus ancienne ou ne pas charger du tout.
Décision : Si le serial n’est pas celui attendu, trouvez le master réel et le chemin de propagation (AXFR/IXFR, déploiement git, gestion de config).
Task 13: Vérifier ce qu’Unbound a mis en cache (les réponses périmées sont un mode de vie)
cr0x@server:~$ sudo unbound-control lookup api.example.com
api.example.com. 20 IN A 10.20.30.40
Signification : Unbound a actuellement un enregistrement mis en cache avec un TTL restant. Si cette réponse est incorrecte, les clients continueront à la voir jusqu’à l’expiration du TTL (ou jusqu’à ce que vous la vidiez).
Décision : Si vous venez de changer la zone et que le cache contient encore l’ancienne donnée, videz ce nom spécifique et confirmez que les données autoritatives sont correctes.
Task 14: Vider un nom mis en cache spécifique (surgical, pas « reboot DNS »)
cr0x@server:~$ sudo unbound-control flush api.example.com
ok
Signification : Entrée de cache supprimée ; la requête suivante devra récupérer des données fraîches.
Décision : Si le vidage corrige, vous avez besoin d’une stratégie TTL et d’un processus de changement qui reconnaît la mise en cache—surtout pour le split-horizon où deux caches peuvent être en désaccord.
Task 15: Prouver si l’application utilise le résolveur OS ou autre chose
cr0x@server:~$ strace -f -e trace=network -s 128 -p $(pidof myapp) 2>&1 | head -n 20
socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, IPPROTO_IP) = 42
connect(42, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("127.0.0.53")}, 16) = 0
sendto(42, "\252\252\1\0\0\1\0\0\0\0\0\0\3api\7example\3com\0\0\1\0\1", 33, 0, NULL, 0) = 33
Signification : L’appli parle à 127.0.0.53, le stub local. Si vous attendiez des requêtes directes vers un résolveur corporate, votre « correction DNS » pourrait ne pas toucher le chemin réel.
Décision : Décidez si vous réglez au niveau du stub, du résolveur en amont ou de l’application (par ex. paramètres de cache DNS JVM). Accuser « le DNS » sans localiser le résolveur est de l’art de la performance.
Task 16: Vérifier le cache DNS JVM (parce que Java garde des rancunes)
cr0x@server:~$ jcmd $(pidof java) VM.system_properties | egrep 'networkaddress.cache|networkaddress.cache.negative'
networkaddress.cache.ttl=-1
networkaddress.cache.negative.ttl=10
Signification : Un TTL de -1 signifie cache indéfini (sauf si redéfini par une politique de security manager). Cela neutralise le basculement basé sur DNS et peut préserver le « mauvais horizon » lors de mouvements réseau.
Décision : Si vous comptez sur des changements DNS pour la récupération, définissez des TTL sensés dans le runtime ou sortez la découverte de service de DNS pour un basculement rapide.
Blague #1 : Le DNS est le seul système où « c’est mis en cache » est à la fois une explication et une confession.
La correction : un design split-horizon qui reste sain
La correction fiable n’est pas « ajoutez une autre vue » ou « baissez le TTL à 5 secondes ». La correction consiste à faire du split-horizon un produit délibéré avec un seul propriétaire, une seule source de vérité et un chemin de résolveur prévisible.
Étape 1 : Décidez le modèle de nommage : même nom de zone, ou noms de zone différents
Vous avez deux schémas viables :
-
Même nom de zone (split-horizon classique) :
api.example.comexiste à la fois dans les vues interne et externe, avec des réponses différentes.- Avantages : simple pour les utilisateurs, un seul hostname partout.
- Inconvénients : chaque mauvaise classification devient une panne ; le débogage est plus lent ; les caches amplifient les erreurs.
-
Noms de zone différents (recommandé quand possible) : services internes sous
api.corp.example.comet public sousapi.example.com.- Avantages : moins de réponses ambiguës ; le « mauvais horizon » fonctionne souvent encore parce que c’est un nom différent.
- Inconvénients : nécessite des changements d’appli/config ; les humains doivent apprendre quel nom appartient à quel usage.
Mon conseil tranché : si vous le pouvez, utilisez des noms différents. Sinon, utilisez le split-horizon même-nom mais traitez‑le comme du code de production avec tests, CI et revue de changement.
Étape 2 : Choisir un workflow autoritatif par horizon
Si vous exécutez deux autorités différentes (fournisseur DNS public et BIND interne), acceptez que vous gérez deux produits.
Votre travail est d’éliminer les « edits à la main » et la dérive :
- Stockez les données de zone en contrôle de version (même si elles sont générées).
- Générez les zones interne et externe à partir d’un inventaire partagé, avec des overrides explicites.
- Exigez une discipline de serial (monotonique, automatisée) et validez avant déploiement.
- Faites en sorte que « qui possède cet enregistrement ? » soit réponse‑able en une minute.
Étape 3 : Séparer le DNS autoritatif du DNS récursif
C’est là où beaucoup d’environnements pourrissent. Ils installent BIND sur une machine, activent la récursion, ajoutent des vues, ajoutent du forwarding, et maintenant le même démon est :
autoritatif pour des zones internes, autoritatif pour des overrides externes, récursif pour tout le reste, et exposé à des réseaux qu’il ne devrait pas voir.
Une architecture propre ressemble à ceci :
- Couche autoritative : sert les zones internes (et la vue interne des zones partagées) uniquement aux résolveurs récursifs, pas aux clients.
- Couche récursive : les clients parlent aux résolveurs récursifs ; les résolveurs parlent aux serveurs autoritatifs et à Internet selon les besoins.
- Couche politique : la logique split-horizon vit dans un petit nombre d’endroits (vues BIND sur l’autoritatif, ou forwarders conditionnels sur les recursors), pas éparpillée sur les laptops et clients VPN.
Étape 4 : Rendre la sélection de vue déterministe et documentée
Si vous utilisez des vues BIND, matchez sur les IP que le serveur DNS voit. Cela signifie que vous devez prendre en compte :
- Les pools VPN (souvent des sous‑réseaux différents du LAN de bureau)
- Les gateways NAT (les clients apparaissent comme l’IP NAT, pas leur sous‑réseau d’origine)
- Les résolveurs proxy (l’IP d’un forwarder peut être le « client »)
- Le comportement dual‑stack (les clients IPv6 peuvent contourner des hypothèses IPv4 uniquement)
L’incident de split-horizon le plus courant est un nouveau chemin réseau (nouveau VPN, nouveau NAT, nouvelle VPC) qui n’a pas été ajouté à l’ACL « interne ». Soudainement, des « utilisateurs internes » deviennent « externes », et les réponses DNS deviennent incompatibles avec le routage interne.
Étape 5 : Garder les réponses internes et externes compatibles quand c’est possible
Quand vous devez partager un nom, concevez‑le pour que la « mauvaise » réponse échoue en douceur :
- Préférez retourner des adresses routables depuis les deux horizons quand c’est possible (par ex. les clients internes peuvent atteindre le LB public aussi).
- Si l’interne retourne des IP privées, assurez‑vous que les clients externes ne voient jamais ces enregistrements (pas de fuite via une vue mal appliquée, un forwarder ou un résolveur dans le mauvais sous‑réseau).
- Utilisez les CNAMEs avec précaution : ils augmentent la zone d’impact car la cible peut ne pas exister dans l’autre horizon.
- Faites attention aux enregistrements wildcard ; ils peuvent transformer des fautes de frappe en noms « valides » qui routent vers des destinations coûteuses.
Étape 6 : Stratégie TTL : arrêtez le cargo‑cult « mettre le TTL à 5 »
Le TTL est votre levier sur la durée de vie des erreurs. C’est aussi votre levier sur la charge infligée à votre infrastructure DNS en fonctionnement normal.
Conseils pratiques sur les TTL :
- Pour les enregistrements stables, utilisez des TTL modérés (5–30 minutes). Les TTL bas augmentent le volume de requêtes et ne garantissent pas une propagation rapide à cause des plafonds des résolveurs.
- Pour les fenêtres de migration, baissez les TTL bien en avance (heures à jours), puis changez les enregistrements, puis augmentez les TTL après stabilité.
- Souvenez‑vous du cache négatif : NXDOMAIN peut rester et gâcher votre moment « je viens de créer cet enregistrement ».
Étape 7 : Tester le split-horizon comme vous testez les déploiements
Vous avez besoin de tests depuis chaque point de vue. Pas « j’ai lancé dig une fois depuis mon laptop ». Des tests qui tournent en continu et alertent sur les divergences :
- LAN interne : réponses internes attendues.
- VPN : réponses internes attendues ou comportement externe explicitement attendu.
- VPC cloud : réponses internes attendues si c’est partie du réseau corporate.
- Internet public : réponses externes attendues.
- Depuis les résolveurs récursifs directement : comportement autoritatif attendu, TTL correct, chaîne CNAME correcte.
Voici la citation opérationnelle qui devrait hanter vos revues de design DNS :
« L’espoir n’est pas une stratégie. » — Général Gordon R. Sullivan
Les pannes DNS commencent souvent par de l’espoir : espoir que les caches expirent vite, espoir que les pools VPN ne changent jamais, espoir que personne n’ajoute un forwarder « temporaire ». Ne faites pas tourner la production sur de l’espoir.
Trois mini-récits de la vie en entreprise
Mini-récit 1 : L’incident causé par une mauvaise hypothèse
Une entreprise de taille moyenne faisait du split-horizon sur une paire de serveurs BIND. La vue interne retournait des IP privées pour git.example.com ; la vue externe retournait un reverse proxy public.
Pendant des années, ça a fonctionné. Silencieusement. Ce sont souvent les débuts des incidents.
Ils ont remplacé leur concentrateur VPN. Nouveau fournisseur, architecture « moderne » : le trafic client a été hairpiné via une série de gateways NAT. L’équipe qui a mis à jour le VPN a focalisé sur l’authent et le débit. Le DNS était « inchangé ».
Lundi matin, les ingénieurs distants pouvaient authentifier sur le VPN et atteindre des IP internes par adresse, mais git.example.com résolvait vers le proxy externe. Le proxy externe appliquait un SSO bloqué par des règles d’accès conditionnel lorsqu’on venait d’adresses corporate. Les ingénieurs étaient sur le VPN, mais le DNS les classait comme « extérieurs ». Le pire des deux mondes.
L’hypothèse erronée était simple : « les clients VPN viendront toujours des sous‑réseaux du pool VPN ». Ils ne l’ont pas fait. Le serveur DNS ne voyait que les IP des gateways NAT. Les vues BIND matchaient la plage NAT comme any, donc elles tombaient sur la vue externe.
La correction a été ennuyeuse : ajouter les plages des gateways NAT à l’ACL interne, puis créer une sonde de monitoring qui effectue la même requête DNS derrière ce NAT et valide la réponse interne. Ils ont aussi documenté que « la classification de vue DNS dépend de l’IP source après NAT », ce qui aurait dû être évident, mais les incidents sont juste des frais de scolarité pour apprendre l’évidence.
Mini-récit 2 : L’optimisation qui s’est retournée contre eux
Une grande entreprise voulait un basculement plus rapide pour une API client. Ils ont abaissé le TTL de 300 secondes à 5 secondes sur l’enregistrement A externe et automatisé la bascule entre deux load balancers. En démo, c’était parfait.
Puis la production est arrivée. Leur flotte de résolveurs récursifs (interne et chez certains gros FAI) a plafonné les TTL bas vers le haut. Pas de manière consistante ; pas de manière prévisible. Certains clients ont re‑résolu rapidement, d’autres ont continué d’utiliser l’ancienne adresse pendant des minutes. Entre‑temps, le TTL bas a multiplié les taux de requêtes. Leur provider autoritatif a tenu, mais leurs forwarders internes non. Le CPU a grimpé. Les hit ratios du cache ont chuté. La latence a augmenté. L’« optimisation » a accru la probabilité de défaillance au moment précis où ils avaient besoin d’un DNS calme.
Pire : les enregistrements split-horizon internes pour le même nom utilisaient des TTL différents et étaient gérés dans un système différent. Lors d’un test de basculement, les monitors internes résolvaient encore vers l’ancienne cible interne plus longtemps que les clients externes. Le canal d’incident a rempli de « mais le tableau de bord dit que c’est encore down » alors que les clients étaient déjà revenus.
Ils ont annulé le TTL à 5 secondes, déplacé le basculement au niveau du load balancer où les changements d’état se propagent plus vite que les caches DNS, et remis les TTL DNS dans la plage « normale ». Ils ont aussi aligné les politiques TTL internes et externes pour les noms partagés et ont commencé à suivre les taux de requêtes en tant que SLO de première classe.
Blague #2 : Mettre le TTL à 5 secondes, c’est l’équivalent DNS de rouler plus vite en enlevant les freins.
Mini-récit 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise
Une autre organisation a maintenu une séparation propre : serveurs autoritatifs pour les zones internes, résolveurs récursifs pour les clients, et un petit ensemble de forwarders conditionnels pour les zones privées cloud. Ils avaient une règle écrite : « Aucun client ne pointe directement vers le DNS autoritatif. » Ça semblait pédant. Ça l’était.
Un jour, un nouveau déploiement de fichier de zone interne a introduit une erreur de syntaxe dans la vue interne pour une zone partagée. Le démon autoritatif a refusé de charger cette zone. Les résolveurs récursifs ont continué à servir des réponses en cache, gagnant du temps. Leur monitoring ne testait pas seulement la résolution ; il testait le chargement des zones autoritatives et l’incrémentation des serials.
L’astreinte a été paginée pour « zone autoritative non chargée » avant que le cache n’arrive à expiration. Ils ont rollbacké le fichier de zone en quelques minutes. Les clients n’ont rien remarqué. C’était le genre d’incident qui ne fait pas une bonne histoire de conférence parce que rien n’a pris feu. C’est le but.
La pratique qui les a sauvés était la plus ennuyeuse en DNS : valider les configs avant reload, monitorer la santé autoritative, et ne pas laisser les clients frapper l’autorité directement. Cela a transformé un incident potentiellement global en un rollback discret.
Erreurs courantes : symptôme → cause racine → correction
1) Symbole : « Ça marche au bureau, ça échoue sur le VPN »
Cause racine : Les clients VPN sont SNATés ou se voient attribuer un sous‑réseau non inclus dans l’ACL de la vue interne ; ils reçoivent des réponses DNS externes.
Correction : Mettez à jour les ACL des vues pour inclure les plages VPN/NAT telles que vues par le serveur DNS ; ajoutez des probes continues depuis la sortie VPN.
2) Symbole : « dig marche, l’appli non »
Cause racine : L’application utilise un chemin de résolveur différent (stub local, DNS de conteneur, cache du runtime langage) que votre test.
Correction : Tracez les appels DNS de l’appli (strace), inspectez le cache runtime (JVM, Go netdns, .NET) et testez le résolveur exact utilisé.
3) Symbole : « nslookup montre une IP, dig en montre une autre »
Cause racine : Différents serveurs par défaut ; un outil interroge un résolveur différent, ou l’expansion du domaine de recherche change le nom interrogé.
Correction : Spécifiez toujours le résolveur avec @server ; utilisez des noms pleinement qualifiés avec un point final si nécessaire ; vérifiez les domaines de recherche.
4) Symbole : « Après un changement DNS, certains clients touchent encore l’ancienne cible »
Cause racine : Mise en cache des résolveurs, plafonds de TTL, cache applicatif ; cache négatif pour un précédent NXDOMAIN.
Correction : Utilisez des réductions de TTL planifiées avant les migrations ; videz les caches quand approprié ; ne comptez pas sur DNS pour un basculement sub‑minute.
5) Symbole : « Échecs intermittents avec DNSSEC ou grands TXT »
Cause racine : TCP 53 bloqué ; fragmentation UDP ou problèmes EDNS0 ; réponses tronquées non relancées correctement.
Correction : Autorisez TCP/UDP 53 bout en bout ; vérifiez le support EDNS0 ; testez avec dig +tcp.
6) Symbole : « Des utilisateurs externes voient parfois des IP privées »
Cause racine : Données split-horizon fuyantes via des vues mal appliquées, un forwarder exposé à Internet, ou un résolveur public qui forwarde vers l’interne.
Correction : N’exposez jamais des résolveurs récursifs internes publiquement ; restreignez la récursion ; auditez les chaînes de forwarding ; assurez‑vous que les zones internes ne sont pas servies publiquement.
7) Symbole : « Les pods Kubernetes résolvent différemment des nœuds »
Cause racine : Les pods utilisent CoreDNS et la politique DNS du cluster ; les nœuds utilisent le résolveur système ; le forwarding conditionnel diffère.
Correction : Alignez les forwarders CoreDNS avec votre stratégie de résolveur récursif corporate ; ajoutez des probes depuis des pods ; évitez les hacks de réécriture sauf si vous pouvez les tester.
8) Symbole : « Seuls certains sites/bureaux échouent »
Cause racine : Forwarders différents par site ; forwarding conditionnel incohérent ; transferts de zone obsolètes vers un résolveur local.
Correction : Standardisez la configuration des résolveurs ; monitorisez les serials de zone par site ; préférez la récursion centralisée avec cache local seulement quand nécessaire.
Checklists / plan pas à pas
Pas à pas : reconstruire le split-horizon pour qu’il arrête de nuire
-
Inventoriez les chemins de résolveur.
- Listez les résolveurs récursifs corporate, résolveurs cloud, résolveurs fournis par le VPN et tous les stubs locaux.
- Décidez : quels clients doivent utiliser quel résolveur, et pourquoi.
-
Tracez la frontière d’autorité.
- Quels serveurs sont autoritatifs pour les zones internes ?
- Quel fournisseur est autoritatif pour les zones publiques ?
- Qui possède les changements dans chaque cas ?
-
Choisissez le mécanisme split-horizon.
- Vues BIND sur l’autoritatif ? Forwarding conditionnel sur les récursifs ? Zones privées cloud ?
- Minimisez le nombre de points de politique. Deux, c’est déjà beaucoup.
-
Définissez les règles de classification des vues.
- Enumérez les réseaux internes tels que vus par les serveurs DNS (post‑NAT).
- Incluez délibérément les sorties VPN et les plages NAT cloud.
-
Unifiez la génération des enregistrements.
- Générez les variantes interne/externe à partir de données partagées avec des diffs explicites.
- Interdisez les edits manuels en production en dehors d’un processus d’urgence contrôlé.
-
Définissez une politique TTL et respectez‑la.
- Définissez des TTL standards par type d’enregistrement et par environnement.
- Définissez un playbook de migration pour baisser les TTL en amont.
-
Construisez des tests depuis chaque horizon.
- Au minimum : LAN interne, VPN, cloud, Internet public.
- Testez le nom, la chaîne CNAME et la reachabilité finale (santé HTTP/TCP).
-
Déployez avec des gardes de validation.
- Vérification de syntaxe de zone, vérification monotone du serial, et un reload canari du résolveur avant déploiement global.
-
Monitorisez ce qui compte.
- Taux NXDOMAIN, taux SERVFAIL, échecs de récursion, latence de requête et échecs de bascule TCP.
- Statut de charge des zones et dérive des serials entre secondaires.
Checklist opérationnelle : avant de changer un enregistrement split-horizon
- Ce nom existe‑t‑il dans plus d’un horizon ? Listez‑les.
- Le changement affectera‑t‑il des cibles CNAME qui n’existent pas à l’extérieur ?
- Le TTL est-il déjà assez bas pour la fenêtre de migration ? Sinon, baissez‑le à l’avance.
- Changez‑vous la source autoritative ou seulement un override récursif ?
- Quelles probes de monitoring vous diront que cela a fonctionné depuis chaque horizon ?
- Avez‑vous un jeu d’enregistrements de rollback prêt (et testé) ?
Checklist opérationnelle : pendant un incident
- Identifiez le chemin de résolveur depuis le client en échec.
- Comparez les réponses : résolveur client vs autoritatif.
- Confirmez le matching de vue avec capture de paquets ou logs du résolveur.
- Validez TCP 53.
- Décidez : corriger la classification, corriger les données d’enregistrement, ou vider les caches (et où).
- Notez le nom de requête exact (avec point final si nécessaire) et l’IP du résolveur. L’ambiguïté est ce qui prolonge les incidents.
FAQ
1) Dois‑je utiliser le split-horizon DNS du tout ?
Utilisez‑le quand vous avez vraiment besoin qu’un nom d’hôte mappe vers des cibles différentes selon la localisation du client. Sinon, préférez des noms différents pour les services internes et externes. Cela réduit l’ambiguïté et diminue le temps d’incident.
2) Le split-horizon DNS est‑il la même chose que le « DNS privé » dans le cloud ?
Conceptuellement résultat similaire : des réponses différentes selon le point d’interrogation. Mécaniquement différent : les zones privées cloud sont souvent attachées à des VPC et utilisent des résolveurs fournis par le provider, tandis que le split-horizon classique utilise des vues ou des politiques sur vos serveurs DNS.
3) Pourquoi je vois la « mauvaise » réponse seulement depuis des pods Kubernetes ?
Les pods interrogent typiquement CoreDNS (ou équivalent), qui forwarde selon la configuration du cluster. Les nœuds peuvent utiliser d’autres résolveurs. Corrigez en alignant les forwarders/forwarding conditionnel de CoreDNS avec la stratégie de résolveur récursif corporate et testez depuis un pod.
4) Puis‑je compter sur le TTL pour un basculement rapide ?
Pas pour un comportement sub‑minute. Certains résolveurs plafonnent les TTL vers le haut ; les applications mettent en cache ; les pools de connexions continuent d’utiliser d’anciennes IPs. Mettez le basculement rapide dans des load balancers ou des meshes de service ; gardez DNS pour la découverte état stable et les coupures de migration lentes.
5) Quel est le risque opérationnel majeur avec les vues BIND ?
La mauvaise classification. Si un nouveau sous‑réseau (VPN, NAT, egress cloud) n’est pas inclus dans la bonne ACL, ces clients obtiennent la mauvaise zone. Ensuite vous déboguez le « réseau » alors que le vrai problème est une politique.
6) Comment empêcher les enregistrements internes de fuir vers Internet ?
N’exposez pas de résolveurs récursifs internes publiquement. Désactivez la récursion sur les serveurs autoritatifs qui font face à des réseaux non fiables. Restreignez les transferts de zone. Auditez les chaînes de forwarding pour qu’aucun résolveur public ne forwarde des zones privées vers l’infrastructure interne.
7) Pourquoi vider les caches « corrige » parfois, puis le problème revient ?
Parce que vous avez vidé un cache, pas toute la chaîne. Stubs clients, caches récursifs, caches runtime applicatifs et parfois des forwarders intermédiaires existent tous. Si les données autoritatives sont erronées, vider n’achète qu’une illusion temporaire de compétence.
8) Les TTL internes et externes doivent‑ils correspondre ?
Pas toujours, mais ils devraient être gouvernés par la même politique. Si le même nom est split-horizon, des TTL très différents peuvent embrouiller le monitoring, ralentir la confirmation d’incident et créer un comportement asymétrique pendant les migrations.
9) Quel est un schéma sûr pour « un nom partout » sans split-horizon ?
Terminez le trafic à un point public unique que les réseaux internes peuvent aussi atteindre (LB public avec allowlists internes, ou VIP à double accès). Alors DNS peut retourner une seule réponse globalement. Cela échange la complexité DNS contre la politique réseau, ce qui est généralement un gain.
10) Comment monitorer correctement le split-horizon ?
Lancez la même requête DNS depuis chaque horizon et alertez sur la dérive inattendue. Monitorisez aussi les taux SERVFAIL/NXDOMAIN, la latence des résolveurs et la cohérence des serials de zone. Si vous ne surveillez que « est‑ce que ça résout », vous manquerez « est‑ce que ça résout vers la bonne chose ».
Conclusion : prochaines étapes pratiques
Le split-horizon DNS est survivable si vous arrêtez de le traiter comme un tour intelligent et que vous commencez à le traiter comme une infrastructure avec des modes de défaillance. La correction n’est pas un nouvel enregistrement. C’est une nouvelle discipline : chemins de résolveur clairs, classification déterministe et une seule vérité pour les données de zone.
Faites ceci ensuite, dans l’ordre
- Cartographiez les chemins de résolveur pour LAN, VPN, cloud, Kubernetes et un hôte public. Notez‑les par écrit.
- Choisissez votre point de politique : vues sur l’autoritatif ou forwarding conditionnel sur les récursifs. Minimisez le nombre.
- Rendez la classification explicite : énumérez les sous‑réseaux tels que vus par les serveurs DNS (y compris NAT/VPN egress).
- Unifiez la gestion des enregistrements : générez les variantes interne/externe à partir de données partagées ; pas d’éditions à la main.
- Ajoutez des probes par horizon et alertez sur la dérive des réponses, pas seulement sur le succès de résolution.
- Adoptez un playbook de migration TTL afin de pouvoir changer de cible sans supplier les caches pour de la pitié.
Si vous voulez que la « folie intérieur/extérieur » cesse, votre DNS doit devenir ennuyeux. Pas minimal. Pas astucieux. Ennuyeux. C’est ainsi que la production survit.