Wanneer je gateway DNS kaapt
Een interne site die niet laadde, een resolver die niemand bevroeg, en de dead-IP-query die bewees dat een 'behulpzame' gateway stiekem mijn DNS beantwoordde.
Een interne site geraakte opeens vanaf geen enkele client nog bereikbaar. De stack erachter was prima. Poort 443 reageerde, en curl --resolve gaf deftig HTTP 200 met een geldig certificaat. De server was dus niet het probleem, niemand kon hem alleen nog vinden. Klassiek DNS, alleen waren alle voor de hand liggende verdachten onschuldig.
De resolver werd niet eens bevraagd
Interne namen leven in een split-horizon-zone op een resolver die ik zelf draai; publieke namen gaan naar het internet. Toen ik die resolver vanaf een client bevroeg, kreeg ik een leeg antwoord, en de authority-sectie was ondertekend door de publieke nameservers van mijn domein, niet door mijn interne resolver. Dat kan niet als de query mijn resolver echt had bereikt.
Dus richtte ik dig op een gegarandeerd dood adres (192.0.2.1, een documentatie-IP dat niks kan beantwoorden) op poort 53. Het antwoordde. Een lijk dat de telefoon opneemt, the call is coming from inside the house. Dat is het hele bewijs: als een dood IP een DNS-query beantwoordt, dan onderschept iets op het pad poort 53. De gateway DNAT’te stilletjes alle DNS van elke client naar zijn eigen resolver, die doorstuurde naar het publieke internet, waar interne namen simpelweg niet bestaan. Geen IPv6, niet de resolver die iedereen verdacht. Een ‘behulpzame’ DNS-functie van de gateway die split-horizon per definitie sloopt.
# Eerst de stack zelf, DNS overgeslagen: pin de hostname op de echte server-IP.
curl -I --resolve web.be.jit-c.eu:443:<server-ip> https://web.be.jit-c.eu
# -> HTTP/2 200. De server is gezond; het probleem zit in het vinden, niet het serveren.
# De dead-IP-test: 192.0.2.1 (TEST-NET-1) is dood en hoort te timen-outen.
dig @192.0.2.1 -p 53 example.com +short
# -> Je krijgt toch een A-record terug. Een antwoord van een gegarandeerd dood
# adres betekent dat iets op het pad poort 53 onderschept: de gateway DNAT't alle DNS.
192.0.2.1) dat tóch opneemt: iets op je eigen pad onderschept poort 53. The call is coming from inside the house.Eén resolver, alle lagen
De fix bestond uit twee stappen: de gateway laten stoppen met het onderscheppen van DNS, en elke laag DNS-beveiliging onderbrengen in de ene resolver die ik echt beheer en kan inspecteren.
Technitium doet nu alles: autoritatieve antwoorden voor interne namen, advertentie- en dreigingsblokkering via OISD Big en Hagezi’s Threat Intelligence Feed (~2M domeinen, als NXDOMAIN), DNSSEC-validatie, QNAME-minimalisatie, EDNS Client Subnet weggelaten voor privacy, en recursie voor de rest over versleutelde DNS-over-TLS naar Quad9 en Mullvad. Alles staat in een Ansible-rol, dus het bouwt op uit code in plaats van uit het geheugen.
Split-horizon-DNS werkt alleen als je clients je resolver ook echt kunnen bereiken. Alles op het pad dat ‘behulpzaam’ poort 53 beantwoordt, is een stille single point of failure.
De les die ik onthoud: als DNS zich misdraagt, kijk dan niet alleen naar de server. Check of je query er überhaupt geraakt. Een dead-IP-test kost tien seconden en vertelt je wie er echt antwoordt.
(Eerdere field notes uit deze hoek van de lab: de interne DNS in eerste instantie opzetten en een AI-agent Technitium leren spreken.)