SSD Nodes Learn Hosting plans →
가이드 Matt Connor작성자 Matt Connor

WireGuard DNS 연결 실패 원인과 해결 방법 3가지

WireGuard 터널은 연결되었으나 DNS 이름 해석이 안 되거나 쿼리가 유출되는 문제를 해결합니다. 리졸버 응답 없음, 설정 덮어쓰기, 라우팅 오류 등 세 가지 주요 실패 모드를 진단하고 각 상황에 맞는 정확한 수정 방법을 안내합니다.

WireGuard 터널이 활성화되는 즉시 DNS가 작동하지 않는 이유

WireGuard를 통한 DNS는 세 가지 방식으로 실패하며, 각각 해결 방법이 다릅니다. 이름 해석이 전혀 되지 않거나, 이름은 해석되지만 쿼리가 터널 외부로 나가거나, 혹은 인터페이스가 시작된 직후 클라이언트의 리졸버 관리자가 설정을 덮어쓰는 경우입니다. 터널 자체가 문제인 경우는 거의 없습니다. 문제는 클라이언트에게 어떤 리졸버를 사용할지 지시하는 한 줄의 설정과, 해당 리졸버로 향하는 패킷의 경로를 결정하는 라우팅입니다.

WireGuard는 IP 패킷을 전달할 뿐 DNS(도메인 이름 시스템, example.com과 같은 이름을 IP 주소로 변환하는 서비스)에 대해서는 아무것도 알지 못합니다. 클라이언트 [Interface] 블록의 DNS = 줄은 WireGuard 설정이 아닙니다. 이 설정은 인터페이스를 활성화하는 셸 래퍼인 wg-quick이 읽으며, wg-quick는 터널이 활성화된 동안 클라이언트의 리졸버 설정을 편집하고 wg-quick down 시점에 이를 복구합니다. 따라서 아래의 모든 문제는 라우팅 문제이거나 wg-quick 문제이며, 암호화 문제는 아닙니다. 터널 자체가 아직 구축되지 않았다면 직접 호스팅하는 VPS 기반 WireGuard VPN을 먼저 설정한 뒤 이 페이지로 돌아오십시오.

DNS 설정을 변경하기 전에 터널이 정상인지 확인하십시오.

sudo wg show
ping -c 3 10.8.0.1
ping -c 3 1.1.1.1

wg show 명령을 실행하면 최근 latest handshake을 가진 피어가 표시되어야 하며, 양방향 핑이 응답해야 합니다. 만약 ping 1.1.1.1에서 타임아웃이 발생한다면, 이는 DNS 문제가 아니라 포워딩이나 NAT(네트워크 주소 변환) 문제일 가능성이 높으며 리졸버 설정 변경으로는 해결되지 않습니다. ICMP를 차단하는 방화벽도 동일한 타임아웃을 유발하므로, 포워딩 규칙을 변경하기 전에 curl -sI https://1.1.1.1로 먼저 확인하십시오. 핑은 응답하지만 실제 트래픽이 시작될 때 처리량이 급감한다면 이는 별개의 문제이며, WireGuard 속도 저하의 원인은 대부분 MTU와 관련이 있습니다. 여기의 모든 예제는 10.8.0.0/24를 터널 서브넷으로, 10.8.0.1을 서버의 터널 주소로 사용합니다. 본인의 환경에 맞게 수정하십시오.

첫 번째 실패: 리졸버가 응답하지 않아 아무것도 해석되지 않음

증상은 명확합니다. ping 1.1.1.1은 정상 작동하며, curl https://example.com은 다음을 반환합니다.

curl: (6) Could not resolve host: example.com

클라이언트에서 터널 리졸버로 직접 질의하십시오. dig은 Ubuntu 및 Debian의 dnsutils 패키지에 포함되어 있습니다.

dig +short @1.1.1.1 example.com
dig +short @10.8.0.1 example.com

