VPN을 끊으면 ping 1.1.1.1은 여전히 응답하지만, 페이지가 마침내 포기할 때까지 모든 이름 조회가 멈춘다. 그러면 cat /etc/resolv.conf에 터널 안에만 존재했던 네임서버가 보통 목록 첫 자리에 나타난다. 그 줄을 지우면 십여 분쯤 고쳐진 듯 보이지만, 절전, Wi-Fi 로밍, 재시작 중 하나로 다시 돌아온다. 편집한 파일은 설정이 아니다. 그것은 다른 구성 요소가 소유한 링크 상태의 표현일 뿐이다.

오래된 항목 하나가 조회마다 몇 초를 잡아먹는 이유

glibc 리졸버는 네임서버 목록을 순서대로 훑고 다음 서버를 시도하기 전에 타임아웃이 끝나기를 기다린다. 그리고 resolv.conf(5)는 그 목록을 세 개 항목으로 제한한다. 터널이 사라진 뒤 10.8.0.1 같은 터널 측 리졸버를 목록 맨 위에 두면, 응답하는 서버로 넘어가기 전에 모든 조회가 타임아웃을 치른다. 증상이 DNS 오류가 아니라 죽은 연결처럼 보이는 이유가 여기 있다. 브라우저는 멈추고 시간 초과가 나며, 때로는 나중에 낡아 버릴 캐시된 주소를 내놓는다.

«인터넷이 망가졌다»는 불평이 오는 이유도 이걸로 설명된다. IP 주소로 ping이 통하면 링크는 정상이라는 뜻이라, 고장이 애플리케이션 문제처럼 보인다. 리졸버 목록은 가장 먼저 읽고 가장 나중에 편집할 대상이다.

속설: resolv.conf는 설정 파일이다

바꾸기 전에 그 경로가 실제로 무엇인지 물어보라.

ls -l /etc/resolv.conf
readlink -f /etc/resolv.conf

systemd-resolved 호스트에서는 127.0.0.53을 가리키는 스텁 파일로 가는 심볼릭 링크이고, 실제 서버는 resolvectl이 출력하는 링크별 상태에 산다. NetworkManager 호스트에서는 NetworkManager가 활성 연결에서 기록하는 파일일 수 있다. 일부 벤더 클라이언트에서는 연결할 때 그 자리에서 다시 쓰이고, 운이 나쁘면 연결 해제 시 결코 복원되지 않는 평범한 파일이다. 이들은 서로 다른 세 주인이며, 앞의 둘을 손으로 편집하면 아무 일도 없거나 다음 네트워크 이벤트까지만 버틴다.

resolvectl status는 진실을 말하는 뷰다. 링크별 DNS 서버, 라우팅 도메인, 터널 링크의 존속 여부를 보여 준다. resolvectl query example.com은 더 나아가 어느 인터페이스가 응답했는지 보여 준다. 파일을 건드리기 전에 둘 다 사용하고, 자체 보안 DNS를 가진 브라우저는 이 모두를 무시한다는 점을 기억하라.

무엇이든 편집하기 전에 소유자를 찾아라

보통 세 구성 요소가 같은 설정을 두고 다툰다. systemd-resolved, NetworkManager, 그리고 VPN 클라이언트가 설치한 것이다. 먼저 살아 있는 쪽을 식별하라.

systemctl is-active systemd-resolved
nmcli connection show --active
ps aux | grep -E "openvpn|wg-quick|vpn"

resolved가 활성이고 심볼릭 링크가 그 스텁을 가리키면 전역 파일은 생성된 출력이며 편집은 겉치레에 불과하다. NetworkManager가 연결을 소유하면 서버는 연결 프로필에 속하므로, 해법은 파일 편집이 아니라 nmcli 속성 변경이다. 벤더 데몬이 돌고 있으면 다음 연결까지 살아남는 것은 그 자체의 복원 경로뿐이다.

클라이언트가 남긴 링크 상태를 정리하라

죽은 클라이언트는 정리 훅을 실행할 수 없다. SIGKILL은 아예 잡을 수 없고 SIGINT와 SIGTERM은 잡을 수 있으므로, 강제 종료 후 «그냥 멈춘» 터널은 인터페이스 등록과 리졸버 항목을 모두 그대로 남기는 일이 많다. 특정 PID를 찾아 그 프로세스에만 시그널을 보내라.

