Twój laptop jest w VPN, dig service.internal.example odpowiada adresem 10.x, a curl nadal kończy się z Could not resolve host. Tunel nie jest problemem: przeniósł zapytanie dig bez skargi. Różnica polega na tym, kto zadaje pytanie. dig wysyła jedno zapytanie do jednego wskazanego przez ciebie serwera, natomiast curl przechodzi przez łańcuch wyszukiwania systemu operacyjnego, a ten łańcuch ma własne zasady dotyczące tego, który serwer jest pytany.

Dwa wyszukiwania tej samej nazwy

dig jest własnym stub resolverem. Czyta /etc/resolv.conf, wybiera serwer nazw, wysyła jedno zapytanie A lub AAAA i wypisuje odpowiedź. Całkowicie pomija nsswitch i nie wie nic o stanie na łączu, który systemd-resolved lub NetworkManager trzymają poza tym plikiem, dlatego odpowiedź dig może być poprawna i mimo to nic nie mówić o tym, co zobaczy twoja aplikacja.

Aplikacje wywołują getaddrinfo, a getaddrinfo przechodzi dłuższy łańcuch: kolejność modułów nsswitch, potem moduł obsługujący nazwę, a na końcu resolver zbudowany z bieżącej konfiguracji łącza. Na maszynie z systemd zapytanie trafia do 127.0.0.53, a systemd-resolved wybiera serwer nadrzędny na łącze na podstawie domeny routingu przypisanej do interfejsu tun.

Dwie komendy rozdzielają te warstwy bez zgadywania:

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

getent używa tej samej ścieżki co curl, więc błąd getent obok działającego dig lokalizuje usterkę w polityce wyszukiwania, a nie w routingu pakietów. resolvectl query wypisuje też interfejs i serwer nadrzędny, który dał odpowiedź, co pokazuje, czy decyzję wygrało łącze tunelu, czy łącze fizyczne.

Odczytaj resolver, którego aplikacja naprawdę używa

/etc/resolv.conf jest generowany na większości pulpitów, a klient VPN ma trzy sposoby obchodzenia się z nim: dopisać serwer nazw na początku, nadpisać plik albo przekazać swoje serwery do NetworkManagera i pozwolić demonowi go przepisać. Tylko trzeci wzorzec zachowuje DNS na łącze, ponieważ serwery pozostają przypisane do interfejsu, zamiast zostać spłaszczone do jednej globalnej listy.

W systemach NetworkManager wynik zależy od backendu w /etc/NetworkManager/NetworkManager.conf:

[main]
dns=systemd-resolved

Przy dns=default serwery klienta są scalane w resolv.conf w kolejności, która wygrywa, a prywatne zapytanie nadal może trafić do publicznego resolvera. Z backendem rozumiejącym łącza, systemd-resolved lub dnsmasq, serwery tunelu pozostają ograniczone do tunelu i trafiają tam tylko nazwy pasujące do jego domeny routingu. To ograniczenie jest tym, co split DNS oznacza w praktyce, i to jego najczęściej brakuje, gdy klient twierdzi, że go obsługuje.

Odczytaj stan, zanim cokolwiek zmienisz:

resolvectl status
resolvectl dns tun0

Chcesz zobaczyć interfejs tun z serwerem DNS i domeną routingu dla prywatnego sufiksu, wyświetlanymi jako ~internal.example. Brak serwera na łączu oznacza, że klient nic nie wysłał. Serwer bez domeny oznacza, że zapytania idą albo wszystkie tam, albo żadne, zależnie od klienta, i żadna z tych sytuacji nie jest split DNS.

Gdzie nsswitch kończy wyszukiwanie za wcześnie

Standardowa linia pulpitu wygląda tak:

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

mdns4_minimal obsługuje multicast DNS i rości sobie prawo do każdej nazwy kończącej się na .local. Usługa Kubernetes, taka jak api.infra.svc.cluster.local, też kończy się na .local. Gdy moduł mDNS zgłasza NOTFOUND, akcja [NOTFOUND=return] kończy całe wyszukiwanie zamiast przejść do modułu dns, więc nazwa nigdy nie trafia do resolvera VPN. dig omija plik całkowicie i dokładnie dlatego oba narzędzia się nie zgadzają.