첫 번째 명령은 주소를 반환하며, 이는 패킷이 터널을 통해 인터넷에 도달함을 증명합니다. 두 번째 명령은 아무것도 반환하지 않고 ;; communication timed out; no servers could be reached을 출력합니다. 이것이 진단의 전부입니다. 클라이언트가 10.8.0.1을 가리키고 있으나, 10.8.0.1가 UDP 53번 포트에서 응답하지 않는 상태입니다.

이 현상은 두 가지 원인으로 발생합니다. 서버에서 리졸버가 실행 중이지 않거나, 서버 방화벽이 쿼리가 도착하기 전에 차단하는 경우입니다. 서버에서 두 가지 모두 확인하십시오.

sudo ss -ulnp | grep ':53'
sudo nft list ruleset

정상적으로 실행되어 바인딩된 리졸버는 10.8.0.1:53 또는 0.0.0.0:53가 포함된 줄을 보여줍니다. Ubuntu에서 흔히 발생하는 의외의 상황은 127.0.0.53:53입니다. 이는 systemd-resolved 스텁 리스너로, 루프백 주소에 바인딩되어 외부 기기에서는 의도적으로 접근할 수 없습니다. VPN 클라이언트가 이 스텁 리졸버만 가진 서버를 가리키면 정확히 이 타임아웃이 발생합니다.

해결책은 터널 주소에서 수신 대기하는 리졸버를 구성하고, 피어(peer)가 해당 리졸버에 접근할 수 있도록 방화벽 규칙을 하나 추가하는 것입니다.

sudo apt update && sudo apt install -y unbound
printf 'server:\n  interface: 10.8.0.1\n  access-control: 10.8.0.0/24 allow\n' \
  | sudo tee /etc/unbound/unbound.conf.d/wireguard.conf
sudo systemctl restart unbound
sudo ss -ulnp | grep '10.8.0.1:53'

그런 다음 터널 트래픽에 대해서만 포트를 개방하십시오. nftables를 사용하는 경우, /etc/nftables.conf의 input 체인에 다음 두 줄을 추가하고 sudo systemctl reload nftables로 다시 로드하십시오.

udp dport 53 iifname "wg0" accept
tcp dport 53 iifname "wg0" accept

ufw를 사용하는 경우, sudo ufw allow in on wg0 to any port 53로 동일한 작업을 수행할 수 있습니다. 53번 포트를 공용 인터넷에 절대 개방하지 마십시오. 개방된 재귀적 리졸버는 며칠 내로 스캐너에 탐지되어 서비스 거부 공격(DoS) 증폭에 악용되며, 서비스 제공업체는 귀하보다 먼저 해당 트래픽을 감지할 것입니다.

클라이언트에서 dig +short @10.8.0.1 example.com을 다시 실행하십시오. 출력에 주소가 나타난다면 리졸버 경로가 정상 작동하는 것이며, 이제 클라이언트가 해당 경로를 사용하도록 설정하기만 하면 됩니다. 클라이언트 [Interface] 블록에 해당 줄을 추가하고 sudo wg-quick down wg0 && sudo wg-quick up wg0로 인터페이스를 재시작하십시오.

[Interface]
PrivateKey = <client private key>
Address = 10.8.0.2/32
DNS = 10.8.0.1

두 번째 실패: 스플릿 터널이 리졸버를 라우팅하지 않아 발생하는 DNS 누수

이 문제는 모든 것이 정상적으로 작동하는 것처럼 보이기 때문에 더 위험합니다. 도메인 이름은 해석되고 페이지는 로드되지만, DNS 쿼리는 신뢰할 수 없는 로컬 네트워크를 통해 평문으로 전송됩니다.

