Ett DNS-fel som ekade i 48 timmar

En felaktig delegering som var rättad efter 45 minuter slog ut min Home Assistant två dygn senare. Det var inte en attack, och DNSSEC hade inte hjälpt.

Den 26 augusti slutade ha.paftech.se att fungera. Apache-proxyn som terminerar namnet svarade:

HTTP/1.1 500 Proxy Error
Server: Apache

Reason: DNS lookup failure for: mikrotik.paftech.se

Home Assistant själv var opåverkad genom hela avbrottet — den svarade 200 på sin lokala adress hela tiden. Felet låg i ett enda led: proxyn kunde inte längre slå upp namnet på sin backend.

Kedjan Link to heading

Namnet mikrotik.paftech.se var en CNAME in i MikroTiks moln-DDNS. Det såg ut som ett hopp men var fyra, och det tredje ledde in i en zon som betjänas av namnservrar i en fjärde domän.

Fyra hopp, varav ett utanför all egen kontroll.
Fyra hopp, varav ett utanför all egen kontroll.

Serienumret i värdnamnet, EC190FAE79D9, är routerns eget — MikroTiks DDNS bygger namnet på hårdvarans serienummer.

Vad cachen innehöll Link to heading

En fråga till den egna resolvern utan rekursion visade problemet direkt:

$ dig @127.0.0.1 +norec ns1.kissthenet.net A

;; AUTHORITY SECTION:
kissthenet.net.   44773  IN  NS  ns1.mikrotik.com.
kissthenet.net.   44773  IN  NS  ns2.mikrotik.com.

Föräldrazonen .net delegerar kissthenet.net till ns1 och ns2.kissthenet.net. Cachen pekade på något helt annat. Och de servrarna betjänade inte zonen: de svarade utan aa-flagga, med ra satt, och returnerade en hänvisning tillbaka till roten. Resolvern gav upp efter 24 millisekunder.

Servrarna är däremot auktoritativa för mikrotik.com, och de besvarade dessutom frågor om www.google.com — de är alltså öppna rekursiva resolvrar. De rätta namnservrarna, 159.148.147.201 och 159.148.172.251, svarade korrekt under hela avbrottet. Det gick att fråga dem direkt hela tiden; det fanns bara ingen väg dit.

Tidslinjen Link to heading

Det intressanta är inte att det gick sönder, utan när.

Felet var rättat på 45 minuter. Avbrottet kom 33 timmar senare, när cachen löpte ut.
Felet var rättat på 45 minuter. Avbrottet kom 33 timmar senare, när cachen löpte ut.

Min SmokePing övervakar mikrotik.paftech.se — namnet, inte adressen. Därför fångade den ett rent DNS-fel och daterade det på minuten: förlust från mätfönstret 17:45 (14 %, alltså felet inträffade mot slutet av fönstret), återställt 20:25. Två timmar och 41 minuter.

Delegerings-TTL:en i .net är 172 800 sekunder, exakt 48 timmar. Avbrottet började 15:44 UTC den 26:e. Precis 48 timmar tidigare, 15:44 UTC den 24:e, hämtade min resolver om delegeringen för mynetname.net. Avbrottet började i samma sekund som den cachen löpte ut.

Och 72 minuter före den hämtningen ändrade MikroTik delegeringen hos registraren. WHOIS visar:

mynetname.net    Updated Date: 2026-08-24T14:32:05Z
kissthenet.net   Updated Date: 2026-08-25T07:24:07Z

Den återstående TTL:en på den felaktiga posten, 44 773 sekunder, placerar den i cachen omkring 06:39 UTC den 25:e — knappt en timme innan kissthenet.net ändrades tillbaka. Felet var alltså live i storleksordningen 45 minuter, och rättades. Det spelade ingen roll: min cache var redan satt i 48 timmar.

Det ska sägas att just den beräkningen är härledd ur TTL-aritmetik och inte ur en logg — loggarna hade roterat bort. Men den stämmer med registrarens tidsstämplar och med ett inlägg på MikroTiks eget forum, daterat den 24 augusti, där en användare konstaterar att registret nu visar ns1/ns2.mikrotik.com medan Cloudflare fortfarande visar kissthenet.net.

Var det cacheförgiftning? Link to heading

Nej. Cachen innehöll felaktig data, men den var äkta — publicerad av den rättmätige operatören genom registraren. Resolvern gjorde exakt vad protokollet föreskriver.