To najostrzejszy kontrast w tym problemie i sprowadza się do jednego słowa w pliku konfiguracyjnym starszym niż twój VPN. Zmiana akcji na [NOTFOUND=continue] zachowuje mDNS dla drukarek i urządzeń lokalnych, pozwalając DNS zobaczyć nazwę. Zastąpienie linii przez hosts: files dns też działa i jest grubsze: usuwa rozwiązywanie .local dla drukarki, której naprawdę używasz.

Nazwy, które żyją w dokładnie jednej sieci

Strefy prywatne są autorytatywne w dokładnie jednym miejscu. Nazwa AWS, taka jak ip-10-0-4-31.eu-west-1.compute.internal, jest odpowiadana przez resolver VPC tego regionu i tylko dopóki twój punkt końcowy VPN jest w tym samym regionie co zasób. Połącz się z punktem końcowym we Frankfurcie, zapytaj o nazwę compute.internal z Irlandii, a żadne lokalne ustawienie nie pomoże: strefa trzymająca rekord nie jest stamtąd osiągalna.

Domeny wyszukiwania dają obraz odwrotny. Gołe service jest uzupełniane sufiksem, który resolver uważa za właściwy, a przy łączu VPN domena lokalna może wygrać z korporacyjną. Pytaj z kropką na końcu, aby usunąć zgadywanie, getent ahosts service.internal.example., i jeśli to działa, podczas gdy krótka nazwa zawodzi, poprawką jest domena wyszukiwania na tunelu, a nie adres serwera.

Zarządzani klienci dodają trzecią warstwę nad resolverem systemu. Profil VPN Windows może nieść własną politykę rozwiązywania nazw, reguły dla aplikacji i tabelę NRPT, która obowiązuje nawet przy wyłączonym tunelu. Aplikacje z własnym cache DNS, częste w środowiskach .NET i Java, utrzymują nieaktualny błąd długo po naprawieniu łańcucha. Klienci Apple konfigurują split DNS przez domeny dopasowania w ustawieniach DNS, a nie przez resolv.conf, a Chromium lub Firefox w trybie bezpiecznego DNS mogą omijać resolver systemu dla nazw publicznych. Dokumentacja Microsoftu dotycząca rozwiązywania nazw VPN opisuje stronę Windows tej warstwy polityki.

Zdecyduj, którą warstwę naprawić

Pracuj od objawu do warstwy, zamiast próbować poprawek po kolei:

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

Dwa wiersze niosą większość ciężaru. Limit czasu to problem osiągalności: prywatny resolver nie odpowiada przez tunel, więc sprawdź trasę do jego adresu i to, czy serwer DNS na łączu jest tym, którego myślisz. NXDOMAIN to problem autorytetu: coś odpowiedziało i nie zna nazwy, co wskazuje na strefę, region lub resolver, który nigdy nie powinien otrzymać zapytania.

Ostatni wiersz to ten, który ludzie pomijają, ponieważ obie połowy wyglądają na sukces. Krótka nazwa rozwiązująca się inaczej niż FQDN to niezgodność sufiksu, a ponowne połączenie tunelu nie zmieni tego, który sufiks wysyła klient.

Zapisz stan przed edycją pliku konfiguracyjnego, aby móc powiedzieć, czy edycja coś zmieniła:

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

Wykonaj kontrole w tej kolejności i zatrzymaj się na pierwszej, która zawiedzie. getent i resolvectl query muszą się zgadzać; jeśli getent zawodzi, podczas gdy dig odpowiada, pozostań w łańcuchu wyszukiwania i nie ruszaj routingu. resolvectl status musi pokazywać serwer DNS i domenę routingu na interfejsie tun. nsswitch nie może wracać zbyt wcześnie z NOTFOUND dla testowanego sufiksu. Nazwa musi być autorytatywna w sieci, do której podłączony jest twój punkt końcowy, sprawdzona z kropką na końcu. Aplikacja nie może trzymać własnego cache ani nadpisania bezpiecznego DNS.

Napraw tylko pierwszą zawodzącą warstwę, a potem uruchom ponownie te same polecenia. Dwie zmiany naraz nie pozwalają powiedzieć, która miała znaczenie, a następna awaria zaczyna dochodzenie od zera.

Powiązane artykuły