이 문제는 두 가지 설정에서 발생합니다. 첫 번째는 AllowedIPs = 0.0.0.0/0, ::/0을 사용하면서 DNS = 줄이 없는 클라이언트입니다. wg-quick은 자체 라우팅 테이블에 기본 경로를 설치하고 suppress_prefixlength 0 규칙을 추가하는데, 이는 프린터와 같은 로컬 장치에 계속 접근할 수 있도록 더 구체적인 로컬 경로를 의도적으로 유지합니다. 클라이언트가 DHCP를 통해 학습한 리졸버(일반적으로 192.168.1.1에 있는 라우터)는 이러한 로컬 경로 중 하나와 일치합니다. 데이터 트래픽은 터널을 통하지만, 로컬 네트워크는 사용자가 조회하는 모든 도메인 이름 목록을 그대로 수신하게 됩니다.

두 번째는 스플릿 터널 설정입니다. AllowedIPs = 10.8.0.0/24에 DNS = 9.9.9.9이 포함된 경우입니다. 9.9.9.9이 AllowedIPs 내부에 포함되어 있지 않기 때문에, 클라이언트는 터널을 통해 해당 리졸버로 가는 경로를 알지 못합니다. 따라서 첫 번째 사례와 마찬가지로 쿼리가 로컬 네트워크 인터페이스를 통해 외부로 나갑니다.

어떤 리졸버가 실제로 응답하는지 확인하십시오. whoami.akamai.net은 쿼리를 보낸 재귀적 리졸버의 IP 주소를 반환하는 공개 테스트 도메인입니다. 이 결과를 서버의 공인 IP 주소와 비교할 수 있습니다.

resolvectl status
dig +short whoami.akamai.net
sudo tcpdump -ni any -c 10 port 53

resolvectl status은 링크별로 하나의 블록을 출력합니다. 이더넷이나 무선 링크 블록에 여전히 Current DNS Server: 192.168.1.1가 표시되는데 wg0 블록에는 아무것도 없다면, 이는 누수가 발생하고 있다는 뜻입니다. dig +short whoami.akamai.net가 서버 주소가 아닌 가정용 광대역 주소를 반환한다면 원격지에서도 누수가 확인된 것입니다. tcpdump 줄은 논쟁을 종식하는 증거입니다. 정상적인 상태라면 기기를 떠나는 모든 53번 포트 패킷은 wg0 인터페이스를 거쳐야 하며, 누수가 발생하면 wlan0이나 enp3s0 인터페이스를 거치게 됩니다. lo를 통해 127.0.0.53으로 오가는 패킷은 로컬 systemd-resolved 스텁과 통신하는 애플리케이션이므로 누수가 아닙니다.

이 문제를 해결하려면 두 가지 조치가 모두 필요합니다. DNS을 터널 내부의 주소로 설정하고, 해당 주소가 AllowedIPs 범위 내에 있는지 확인하십시오.

[Interface]
Address = 10.8.0.2/32
DNS = 10.8.0.1

[Peer]
PublicKey = <server public key>
Endpoint = vpn.example.com:51820
AllowedIPs = 10.8.0.0/24, 10.20.0.0/16

10.8.0.1은 10.8.0.0/24 내부에 위치하므로 쿼리가 암호화되어 서버로 전송됩니다. 스플릿 터널에서 공개 리졸버를 반드시 사용해야 한다면, 호스트 경로로 추가하십시오: AllowedIPs = 10.8.0.0/24, 9.9.9.9/32. 이렇게 하면 패킷이 터널링되지만, 로컬 네트워크는 이전 세션 기록을 통해 사용자가 해당 제공업체를 선택했음을 여전히 알 수 있습니다. 직접 운영하는 리졸버를 사용하면 이러한 문제를 피할 수 있습니다.

리졸버 할당은 직접 구축한 WireGuard와 조정된 메시 네트워크 간의 눈에 띄는 차이점 중 하나이며, 이는 WireGuard와 Tailscale 비교에서 고려해야 할 절충안의 일부입니다. 자체 호스팅 Headscale 제어 서버를 운영하면 키 자료를 제3자에게 넘기지 않고도 이러한 조정 기능을 얻을 수 있습니다. 마지막 문구가 우려된다면, Tailscale은 트래픽을 암호화하는 키를 보유하지 않는다는 점을 참고하십시오. 더 중요한 질문은 조정 서버가 침해되거나 ID 계정을 탈취당했을 때 네트워크에 어떤 위험이 추가될 수 있는지입니다.

