로그에는 잘못된 것이 없어 보입니다. 인터페이스는 올라와 있고, 리졸버는 VPN의 DNS 서버를 보고하며, 디스크의 파일에도 그것이 적혀 있습니다. 그런데도 내부 호스트 이름은 여전히 로컬 라우터를 통해 확인됩니다. 이 세 진술은 서로 모순되지 않습니다. 파일과 링크별 상태와 조회 경로는 우연히 같은 이름을 공유하는 서로 다른 객체이기 때문입니다. 다음은 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를 읽습니다. resolve이 들어 있는 hosts 줄은 질문을 nss-resolve로 보내고, 그것은 D-Bus를 통해 resolved에 묻고 링크별 답을 받습니다. files dns만 있는 줄은 /etc/resolv.conf가 지닌 주소로 질의를 보냅니다.

그 한 줄이 브라우저와 dig 사이의 대부분의 불일치를 설명합니다. dig은 resolv.conf을 직접 읽고 NSS를 전혀 건드리지 않으므로, 애플리케이션이 쓰는 경로가 아니라 stub이나 상위 서버를 시험합니다. getent hosts internal.example은 NSS를 거치며, 그 답을 dig의 답과 비교하는 것이 첫 번째로 쓸모 있는 측정입니다.

자체 리졸버를 품은 애플리케이션이나 고정 엔드포인트로 HTTPS를 통한 DNS를 말하는 애플리케이션은 두 경로를 모두 우회합니다. 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 연결은 자기 서버와 도메인을 자기 링크에 등록한 뒤 연결이 끊길 때 그것을 철회해야 합니다. NetworkManager의 resolved 통합과 클라이언트 자체 도우미 모두 그렇게 동작합니다. resolv.conf만 다시 쓰는 스크립트는 그렇지 않습니다.

이미 존재하는 링크에서는 런타임 설정이 그 메커니즘을 보여 줍니다. resolvectl dns tun0 10.40.0.53은 서버를 붙이고, resolvectl domain tun0 '~corp.example'은 라우팅 도메인을 붙입니다. 물결표가 짐을 지는 문자입니다. 그 접미사에 대한 질의를 이 링크로 가두고 나머지는 그대로 두며, 물결표가 없으면 같은 값이 검색 도메인이 되어 분리가 사라집니다. 그 주소들은 실험실 예시이며 영구 구성이 아닙니다. resolvectl query host.corp.example로 확인하고 어느 링크가 답했는지 읽으십시오.

등록된 서버에도 여전히 경로가 필요합니다. 질의가 올바른 링크에 도달한 뒤 시간이 초과된다면, 결함은 리졸버 소유권이 아니라 터널을 통한 도달 가능성에 있습니다.

동작하는 상태의 모습

건강한 결과는 세 보기 모두에서 일관됩니다. 링크별 서버가 VPN이 배포한 것과 맞고, 라우팅 도메인이 내부 접미사만 덮으며, 중요한 애플리케이션은 내부 이름을 확인하면서 공용 이름은 여전히 로컬 경로를 따릅니다. 질의 하나가 성공하는 것은 그 일치보다 약한 증거입니다.

그다음 연결을 끊고 두 질의를 다시 하십시오. 해제 뒤에도 도메인을 등록된 채로 두는 클라이언트는 몇 시간 뒤 내부 조회에서 실패하는 머신을 만듭니다. 열린 터널도, 분명한 원인도 없습니다.

관련 글