DNS가 계속 원래대로 돌아가고, VPN 재연결이 수정한 내용을 되돌리며, 결국 팀의 누군가가 파일을 잠그자고 제안합니다. 그 흐름은 증상이지 해결책이 아닙니다. 두 구성 요소가 모두 /etc/resolv.conf을 자기 것이라고 믿고 있고, 마지막에 쓰는 쪽이 이깁니다. 파일을 잠그는 것은 소유권 충돌을 해결하지 못하고 숨길 뿐이며, 정작 원했던 구성 요소를 망가뜨립니다. 아래 네 가지 점검을 순서대로 수행하고, 파일이 아니라 그것을 소유한 계층에서 구성을 고치세요.

점검 1: 심볼릭 링크인지, 무엇을 가리키는지

편집하는 경로가 파일이 아닐 수도 있으므로 파일 자체부터 시작하세요. 리졸버의 런타임 디렉터리를 가리키는 심볼릭 링크는 클라이언트 스크립트가 다시 쓰는 일반 파일과 완전히 다르게 동작합니다:

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

systemd-resolved의 stub 경로로 가는 링크는 파일 내용이 생성된다는 뜻이며, 직접 편집하면 다음 이벤트에서 덮어써집니다. nameserver 줄이 있는 일반 파일은 보통 연결 시 실행되는 클라이언트나 DHCP 훅이 씁니다. 둘 다 정당합니다. 중요한 것은 무엇이든 바꾸기 전에 어느 쪽인지 아는 것입니다.

점검 2: 어떤 데몬이 실행 중인지

설치된 것이 아니라 실제로 활성화된 것을 확인하세요. 최신 배포판에는 여러 리졸버가 있을 수 있고, 루프백 인터페이스에서 53번 포트를 잡고 있는 쪽이 응답합니다:

systemctl status systemd-resolved --no-pager
resolvectl status
ss -lunp | grep ':53'

상태 출력에는 링크별 DNS 서버와 라우팅 도메인이 나오며, 이것이 분할 DNS에 필요한 지도입니다. 내부 이름은 내부 서버로, 나머지는 공용 서버로 보내야 한다면 그 정책은 링크별 도메인에 살고 있으며, 이를 파일의 정적 nameserver 목록으로 평탄화하면 그 구분이 조용히 사라집니다.

NetworkManager 자체 구성이 이 백엔드 중 무엇을 쓸지 결정합니다. 기본 구성 파일의 dns= 설정은 내부 리졸버, dnsmasq, systemd-resolved, none 중에서 고르며, 배포판이 대신 골라 두었을 수 있습니다. 이 호스트에서 “DNS 고치기”가 무엇을 뜻하는지 정하기 전에 그 설정을 읽으세요.

점검 3: VPN 클라이언트가 쓸 수 있는 것

충돌은 보통 여기에 있습니다. 리졸버 파일을 직접 다시 쓰는 VPN 클라이언트는 그 파일을 함께 유지하는 모든 서비스와 다투게 되고, 먼저 실행되는 쪽이 집니다. 최신 OpenVPN은 파일을 다시 쓰는 대신 DNS 설정을 systemd-resolved에 넘길 수 있는데, 이것이 원하는 협력적 동작입니다. 오래된 스크립트와 손으로 쓴 훅은 레거시 도우미를 호출하거나 그냥 파일을 덮어쓰곤 합니다.

WireGuard의 상황도 형태가 비슷합니다. 인터페이스 구성 자체는 DNS를 관리하지 않고, 주변 도구가 리졸버 도우미나 resolved 통합을 통해 관리합니다. 그런 도우미가 두 개 설치되어 있으면 둘 다 연결 시 자신의 세계관을 적용하려 합니다.

추측하지 말고 클라이언트의 up, down 훅을 읽으세요. /etc/openvpn/up.sh나 이에 상응하는 디렉터리의 몇 줄 shell이 그렇지 않으면 무작위로 보이는 동작을 설명해 주며, 클라이언트가 경쟁 대신 협력하게 만들 지점이기도 합니다.

점검 4: 파일이 아니라 소유자 쪽에서 고치세요

소유자를 알았다면 그 소유자가 지원하는 인터페이스로 DNS를 구성하세요. systemd-resolved라면 정적 파일 대신 링크별 설정과 라우팅 도메인, NetworkManager라면 연결 프로필의 DNS 설정과 선택한 백엔드, resolvconf 통합을 쓰는 배포판이라면 그 도우미를 통해 쓰는 것입니다.

파일을 불변으로 만들지 마세요. 불변 속성은 정당한 DHCP 갱신과 VPN 업데이트를, 싸우고 있던 것들과 함께 멈추고, 다음 패키지 업그레이드나 재연결 때 구성에서 아무도 설명할 수 없는 장애를 만드는 상태로 시스템을 남깁니다. 그것은 소유권 충돌을 숨길 뿐이며, 바로 그 충돌이 해결하려던 대상입니다.

두 구성 요소가 정말 공존해야 한다면 역할을 나누세요. 네트워크 관리자가 인터페이스 수준 구성을 소유하게 하고, VPN 클라이언트는 같은 통합을 통해 링크별 DNS와 도메인을 제공하게 하며, 파일을 직접 쓰는 세 번째 스크립트는 제거하세요. systemd-resolved 문서에는 지원하는 모드와 각 모드가 /etc/resolv.conf에서 무엇을 보여 주는지 나와 있습니다. 유지해야 할 정책의 양에 맞는 모드를 고르세요. 분할 DNS가 중요하다면 링크별 도메인이 있는 stub 구성이 그것을 보존하고, 업스트림 서버를 파일로 복사하는 모드는 그것을 조용히 평탄화합니다.

편집 후가 아니라 재연결 후에 검증하세요

변경 직후에 올바로 보이는 파일은 아무것도 증명하지 못합니다. 다음 이벤트가 아직 일어나지 않았기 때문입니다. 예전에 그것을 깨뜨리던 순서를 실행하세요: 터널을 내렸다가 올리고, DHCP 임대를 갱신하거나 인터페이스를 전환한 뒤 같은 값을 다시 읽으세요.

resolvectl query build.corp.example
resolvectl query example.com
resolvectl status | head -40

그다음 중요한 차이를 확인하세요. 내부 이름은 내부 서버로 해석되고 공용 이름은 여전히 동작해야 합니다. 모든 것을 내부 서버로 보내는 구성은 누군가 터널을 통해 인터넷에 나가려 할 때까지 괜찮아 보이고, 모든 것을 공용으로 보내는 구성은 내부 이름을 유출합니다.

배포 노트에 소유자, 백엔드, 심볼릭 링크 모드를 기록하세요. 그 한 단락이 향후 패키지 업그레이드를 사건이 아니라 검토 항목으로 바꾸고, 다음 사람이 chattr 명령으로 같은 충돌을 다시 발견하지 못하게 합니다.

관련 글