실패 사례 3: Linux 클라이언트에서 resolvconf와 systemd-resolved의 충돌

macOS, Windows, iOS, Android 클라이언트는 공식 앱을 통해 DNS =을 적용하며 별다른 문제를 일으키지 않습니다. Linux의 경우, 여러 리졸버 관리자 중 무엇을 사용하는지 추측해야 하는 셸 스크립트로 설정을 적용하기 때문에 문제가 발생합니다.

첫 번째 실패는 명확하게 드러납니다. sudo wg-quick up wg0이 다음 오류와 함께 중단됩니다.

resolvconf: command not found

wg-quick은 resolvconf를 호출하는데, 해당 바이너리가 설치되어 있지 않은 경우입니다. Debian 12 및 Ubuntu 24.04 이상에서는 systemd-resolved와 통신하는 resolvconf 명령어가 systemd-resolved 패키지에 포함되어 있으므로, 이를 설치한 후 인터페이스를 다시 활성화하십시오. Ubuntu 22.04에는 별도의 패키지가 없으므로, sudo ln -s /usr/bin/resolvectl /usr/local/bin/resolvconf를 사용하여 호환성 링크를 직접 생성하고 설치 단계는 건너뛰십시오.

sudo apt install -y systemd-resolved
sudo systemctl restart systemd-resolved
sudo wg-quick down wg0 && sudo wg-quick up wg0
resolvectl status wg0

두 번째 실패는 조용히 발생하며, 해결하는 데 많은 시간이 소요될 수 있습니다. 인터페이스가 활성화되고 resolvectl status wg0이 DNS Servers: 10.8.0.1를 올바르게 표시하더라도, 조회 요청은 여전히 이전 리졸버에 도달합니다. systemd-resolved가 wg0를 무시하는 것이 아닙니다. 라우팅 도메인과 일치하지 않는 이름은 기본 경로로 간주되는 모든 링크의 DNS 서버로 전달되는데, DNS 서버가 있고 라우팅 전용 도메인이 없는 링크는 기본적으로 기본 경로로 간주됩니다. 따라서 쿼리가 10.8.0.1과 무선 링크의 라우터로 동시에 전송되며, 먼저 성공적으로 응답하는 쪽이 승리합니다. 라우터는 한 홉 거리에 있어 보통 먼저 응답하며, 어느 쪽이든 쿼리는 이미 평문으로 전송된 상태입니다. lan과 같이 무선 링크 자체의 검색 도메인에 속한 이름은 가장 구체적인 라우팅 도메인이 우선하므로 해당 링크로만 전송됩니다.

wg-quick이 systemd의 자체 resolvconf 래퍼를 통해 DNS =를 적용할 때는 이미 전체를 포괄하는(catch-all) 라우팅 도메인을 요청하므로, 이 실패는 주로 다른 resolvconf 구현체나 네트워크 관리자가 설정을 적용했을 때 발생합니다. 명시적으로 설정하면 추측에 의존할 필요가 없습니다.

리졸버를 설정하고 동일한 단계에서 전체를 포괄하는 라우팅 도메인을 지정하십시오. %i는 인터페이스 이름으로 확장되므로, 이 블록은 어떤 인터페이스에서도 수정 없이 작동합니다.

[Interface]
Address = 10.8.0.2/32
PostUp = resolvectl dns %i 10.8.0.1; resolvectl domain %i ~.
PostDown = resolvectl revert %i

