Защо resolv.conf показва DNS на VPN, а заявките го пренебрегват
На хост с resolved файлът е изглед, а не състоянието. Проследете stub, сървърите на всяка връзка и реда nsswitch, за да видите кой от тях наистина използва заявката.
В журналите нищо не изглежда сбъркано: интерфейсът е активен, резолверът съобщава DNS сървъра на VPN и файлът на диска го изброява. И все пак вътрешно име на хост се разрешава през локалния рутер. Тези три твърдения не си противоречат, защото файлът, състоянието на всяка връзка и пътят на заявката са различни обекти, които случайно споделят име. Следва пътят, който заявката действително изминава на хост с systemd-resolved.
Три обекта с името resolv.conf
На хост с resolved /etc/resolv.conf обикновено е символна връзка към /run/systemd/resolve/stub-resolv.conf. Тази цел съдържа адреса на локален stub слушател, 127.0.0.53, плюс домейните за търсене, изчислени от resolved. Това е изглед на състоянието, а не самото състояние.
Състоянието живее в конфигурацията на resolved за всяка връзка, затова resolvectl status може да се разминава с файла и пак да е прав. Всяка връзка носи свои сървъри и свои домейни за маршрутизиране, а stub се обръща към тези данни, когато пристигне заявка. Ръководството на systemd-resolved описва генерираните файлове и модела за всяка връзка; разделението между изглед и състояние е частта, която си струва да запомните. Промяната на целта на символната връзка променя това, което четат традиционните клиенти, а не това, което резолверът знае.
Трети обект, /run/systemd/resolve/resolv.conf, изравнява всяка връзка в един списък със сървъри. Програма, насочена към този файл, изпраща заявките направо към изброените сървъри и пропуска stub, така че политиката за всяка връзка вече не важи за нея. Затова две програми на една и съща машина могат да отговарят различно на едно и също име и нито една от тях не е счупена.
Проследете една заявка през NSS
glibc чете /etc/nsswitch.conf, за да реши кой отговаря на име. Редът hosts, който съдържа resolve, насочва въпроса към nss-resolve, който пита resolved през D-Bus и получава отговора за връзката. Ред само с files dns изпраща заявката към адреса, който държи /etc/resolv.conf.
Този един ред обяснява повечето разминавания между браузър и dig. dig чете resolv.conf директно и никога не докосва NSS, затова тества stub или по-горен сървър, а не пътя, който приложението използва. getent hosts internal.example минава през NSS и сравняването на отговора му с този на dig е първото полезно измерване.
Приложенията, които вграждат собствен резолвер или говорят DNS през HTTPS към фиксирана крайна точка, заобикалят и двата пътя. Нищо в resolv.conf или в състоянието на resolved не ги ограничава.
Проверете кой притежава файла и накъде сочи
Прочетете веригата от връзки, преди да промените нещо, защото собствеността решава кой файл е меродавен. Всяка команда по-долу само чете състояние.
stat -c '%N' /etc/resolv.conf
resolvectl status --no-pager
grep '^hosts' /etc/nsswitch.conf
ls -l /run/systemd/resolve/
Първият ред отпечатва връзката и целта ѝ заедно, а обикновен файл, притежаван от VPN скрипт, означава, че резолверът никога не приема този запис като вход. Изходът на статуса изброява сървърите за всяка връзка плюс домейните за маршрутизиране, които решават накъде отива всяка заявка. Редът nsswitch казва дали stub се пита изобщо, така че адрес на stub във файла без resolve там означава, че заявките стигат до локалния слушател, докато нищо не управлява разделянето. Последният списък също е важен, защото хост без stub-resolv.conf не изпълнява описаната тук схема.
Ако веригата завършва във файл, управляван от друг пакет, VPN клиентът и този пакет са двамата собственици, които трябва да се съгласуват. Замяната на връзката изхвърля работата на единия собственик и оставя работата на другия на място.
Поправка при собственика на връзката
VPN връзката трябва да регистрира своите сървъри и домейни на собствената си връзка, а после да ги оттегля при прекъсване. Интеграцията на resolved в NetworkManager и собственият помощник на клиента работят точно така; скрипт, който само пренаписва resolv.conf, не го прави.
На вече съществуваща връзка механизмът се вижда от настройките по време на работа. resolvectl dns tun0 10.40.0.53 прикачва сървър, а resolvectl domain tun0 '~corp.example' прикачва домейн за маршрутизиране. Тилдата е носещият знак: тя ограничава заявките за този суфикс до тази връзка и оставя всичко останало, докато същата стойност без нея става домейн за търсене и разделянето се изпарява. Тези адреси са лабораторни примери, а не постоянна конфигурация. Потвърдете с resolvectl query host.corp.example и прочетете коя връзка е отговорила.
Регистрираният сървър все още се нуждае от маршрут. Когато заявката стигне до правилната връзка и изтече времето, проблемът е в достъпността през тунела, а не в собствеността на резолвера.
Как изглежда работещото състояние
Здравият резултат е съгласуван в трите изгледа: сървърите за всяка връзка съвпадат с това, което VPN е раздал, домейните за маршрутизиране покриват само вътрешни суфикси, а важното приложение разрешава вътрешно име, докато публичното име все още следва локалния път. Една успешна заявка доказва по-малко от това съгласие.
После прекъснете връзката и повторете двете заявки. Клиент, който остави домейните си регистрирани след разпадането, създава машина, която часове по-късно се проваля при вътрешни заявки, без отворен тунел и без очевидна причина.