Din laptop är på VPN:en, dig service.internal.example svarar med en 10.x-adress, och curl avslutas fortfarande med Could not resolve host. Tunneln är inte problemet: den transporterade dig-frågan utan klagomål. Det som skiljer är vem som ställer frågan. dig skickar en fråga till en server som du anger, medan curl går genom operativsystemets uppslagskedja, och den kedjan har egna regler för vilken server som tillfrågas.

Två uppslagningar för samma namn

dig är en egen stub-resolver. Den läser /etc/resolv.conf, väljer en namnserver, skickar en enda A- eller AAAA-fråga och skriver ut svaret. Den hoppar helt över nsswitch och vet inget om det per-länk-tillstånd som systemd-resolved eller NetworkManager håller utanför filen, och det är därför ett dig-svar kan vara korrekt och ändå inte säga något om vad din applikation kommer att se.

Applikationer anropar getaddrinfo, och getaddrinfo går genom en längre kedja: modulordningen i nsswitch, sedan modulen som hanterar namnet och till sist resolvern som byggs från den aktuella länkkonfigurationen. På en systemd-maskin går frågan till 127.0.0.53 och systemd-resolved väljer en upstream-server per länk utifrån den routingdomän som är knuten till tun-gränssnittet.

Två kommandon skiljer de lagren utan att gissa:

getent ahosts service.internal.example
resolvectl query service.internal.example

getent använder samma väg som curl, så ett getent-fel bredvid en fungerande dig lokaliserar felet till uppslagspolicyn och inte till paketroutningen. resolvectl query skriver också ut gränssnittet och den upstream som gav svaret, vilket visar om tunnellänken eller den fysiska länken vann beslutet.

Läs den resolver som applikationen faktiskt använder

/etc/resolv.conf genereras på de flesta skrivbord, och en VPN-klient har tre sätt att förhålla sig till den: lägga till en namnserver först, skriva över filen eller lämna sina servrar till NetworkManager och låta daemonen skriva om den. Bara det tredje mönstret bevarar DNS per länk, eftersom servrarna förblir knutna till ett gränssnitt i stället för att plattas ut till en global lista.

På NetworkManager-system beror resultatet på backend i /etc/NetworkManager/NetworkManager.conf:

[main]
dns=systemd-resolved

Med dns=default slås klientens servrar samman i resolv.conf i den ordning som vinner, och ett privat uppslag kan fortfarande skickas till den publika resolvern. Med en backend som förstår länkar, systemd-resolved eller dnsmasq, förblir tunnelns servrar begränsade till tunneln och bara namn som matchar dess routingdomän går dit. Den begränsningen är vad split DNS innebär i praktiken, och det är den pusselbit som oftast saknas när en klient påstår sig stödja det.

Läs tillståndet innan du ändrar något:

resolvectl status
resolvectl dns tun0

Du vill se tun-gränssnittet listat med en DNS-server och en routingdomän för det privata suffixet, visat som ~internal.example. Ingen server på länken betyder att klienten inte skickade något. En server utan domän betyder att uppslag antingen alla skickas dit eller inga, beroende på klienten, och ingetdera är split DNS.

Där nsswitch avslutar uppslaget för tidigt

En standardrad på ett skrivbord ser ut så här:

hosts: files mdns4_minimal [NOTFOUND=return] resolve [!UNAVAIL=return] dns myhostname

mdns4_minimal hanterar multicast-DNS och gör anspråk på varje namn som slutar på .local. En Kubernetes-tjänst som api.infra.svc.cluster.local slutar också på .local. När mDNS-modulen rapporterar NOTFOUND avslutar åtgärden [NOTFOUND=return] hela uppslaget i stället för att fortsätta till dns-modulen, så namnet skickas aldrig till VPN-resolvern. dig kringgår filen helt, och det är precis därför de två verktygen är oense.

Det är den skarpaste kontrasten i det här problemet, och den kokar ner till ett enda ord i en konfigurationsfil som är äldre än din VPN. Att ändra åtgärden till [NOTFOUND=continue] behåller mDNS för skrivare och lokala enheter samtidigt som DNS kan se namnet. Att ersätta raden med hosts: files dns fungerar också och är grövre: det tar bort .local-uppslagningen för skrivaren du faktiskt använder.

Namn som bara finns i ett enda nätverk

Privata zoner är auktoritativa på exakt ett ställe. Ett AWS-namn som ip-10-0-4-31.eu-west-1.compute.internal besvaras via den regionens VPC-resolver, och bara så länge din VPN-endpoint ligger i samma region som resursen. Anslut till en endpoint i Frankfurt, slå upp ett compute.internal-namn från Irland, och ingen lokal inställning hjälper: zonen som håller posten går inte att nå därifrån.

Sökdomäner ger spegelbilden. Ett bart service kompletteras med det suffix som resolvern tycker gäller, och med en VPN-länk kan den lokala domänen vinna över företagets. Slå upp med en punkt på slutet för att ta bort gissningen, getent ahosts service.internal.example., och om det fungerar medan det korta namnet misslyckas, är rättelsen sökdomänen på tunneln, inte serveradressen.

Hanterade klienter lägger ett tredje lager ovanpå operativsystemets resolver. En Windows-VPN-profil kan bära sin egen namnupplösningspolicy, regler per app och en NRPT-tabell som gäller även när tunneln är nere. Applikationer med egen DNS-cache, vanligt i .NET- och Java-runtimes, behåller ett föråldrat fel långt efter att kedjan är åtgärdad. Apple-klienter konfigurerar split DNS via match-domäner i DNS-inställningarna i stället för via resolv.conf, och Chromium eller Firefox i secure-DNS-läge kan kringgå systemets resolver för publika namn. Microsofts referens om VPN-namnsupplösning beskriver Windows-sidan av det policylagret.

Bestäm vilket lager som ska åtgärdas

Arbeta från symtomet till lagret i stället för att prova åtgärder i ordning:

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

Två rader bär det mesta. En timeout är ett nåbarhetsproblem: den privata resolvern svarar inte genom tunneln, så kontrollera rutten till dess adress och om DNS-servern på länken är den du tror. NXDOMAIN är ett auktoritetsproblem: något svarade och kan inte namnet, vilket pekar på zonen, regionen eller en resolver som aldrig borde ha fått frågan.

Den sista raden är den folk missar eftersom båda halvorna ser ut som framgång. Ett kort namn som löses annorlunda än FQDN är en suffix-mismatch, och att återansluta tunneln ändrar inte vilket suffix klienten skickar.

Fånga tillståndet innan du redigerar en konfigurationsfil, så att du kan se om ändringen gjorde något:

resolvectl status > before.txt
getent ahosts service.internal.example >> before.txt

Kör kontrollerna i den här ordningen och stanna vid den första som misslyckas. getent och resolvectl query måste stämma överens; om getent misslyckas medan dig svarar, stanna i uppslagskedjan och rör inte routningen. resolvectl status måste visa en DNS-server och en routingdomän på tun-gränssnittet. nsswitch får inte återvända tidigt på NOTFOUND för det suffix du testar. Namnet måste vara auktoritativt i nätverket som din endpoint är ansluten till, testat med en punkt på slutet. Applikationen får inte hålla en egen cache eller en secure-DNS-överstyrning.

Åtgärda bara det första lagret som misslyckas och kör sedan samma kommandon igen. Två ändringar samtidigt gör att du inte kan säga vilken som spelade roll, och nästa avbrott börjar undersökningen från noll.

Relaterade artiklar