이와 같이 PostUp를 사용할 때는 DNS = 라인을 삭제하십시오. 그렇지 않으면 두 메커니즘이 리졸버 상태를 동시에 기록하게 되며, 그중 하나만 나중에 정리 작업을 수행하기 때문입니다. ~. 인수가 중요한 부분입니다. 이 인수는 wg0을 모든 이름에 대한 라우팅 전용 도메인으로 만들어, systemd-resolved가 더 구체적인 도메인과 일치하지 않는 이름을 다른 링크로 보내지 않도록 합니다. lan과 같이 다른 링크의 자체 도메인에 속한 이름은 여전히 해당 링크로 전달되며, 이는 일반적으로 로컬 호스트에 대해 의도한 동작입니다. 이를 확인하십시오.

resolvectl status wg0

정상적인 출력에는 DNS Servers: 10.8.0.1과 DNS Domain: ~.가 포함되어야 합니다. ~.이 누락되었다면 resolvectl domain 부분이 실행되지 않은 것이며, wg0는 다시 다른 링크들과 경쟁하게 됩니다. 기본 경로 플래그를 확인 기준으로 사용하지 마십시오. ~.이 없어도 yes(또는 systemd 버전에 따라 Protocols 라인의 +DefaultRoute)으로 읽히는데, 이는 라우팅 전용 도메인이 없는 링크는 어차피 기본 경로이기 때문입니다.

한 가지 더 언급할 사례가 있습니다. /etc/resolv.conf이 /run/systemd/resolve/stub-resolv.conf에 대한 심볼릭 링크가 아닌 실제 파일이라면, 일반적으로 NetworkManager나 컨테이너 런타임과 같은 다른 무언가가 이를 소유하고 있는 것입니다. 다른 문제를 디버깅하기 전에 ls -l /etc/resolv.conf를 실행하십시오. 네트워크가 변경될 때마다 해당 파일을 다시 작성하는 도구는 가장 필요하지 않은 순간에 당신의 작업을 무효화할 것이기 때문입니다.

업그레이드: 터널을 통한 자체 필터링 리졸버 구성

쿼리가 터널을 통해 안정적으로 전송되면, 원격지의 리졸버가 제어 지점이 됩니다. 해당 위치에서 AdGuard Home을 실행하면 클라이언트 소프트웨어나 기기별 설정 없이도 연결된 모든 기기에 차단 목록 필터링과 쿼리 로그 기능을 제공할 수 있습니다. 2026년 10월 기준으로 확인된 공식 설치 스크립트는 한 줄로 구성되어 있습니다.

curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -v

설정 마법사는 최초 실행 시 3000번 포트에서 대기합니다. 해당 포트를 외부에 공개하는 대신 터널을 통해 http://10.8.0.1:3000으로 접속하십시오. 마법사에서 DNS 대기 주소와 관리자 대기 주소를 모두 10.8.0.1로 설정합니다. 만약 첫 번째 실패에서 사용했던 unbound가 여전히 같은 주소를 점유하고 있다면, sudo systemctl disable --now unbound을 사용하여 먼저 중지하십시오. 두 서비스가 동일한 주소에서 UDP 53번 포트를 두고 경쟁하게 되면 AdGuard Home이 listen udp 10.8.0.1:53: bind: address already in use 오류와 함께 종료되기 때문입니다.

클라이언트 설정이 이미 DNS = 10.8.0.1로 되어 있다면 별도의 변경은 필요하지 않습니다. 이제 쿼리 로그에는 모든 피어로부터 도달하는 모든 조회 기록이 표시됩니다. 특정 클라이언트를 로그에서 제외할 수도 있지만, 이는 단순히 이득을 얻는 것이 아니라 개인정보 보호와 관련된 실질적인 결정입니다. 즉, 인터넷 서비스 제공업체에 두던 신뢰를 본인에게 옮겨오는 것이며, 해당 서버의 보안 패치를 직접 관리해야 할 책임도 따릅니다. 인터넷에 노출된 서버는 기본적인 보안 조치가 선행되어야 하며, 새 VPS에서의 첫 10분 가이드에서 이를 다룹니다.

FAQ

WireGuard 터널은 연결되는데 도메인 이름이 해석되지 않는 이유는 무엇입니까?