Fyra skäl, i fallande styrka:

  1. Nyttolasten är värdelös för en angripare. Den pekade på MikroTiks egna produktionsservrar i deras eget adressblock. Ingen trafik omdirigerades någonstans; resultatet var enbart att uppslagningen slutade fungera. Den som förgiftar en cache vill styra trafik till sig, inte stänga av den.
  2. Ändringen är oberoende dokumenterad. Registrarens tidsstämplar på båda domänerna, plus foruminlägget samma dag.
  3. Formen är en återställning. En ändring, en andra ändring 45 minuter senare, och dagens tillstånd är identiskt med utgångsläget.
  4. Skadan drabbade MikroTik själva. Deras egen DDNS slutade fungera för alla som hann cacha felet.

DNSSEC hade inte hjälpt — och bevisar inget heller Link to heading

Två slutsatser ligger nära till hands. Ingen av dem håller.

“Signering hade förhindrat det.” Nej. NS-posterna i föräldrazonen är inte auktoritativ data där — de är en hänvisning och bär ingen signatur. Bara DS-posten signeras. En validerande resolver får noll skydd mot en felaktig delegering. Data var dessutom äkta: DNSSEC bevisar att en post kommer från rätt avsändare, inte att avsändaren pekade rätt.

“DNSSEC visar att det inte var en attack.” Inte heller. .net är signerat, och min resolver validerade mycket riktigt att kissthenet.net saknar DS — men beviset levereras med en NSEC3-post där opt-out-flaggan är satt. Opt-out betyder uttryckligen att zonen avstår från att uttala sig om osignerade delegeringar i det täckta intervallet. Beviset säger “här finns ingen DS”, inte “dessa NS-poster är äkta”. En angripare hade kunnat bunta ihop ett förfalskat NS-set med genuina, återspelade NSEC3-poster från .net, och valideringen hade gått igenom ändå.

Det som utesluter en attack är alltså inte kryptografi utan data från källan snarare än från cachen: registrarens tidsstämplar och den oberoende rapporten. En förgiftad cache lämnar inga spår hos registraren.

Varken kissthenet.net, mynetname.net eller mikrotik.com har DS eller DNSKEY. Ingen av dem är signerad. Det är just därför den här sortens fel inte kan skiljas från en attack med kryptografiska medel.

Två dygn är ett val, inte en naturlag Link to heading

Delegerings-TTL:en sätts av registret, inte av domäninnehavaren. Det spelar stor roll för hur långlivat ett misstag blir. En mätning mot respektive toppdomäns namnservrar:

Toppdomän NS (delegering) DS
.org, .fr, .nl 3 600 3 600
.no 7 200 7 200
.de 86 400 86 400
.dk 86 400 7 200
.se 86 400 3 600
.com, .net 172 800 86 400

Samma misstag i .org hade varit ett timslångt problem. I .se ett dygn. I .net blev det två dygn — och eftersom TTL:en är fast kan domäninnehavaren inte göra något åt saken.

Lägg märke till DS-kolumnen. Flera register ger DS-posten samma TTL som delegeringen, men .se sätter den till en tjugofjärdedel: 3 600 mot 86 400. .dk gör något liknande och .com/.net halverar. Asymmetrin är rimlig. En felaktig eller inaktuell DS-post gör domänen mörk för varje validerande resolver, och det inträffar just vid nyckelbyten — en planerad och återkommande operation. Kort TTL gör den förutsägbar att genomföra och snabb att backa.

Men den skär åt fel håll för det som hände här. Posten som är lätt att rätta har kort livslängd. Posten som inte går att rätta i efterhand — delegeringen — har den långa. Att .se hade gett samma misstag ett dygns liv i stället för två är en tröst, men principen är densamma: det är NS-posten som saknar nödutgång.

Det påverkar hur man byter namnservrar. Standardreceptet — sänk TTL:en, gör bytet, höj den igen — gäller poster i den egna zonen. För en delegering i .com eller .net går steg ett inte att utföra. Kvar blir överlappning:

  1. Ladda zonen på de nya servrarna först och verifiera att de svarar med aa-flaggan.
  2. Sänk barnzonens apex-NS-TTL i förväg. Den styr du själv; här är den 3 600.
  3. Byt delegeringen hos registraren.
  4. Låt de gamla servrarna fortsätta betjäna zonen i minst 48 timmar.
  5. Höj TTL:erna igen.

