De ce numele de gazdă interne se rezolvă în dig dar eșuează în curl pe un VPN
dig răspunde, dar curl raportează că nu poate rezolva gazda. Comparați cele două lanțuri de rezolvare, apoi remediați nsswitch, DNS-ul pe legătură sau un sufix de căutare greșit.
Laptopul tău este pe VPN, dig service.internal.example răspunde cu o adresă 10.x, iar curl iese în continuare cu Could not resolve host. Tunelul nu este problema: a transportat interogarea dig fără să se plângă. Ceea ce diferă este cine pune întrebarea. dig trimite o interogare către un server pe care îl numești tu, în timp ce curl trece prin lanțul de rezolvare al sistemului de operare, iar acel lanț are propriile reguli despre ce server este întrebat.
Două rezolvări pentru același nume
dig este propriul său stub resolver. Citește /etc/resolv.conf, alege un server de nume, trimite o singură interogare A sau AAAA și afișează răspunsul. Ocolește complet nsswitch și nu știe nimic despre starea pe legătură pe care systemd-resolved sau NetworkManager o țin în afara acelui fișier, motiv pentru care un răspuns dig poate fi corect și totuși să nu-ți spună nimic despre ce va vedea aplicația ta.
Aplicațiile apelează getaddrinfo, iar getaddrinfo parcurge un lanț mai lung: ordinea modulelor din nsswitch, apoi modulul care tratează numele și apoi resolverul construit din configurația curentă a legăturii. Pe o mașină systemd interogarea merge la 127.0.0.53, iar systemd-resolved alege un server upstream pe legătură pe baza domeniului de rutare atașat interfeței tun.
Două comenzi separă acele straturi fără a ghici:
getent ahosts service.internal.example
resolvectl query service.internal.example
getent folosește aceeași cale ca curl, așa că un eșec getent lângă un dig funcțional localizează defectul în politica de căutare, nu în rutarea pachetelor. resolvectl query afișează și interfața și upstreamul care au dat răspunsul, ceea ce arată dacă legătura tunelului sau legătura fizică a câștigat decizia.
Citește resolverul pe care aplicația îl folosește efectiv
/etc/resolv.conf este generat pe majoritatea desktopurilor, iar un client VPN are trei moduri de a interacționa cu el: să adauge un server de nume în față, să suprascrie fișierul sau să-și dea serverele lui NetworkManager și să lase demonul să-l rescrie. Doar al treilea model păstrează DNS pe legătură, pentru că serverele rămân atașate unei interfețe în loc să fie aplatizate într-o listă globală.
Pe sistemele NetworkManager rezultatul depinde de backendul din /etc/NetworkManager/NetworkManager.conf:
[main]
dns=systemd-resolved
Cu dns=default serverele clientului sunt contopite în resolv.conf în ordinea care câștigă, iar o căutare privată poate fi încă trimisă resolverului public. Cu un backend care înțelege legăturile, systemd-resolved sau dnsmasq, serverele tunelului rămân limitate la tunel și doar numele care corespund domeniului său de rutare merg acolo. Această limitare este ceea ce înseamnă split DNS în practică și este piesa care lipsește cel mai des când un client susține că o suportă.
Citește starea înainte de a modifica ceva:
resolvectl status
resolvectl dns tun0
Vrei să vezi interfața tun listată cu un server DNS și un domeniu de rutare pentru sufixul privat, afișat ca ~internal.example. Niciun server pe legătură înseamnă că clientul nu a trimis nimic. Un server fără domeniu înseamnă că toate căutările merg acolo sau niciuna, în funcție de client, și niciuna dintre cele două nu este split DNS.
Unde nsswitch încheie căutarea prea devreme
O linie standard de desktop arată astfel:
hosts: files mdns4_minimal [NOTFOUND=return] resolve [!UNAVAIL=return] dns myhostname
mdns4_minimal gestionează DNS multicast și revendică orice nume care se termină în .local. Un serviciu Kubernetes precum api.infra.svc.cluster.local se termină și el în .local. Când modulul mDNS raportează NOTFOUND, acțiunea [NOTFOUND=return] încheie întreaga căutare în loc să continue către modulul dns, așa că numele nu este trimis niciodată resolverului VPN. dig ocolește complet fișierul și exact de aceea cele două instrumente nu sunt de acord.
Acesta este cel mai ascuțit contrast din această problemă și se reduce la un singur cuvânt dintr-un fișier de configurare mai vechi decât VPN-ul tău. Schimbarea acțiunii în [NOTFOUND=continue] păstrează mDNS pentru imprimante și dispozitive locale, permițând totodată DNS să vadă numele. Înlocuirea liniei cu hosts: files dns funcționează și ea și este mai brutală: elimină rezolvarea .local pentru imprimanta pe care o folosești efectiv.
Nume care trăiesc într-o singură rețea
Zonele private sunt autoritare într-un singur loc. Un nume AWS precum ip-10-0-4-31.eu-west-1.compute.internal este rezolvat prin resolverul VPC al acelei regiuni și numai cât timp endpointul VPN este în aceeași regiune cu resursa. Conectează-te la un endpoint din Frankfurt, interoghează un nume compute.internal din Irlanda și nicio setare locală nu va ajuta: zona care deține înregistrarea nu este accesibilă de acolo.
Domeniile de căutare produc imaginea inversă. Un service simplu este completat cu sufixul pe care resolverul îl consideră aplicabil, iar cu o legătură VPN domeniul local poate învinge cel corporativ. Interoghează cu un punct final pentru a elimina presupunerea, getent ahosts service.internal.example., și dacă asta funcționează în timp ce numele scurt eșuează, corectarea este domeniul de căutare pe tunel, nu adresa serverului.
Clienții gestionați adaugă un al treilea strat deasupra resolverului sistemului. Un profil VPN Windows poate purta propria politică de rezolvare a numelor, reguli per aplicație și un tabel NRPT care se aplică chiar și când tunelul este oprit. Aplicațiile cu cache DNS propriu, obișnuite în runtime-urile .NET și Java, păstrează un eșec învechit mult după ce lanțul este reparat. Clienții Apple configurează split DNS prin domenii de potrivire în setările DNS în loc de resolv.conf, iar Chromium sau Firefox în modul DNS securizat pot ocoli resolverul sistemului pentru nume publice. Referința Microsoft despre rezolvarea numelor VPN descrie partea Windows a acelui strat de politică.
Decide ce strat să repari
Pornește de la simptom spre strat în loc să încerci corecturi în ordine:
| Symptom | What it means | Check first |
|---|---|---|
| dig answers, getent fails | lookup chain, not the packet path | nsswitch line and resolvectl status |
| Timeout rather than NXDOMAIN | private resolver unreachable through the tunnel | resolvectl dns tun0, route to that address |
| NXDOMAIN | an authority answered and does not know the name | zone owner, endpoint region |
| FQDN works, short name fails | search suffix mismatch | resolvectl domain on the tun link |
| getent works, app fails | app cache or its own resolver | restart the app, check secure DNS |
Două rânduri poartă cea mai mare greutate. Un timeout este o problemă de accesibilitate: resolverul privat nu răspunde prin tunel, așa că verifică ruta către adresa lui și dacă serverul DNS de pe legătură este cel pe care crezi. NXDOMAIN este o problemă de autoritate: ceva a răspuns și nu cunoaște numele, ceea ce indică zona, regiunea sau un resolver care nu ar fi trebuit să primească niciodată interogarea.
Ultimul rând este cel pe care oamenii îl ratează pentru că ambele jumătăți par un succes. Un nume scurt care se rezolvă diferit de FQDN este o nepotrivire de sufix, iar reconectarea tunelului nu va schimba ce sufix trimite clientul.
Capturează starea înainte de a edita un fișier de configurare, ca să poți spune dacă editarea a făcut ceva:
resolvectl status > before.txt
getent ahosts service.internal.example >> before.txt
Rulează verificările în această ordine și oprește-te la prima care eșuează. getent și resolvectl query trebuie să fie de acord; dacă getent eșuează în timp ce dig răspunde, rămâi în lanțul de rezolvare și nu atinge rutarea. resolvectl status trebuie să arate un server DNS și un domeniu de rutare pe interfața tun. nsswitch nu trebuie să revină devreme cu NOTFOUND pentru sufixul pe care îl testezi. Numele trebuie să fie autoritar în rețeaua de care este atașat endpointul tău, testat cu un punct final. Aplicația nu trebuie să păstreze propriul cache sau o suprascriere DNS securizată.
Repară doar primul strat care eșuează, apoi rulează din nou aceleași comenzi. Două modificări deodată te lasă fără a putea spune care a contat, iar următoarea defecțiune începe investigația de la zero.