ps aux | grep -E "openvpn|wg-quick"
sudo kill <pid>
resolvectl revert tun0

전체 일치 목록을 읽기 전에 pkill -f vpn이나 killall vpn에 손을 대지 마라. -f는 명령줄 전체와 일치하므로 래퍼 스크립트, 편집기 세션, 또는 경로에 그 문자열이 들어 있는 무관한 프로세스까지 끝낼 수 있다. 작업 도중 잘못된 프로세스를 끝내는 것이 리졸버 문제를 잃어버린 파일로 만드는 길이다.

resolvectl revert는 그 링크의 런타임 DNS 구성을 재설정한다. 리졸버를 재시작하는 것보다 좁고, 전역 파일을 편집하는 것보다 훨씬 좁다. 터널 링크가 완전히 사라졌다면 systemctl restart NetworkManager가 연결 상태를 다시 세운다. 소스 스레드가 자꾸 돌아오는 지점이 바로 이 단계다. Restart=always가 있는 서비스 관리자는 주의해야 한다. 죽은 클라이언트와 그 DNS 설정을 그대로 되돌려 놓을 수 있다.

다른 방향: 밀어 넣었지만 도착하지 않은 DNS

오래된 항목의 거울상은 쓸 만한 리졸버가 전혀 없는 터널이다. 프로필의 DNS 옵션은 요청일 뿐이고, 연결을 관리하는 구성 요소가 그것을 리졸버에 넘겨야 한다. 로그에 서버는 받았다고 나오는데 터널 링크가 DNS 설정을 하나도 싣지 않는다면, 빠진 것은 통합이지 서버가 아니다.

넘겨주기를 손으로 해 보라. «서버가 틀렸다»와 «아무도 적용하지 않았다»가 갈린다.

resolvectl dns tun0 10.10.10.10
resolvectl domain tun0 ~.

모두를 잡는 라우팅 도메인은 모든 이름을 그 링크로 보낸다. 전체 터널 클라이언트가 원하는 것이고, 분할 DNS가 써서는 안 되는 것이다. 이 설정들은 다음 연결에서 사라진다. NetworkManager에서 영구로 만들려면 자동 DNS를 끄고, 연결에 서버를 설정하고, 도메인을 추가하라.

nmcli connection modify office ipv4.ignore-auto-dns yes
nmcli connection modify office ipv4.dns 10.10.10.10
nmcli connection modify office ipv4.dns-search "~."

내부 이름에만 응답해야 하는 회사 터널이라면, 모두를 잡는 대신 선행 물결표를 붙여 그 영역들을 나열하라. 그러면 공용 이름은 계속 로컬 리졸버를 쓴다.

다음 장애가 되는 임시방편

연결 전에 resolv.conf를 복사하고 나중에 되돌리는 것은 런북에 쓸 수 있는 정당한 한 줄이지만, 수리가 아니라 임시방편이다. 크래시나 강제 재부팅은 복원을 건너뛰므로 정작 중요한 순간에 실패한다. 다음 사람이 그 위에 쌓지 않도록 임시방편이라고 적어 두라.

systemd-resolved를 끄고 공용 서버를 하드코딩하는 것도 인기 있는 수법이다. 연결과 해제가 예측 가능해지고 다른 모든 네트워크에서 링크별 DNS가 사라진다. 그 결과 내부 이름의 분할 DNS가 깨지고, 조회를 안쪽에 가두려는 터널의 목적이 무너진다. resolvconf를 설치하고 공용 서버를 고정하는 것도 층이 하나 더 늘 뿐 비용은 같다. 둘 중 무엇이든 진단으로 다뤄라. 탐색이 돌아온다면 공용 서버가 옳다는 뜻이 아니라 리졸버 통합이 원인임을 배운 것이다.

지속되는 해법은 계층마다 소유자를 하나로 두는 것이다. 터널 링크의 DNS를 어느 구성 요소가 갖고 전역 기본값을 어느 쪽이 갖는지 정해 적어 두고, 나머지를 경로에서 빼라. 그다음 연결 해제 후 VPN만 해석하는 이름으로 확인하라. 사설 이름이 여전히 응답하면 등록이 제거되지 않은 것이고, 아무것도 해석되지 않으면 파일이 더 이상 없는 서버를 가리키는 것이다. 둘 다 브라우저를 열기 전에 resolvectl에서 보인다.

관련 글