터널은 패킷을 전달할 뿐 도메인 이름을 처리하지 않습니다. 따라서 터널은 정상인데 이름 해석이 안 된다면 지정한 리졸버가 응답하지 않는 것입니다. 클라이언트에서 dig +short @10.8.0.1 example.com으로 테스트하십시오. communication timed out 응답이 돌아온다면 해당 터널 주소에서 리졸버가 대기 중이지 않은 것입니다. 이는 주로 systemd-resolved 스텁이 127.0.0.53에만 바인딩되어 있거나, 서버 방화벽이 wg0로 들어오는 UDP 53번 포트 패킷을 차단하기 때문입니다. 먼저 리스너 설정을 수정하고, wg0에 대해서만 포트를 개방하십시오.

WireGuard를 통해 DNS 정보가 유출되는지 어떻게 확인합니까?

클라이언트에서 sudo tcpdump -ni any -c 10 port 53을 실행하고 브라우징하는 동안 인터페이스 열을 확인하십시오. 기기에서 나가는 모든 패킷은 wg0를 통해야 합니다. 127.0.0.53로 향하거나 오는 lo 패킷은 로컬 systemd-resolved 스텁이 처리하는 것이므로 정상입니다. 만약 무선이나 이더넷 인터페이스에서 패킷이 보인다면 쿼리가 평문으로 유출되는 것입니다. dig +short whoami.akamai.net를 사용하면 교차 검증이 가능합니다. 이 도구는 쿼리를 보낸 재귀적 리졸버의 공인 IP 주소를 응답으로 돌려주는데, 서버의 주소가 아닌 다른 주소가 나온다면 유출이 발생하고 있다는 증거입니다.

스플릿 터널을 사용할 때 DNS = 줄이 꼭 필요합니까?

네, 그렇습니다. 리졸버 주소 또한 AllowedIPs 범위 내에 있어야 클라이언트가 해당 주소로 가는 경로를 찾을 수 있습니다. AllowedIPs = 10.8.0.0/24 설정을 사용하면 10.8.0.1에 위치한 리졸버가 범위에 포함되어 쿼리가 암호화됩니다. 9.9.9.9과 같은 공용 리졸버는 범위에 포함되지 않으므로, DNS 줄이 올바르게 설정된 것처럼 보여도 쿼리는 로컬 네트워크를 통해 평문으로 나갑니다.

resolvectl에는 올바른 서버가 표시되는데 왜 조회는 다른 곳으로 갑니까?

systemd-resolved는 라우팅 도메인과 일치하지 않는 이름을 기본 경로가 설정된 모든 링크(wg0 포함)로 동시에 보낸 뒤, 가장 먼저 성공한 응답을 반환합니다. 보통은 로컬 라우터의 응답이 가장 빠릅니다. 클라이언트 [Interface] 블록에 PostUp = resolvectl dns %i 10.8.0.1; resolvectl domain %i ~.을 추가하고 DNS = 줄을 제거하십시오. 그러면 resolvectl status wg0은 DNS Domain: ~.을 보고해야 하며, 다른 링크가 더 구체적인 도메인으로 선점하지 않은 모든 이름은 wg0로만 전달됩니다.

여러 클라이언트가 고장 났을 때 무엇부터 고쳐야 합니까?

Linux 클라이언트 하나를 먼저 수정하십시오. 내부 동작 원리를 확인할 수 있는 유일한 플랫폼이기 때문입니다. resolvectl status와 tcpdump을 사용하면 어떤 리졸버가 응답했는지, 어떤 인터페이스를 통해 패킷이 전달되었는지 알 수 있습니다. 모바일 및 데스크톱 앱은 동일한 DNS 및 AllowedIPs 값을 적용하지만 내부 구조를 보여주지 않습니다. 따라서 Linux 클라이언트에서 먼저 설정을 검증한 뒤 다른 기기에 적용하는 것이 좋습니다.

이 글이 도움이 되었나요?