Steg 4 är det enda skyddsnätet, och det saknades. En felaktig delegering går inte att ångra: rättningen når bara resolvrar som frågar föräldern på nytt, och det gör de inte förrän TTL:en löpt ut.

Det man skulle vilja att resolvrar gjorde Link to heading

En rimlig invändning är att resolvern borde ha märkt att den cachade delegeringen ledde till servrar som förnekade zonen, och gått tillbaka till föräldern.

Instinkten är redan standardiserad. RFC 2181 §5.4.1 rangordnar data efter trovärdighet, och auktoritativ data från barnzonen rankas högre än en hänvisning från föräldern. Texten är uttrycklig:

An authoritative answer from a reply should replace cached data that had been obtained from additional information in an earlier reply.

Problemet är att regeln aldrig kunde träda i kraft. För att få barnzonens auktoritativa NS-set måste resolvern fråga en server som betjänar zonen — och den enda vägvisaren den hade var den felaktiga delegeringen. Cirkeln sluter sig.

Det närmaste som finns i praktiken är Unbounds harden-referral-path, som hämtar barnzonens auktoritativa NS-poster och deras adresser under upplösningen. Dokumentationen är dock tydlig med begränsningarna: den är avstängd som standard “because it burdens the authority servers”, den är “not RFC standard”, och den beskrivs som experimentell. Den hade upptäckt att ns*.mikrotik.com inte betjänade zonen — men att därifrån gå tillbaka till föräldern och hämta en ny delegering är inte något någon resolver gör i dag.

Det vore ett rimligt beteende att önska sig: när samtliga servrar i en cachad delegering förnekar zonen, kasta delegeringen och fråga föräldern på nytt. Kostnaden vore låg, eftersom fallet är sällsynt. Vinsten vore att en rättad delegering faktiskt når fram innan TTL:en löpt ut.

Lösningen: ta bort beroendet Link to heading

Att tömma cachen fick igång tjänsten på några sekunder, men löste ingenting — nästa gång någon gör om samma misstag upprepas allt. Den riktiga åtgärden var att lyfta ut tredjeparten ur den kritiska vägen.

Nu sköter routern sin egen DDNS mot min egen DNS:

  • dyn.paftech.se är en delegerad subzon med dnssec-policy och inline-signing. Föräldern paftech.se förblir handredigerad; bara subzonen tar emot dynamiska uppdateringar.
  • Routern upptäcker att WAN-adressen ändrats via DHCP-klientens hook och gör en HTTPS-förfrågan till en endpoint på webbservern.
  • Servern läser routerns adress ur REMOTE_ADDR. Routern behöver aldrig få veta sitt eget IP — och den adress servern ser är per definition den adress proxyn måste ansluta till.
  • nsupdate med en TSIG-nyckel som via update-policy är låst till exakt ett namn och en posttyp. BIND signerar om zonen automatiskt.
  • Ett schemalagt anrop var femte minut självläker om en push missas.

En detalj som är lätt att missa: Apache cachar backendens adress per child-process så länge anslutningar återanvänds. Dokumentationen för mod_proxy är uttrycklig:

When connection reuse is enabled each backend domain is resolved (with a DNS query) only once per child process and cached for all further connections until the child is recycled.

Utan disablereuse=onProxyPass följer proxyn alltså inte med när adressen ändras, hur snabbt DNS än uppdateras. Verifierat skarpt: med parametern på plats gav proxyn 503 direkt när posten pekades fel, och 200 igen så fort den rättades — utan omstart.

Att ta med sig Link to heading

  1. Ett DNS-misstag lever så länge som förälderns TTL, inte så länge som misstaget. 45 minuter blev 48 timmar.
  2. Delegerings-TTL:en går inte att sänka i en gTLD. Säkerheten måste byggas på överlappning i stället.
  3. Övervaka namnet, inte adressen. Hade SmokePing pingat en IP hade grafen varit grön genom hela avbrottet.
  4. TTL-aritmetik är forensik. Den återstående TTL:en på en cachad post säger när den hämtades — här med minutprecision, långt efter att loggarna roterat bort.
  5. Ett beroende du inte kontrollerar kan du inte laga. Rättningen hade redan skett och hjälpte ändå inte. Det enda som hjälpte var att sluta vara beroende.