유니콘프로Mac 연결 시 WireGuard endpoint DNS 해석 실패 (재현 절차·패킷 캡처 증거 포함)
답변함※ 이 글의 도메인과 IP 주소는 모두 예시값으로 치환했습니다. 실제 도메인은 vpn.example.com, 서버 공인 IP는 203.0.113.10, 사설 대역은 192.168.0.0/24로 표기했습니다. 치환 외에 로그와 캡처 내용은 원본 그대로입니다.
■ 요약
유니콘프로Mac이 터널을 올린 상태에서는 WireGuard macOS 앱이 서버 주소(도메인)를 해석하지 못해 터널 연결이 실패합니다. IP를 직접 넣으면 정상 연결됩니다.
패킷 캡처로 확인한 실제 동작은 다음과 같습니다. WireGuard 네트워크 확장은 DNS 쿼리를 네트워크로 내보내지도 못한 채 약 40ms 만에 로컬에서 실패합니다. 유니콘이 DNS 패킷을 가로채서 응답하지 않는 것이 아니라, 쿼리가 애초에 발생하지 않습니다.
원인은 유니콘프로가 라우팅 테이블에 IPv4 전역을 덮는 scoped route(0/2, 64/2, 128.0/2, 192.0.0/2)를 자기 터널 인터페이스로 설치하면서, 그 인터페이스에 대응하는 DNS resolver를 시스템 DNS 구성(scutil --dns)에 등록하지 않는 데 있습니다. 그 결과 네트워크 확장(NEPacketTunnelProvider)이 경로 우선순위 1순위로 보는 인터페이스에는 사용할 수 있는 nameserver가 없고, getaddrinfo가 쿼리 전송 이전 단계에서 즉시 실패합니다. 같은 순간 일반 프로세스는 물리 인터페이스의 resolver를 그대로 사용하므로 정상 해석됩니다.
이 때문에 앱 단위 예외 설정, 도메인 화이트리스트, DNS 서버 변경 같은 기존 회피책은 원리적으로 효과가 없습니다. 모두 쿼리가 발생한 이후를 다루는 수단인데, 쿼리 자체가 발생하지 않습니다.
수정 요청은 한 가지입니다. scoped route를 설치할 때 해당 인터페이스에 대한 resolver 구성도 함께 등록해 주십시오.
■ 환경
- macOS 26.5.2 (빌드 25F84)
- Mac17,4 / Apple M5
- 유니콘프로Mac 1.13.25 (313), 번들 ID com.unicornsoft.unicornproformac
- WireGuard for macOS 1.0.16 (27), Mac App Store 판
- WireGuard 터널 설정: Endpoint = vpn.example.com:51820, MTU 1420, AllowedIPs는 사설 대역 두 개(스플릿 터널)
- vpn.example.com은 권위 DNS에 정상 등재되어 있고 A 레코드는 203.0.113.10입니다. 어느 DNS 서버에 조회해도 동일하게 응답합니다.
■ 증상
WireGuard 터널을 연결하려 하면 즉시 실패하고 다음 로그가 남습니다.
[NET] DNS resolution failed for the following hostnames: vpn.example.com
유니콘프로를 종료한 상태에서는 같은 설정으로 항상 정상 연결됩니다. Endpoint를 도메인 대신 IP(203.0.113.10:51820)로 바꾸면 유니콘프로가 켜져 있어도 연결됩니다. 즉 터널 자체의 통신 경로에는 문제가 없고 이름 해석 단계에서만 막힙니다.
■ 재현 절차
1. 유니콘프로Mac을 연결합니다. (연결 후 utun 인터페이스가 하나 추가되고 아래에 적은 scoped route가 설치됩니다.)
2. WireGuard 터널을 연결합니다. 앱 메뉴바에서 수동으로 켜도 되고, on-demand로 OS가 자동 시작해도 결과는 같습니다.
3. WireGuard 앱의 로그를 확인합니다. 위의 DNS resolution failed 줄이 남고 터널은 즉시 disconnected로 돌아갑니다.
재현율은 100%입니다. 2026년 7월 31일 23:25에 두 차례 연속 실행하여 두 번 모두 동일하게 실패했습니다(23:25:04.088, 23:25:40.525).
■ 증거 1. WireGuard 로그의 실패와 성공 대조
실패 시퀀스 (유니콘프로 연결 상태):
23:25:03.989 | [APP] Tunnel 'Macbook' connection status changed to 'connecting'
23:25:04.045 | [NET] App version: 1.0.16 (27)
23:25:04.045 | [NET] Starting tunnel from the OS directly, rather than the app
23:25:04.088 | [NET] DNS resolution failed for the following hostnames: vpn.example.com
23:25:04.088 | [NET] Network change detected with satisfied route and interface order [utun7, utun7, en0]
23:25:04.089 | [APP] Tunnel 'Macbook' connection status changed to 'disconnecting'
23:25:04.094 | [APP] Tunnel 'Macbook' connection status changed to 'disconnected'
여기서 utun7은 유니콘프로의 터널 인터페이스입니다. 네트워크 확장이 보고하는 경로 우선순위의 1순위가 유니콘 터널입니다. 터널 시작부터 실패까지 걸린 시간은 43ms입니다. DNS 응답 대기 타임아웃이라면 초 단위가 되어야 하므로, 이 시간은 쿼리를 보내고 기다린 결과가 아니라 로컬에서 즉시 실패한 것을 뜻합니다.
성공 시퀀스 (유니콘프로 미연결 상태):
22:18:41.752 | [NET] Starting tunnel from the app
22:18:41.888 | [NET] DNS64: mapped 203.0.113.10 to itself.
22:18:41.888 | [NET] Attaching to interface
22:18:41.889 | [NET] peer(PEER…KEY) - Sending handshake initiation
22:18:41.890 | [NET] Device started
22:18:41.890 | [NET] Network change detected with satisfied route and interface order [en0, en0]
22:18:41.891 | [APP] Tunnel 'Macbook' connection status changed to 'connected'
22:18:41.913 | [NET] peer(PEER…KEY) - Received handshake response
성공할 때는 경로 우선순위 1순위가 물리 인터페이스(en0)이고, 해석이 성공하여 DNS64 줄과 함께 정상적으로 핸드셰이크까지 진행됩니다.
■ 증거 2. 로그 전수 대조
WireGuard 앱의 순환 로그(2048개 엔트리)를 전부 파싱해 실패 사례와 성공 사례를 대조했습니다. 아래 수치는 2026년 7월 31일 22:11:47부터 23:13:34까지를 담은 스냅샷 기준입니다. 링버퍼이므로 시간이 지나면 오래된 엔트리는 밀려나갑니다.
- 실패 177건: 실패 직후에 기록된 interface order가 177건 전부 유니콘 터널 1순위였습니다. (utun9 기준 176건, utun7 기준 1건. 유니콘 터널의 인터페이스 번호는 연결 시점에 따라 달라집니다.)
- 성공 5건: 인접한 interface order가 모두 [en0, en0] 또는 [en0, en0, utun0]으로, 유니콘 터널이 목록에 없었습니다.
터널 시작 경로는 실패와 무관했습니다. 앱에서 수동으로 시작한 3건 중에도 유니콘프로가 연결되어 있던 2건은 실패했고(22:18:35, 22:18:40), OS가 on-demand로 시작한 175건은 실패했지만 유니콘프로가 빠진 뒤의 4건은 성공했습니다(22:18:52, 22:27:39, 22:36:27, 22:47:51).
성공과 실패를 가르는 유일한 변수는 유니콘 터널이 경로 우선순위 1순위인지 여부입니다.
■ 증거 3. 패킷 캡처 (결정적 증거)
프로세스 정보를 함께 기록하는 pktap 모드로 모든 인터페이스의 DNS/WireGuard 트래픽을 캡처했습니다.
sudo tcpdump -i pktap,all -k IP -n -U -w /tmp/wgcap.pcap 'udp port 53 or udp port 51820'
캡처를 켠 상태에서 WireGuard 연결을 두 번 시도했습니다. 캡처 결과 전문입니다. (조회한 도메인 이름과 응답 IP는 예시값으로 치환했고, 세 번째와 네 번째 줄이 문제의 도메인을 조회한 부분입니다.)
23:24:59.538 (en0) IP 192.168.0.20.49837 > 192.168.0.1.53: 13805+ A? host-a.example.net. (37)
23:24:59.549 (en0, proc 551, eproc 10902) IP 192.168.0.1.53 > 192.168.0.20.49837: 13805 1/0/0 A 198.51.100.17 (53)
23:25:03.968 (en0) IP 192.168.0.20.53052 > 192.168.0.1.53: 27214+ [1au] A? vpn.example.com. (42)
23:25:03.972 (en0, proc 26779) IP 192.168.0.1.53 > 192.168.0.20.53052: 27214 1/0/1 A 203.0.113.10 (58)
23:25:07.902 (en0) IP 192.168.0.20.54767 > 192.168.0.1.53: 929+ A? host-b.example.net. (35)
23:25:07.930 (en0, proc 551, eproc 10542) IP 192.168.0.1.53 > 192.168.0.20.54767: 929 1/0/0 A 198.51.100.10 (51)
WireGuard가 실패한 두 시각(23:25:04.088, 23:25:40.525)에 해당하는 패킷이 한 개도 없습니다. 캡처 자체는 정상 동작했습니다. 같은 캡처에 다른 프로세스의 DNS 트래픽이 기록되어 있고, 특히 23:25:03.968의 항목은 명령줄 dig로 문제의 도메인을 조회한 것으로 3.6ms 만에 정상 응답을 받았습니다. 즉 실패 시점 전후로 시스템의 DNS 경로는 완전히 정상이었습니다.
결론은 명확합니다. WireGuard 네트워크 확장은 DNS 쿼리를 네트워크로 전송하지 않은 상태에서 실패합니다. 유니콘프로가 53번 포트 패킷을 가로채서 응답을 주지 않는 것이 원인이라면 캡처에 쿼리 패킷이 남아야 하는데, 남지 않았습니다.
■ 증거 4. 시스템 DNS 구성과 라우팅 테이블
실패 조건(유니콘프로 연결, WireGuard 미연결)에서의 scutil --dns 결과입니다.
resolver #1
nameserver[0] : 192.168.0.1
if_index : 13 (en0)
flags : Request A records
reach : 0x00020002 (Reachable,Directly Reachable Address)
유니콘 터널(utun7)에 대응하는 resolver 항목이 존재하지 않습니다. 등록된 resolver는 물리 인터페이스 en0 쪽 하나뿐입니다.
같은 시점의 라우팅 테이블에서 유니콘 터널이 설치한 경로입니다.
0/2 utun7 UScg utun7
10.1.0.4 10.1.0.4 UH utun7
64/2 utun7 USc utun7
128.0/2 utun7 USc utun7
192.0.0/2 utun7 USc utun7
0/2, 64/2, 128.0/2, 192.0.0/2 네 개로 IPv4 주소 공간 전체를 덮고 있습니다. default route 자체는 en0로 남아 있지만, 위 네 경로가 default보다 구체적이므로 실제 트래픽은 유니콘 터널로 향합니다. 실제로 route -n get 1.1.1.1은 utun7을, 같은 서브넷 주소에 대한 조회는 en0를 반환합니다.
이 구성이 문제의 핵심입니다. 라우팅상으로는 유니콘 터널이 사실상 전역을 담당하는데, DNS 구성에는 그 인터페이스가 존재하지 않습니다. 네트워크 확장은 NWPath가 제공하는 인터페이스 목록을 기준으로 동작하므로 1순위인 유니콘 터널을 보게 되고, 그 인터페이스에는 사용 가능한 nameserver가 없어 이름 해석이 성립하지 않습니다.
한편 일반 응용 프로그램은 mDNSResponder를 통해 등록된 resolver(en0)를 그대로 사용하므로 영향을 받지 않습니다. 이것이 dig는 되는데 WireGuard만 안 되는 이유입니다.
■ 증거 5. 네트워크 확장이 반환한 오류 코드
scutil --nc status의 LastDisconnectError를 디코드한 결과입니다.
도메인: WireGuardNetworkExtension.PacketTunnelProviderError
코드: 1 (WireGuard 소스상 dnsResolutionFailure에 해당)
VPN LastCause: 3
WireGuard 쪽에서 이름 해석 실패로 판정하고 터널 시작을 포기한 것이 확인됩니다.
■ 기존 회피책이 통하지 않는 이유
- 앱 단위 예외(WireGuard를 우회 대상으로 지정): DNS 이름 해석은 시스템 전역 경로를 타고, 문제 지점은 인터페이스 선택 단계이므로 앱 지정으로 우회되지 않습니다.
- 도메인 화이트리스트(문제의 도메인 등록): 이미 등록해 두었으나 실패합니다. 화이트리스트는 쿼리가 발생한 뒤 그 목적지를 판단하는 수단인데, 쿼리 자체가 발생하지 않습니다.
- DNS 서버 변경: 같은 이유로 무효입니다. 어느 서버를 지정해도 그 서버로 향하는 패킷이 만들어지지 않습니다.
이용자 측에서 가능한 우회는 두 가지뿐이며 둘 다 근본 해결이 아닙니다. Endpoint를 IP로 하드코딩하거나, /etc/hosts에 IP를 고정하고 별도 스크립트로 주기 갱신하는 방법입니다. 서버 IP가 바뀌면 수동 관리가 필요해집니다.
■ 수정 요청
유니콘프로가 scoped route를 설치할 때, 해당 터널 인터페이스에 대한 DNS resolver 구성도 시스템 DNS 구성에 함께 등록해 주시기를 요청합니다. 그러면 네트워크 확장이 1순위 인터페이스를 선택했을 때에도 사용할 수 있는 nameserver가 존재하므로 이름 해석이 정상 진행됩니다.
같은 문제는 WireGuard에 한정되지 않을 것으로 보입니다. NEPacketTunnelProvider 또는 NEVPNManager 기반으로 동작하면서 서버 주소를 도메인으로 지정하는 모든 VPN 클라이언트, 그리고 네트워크 확장 형태로 동작하는 다른 앱들이 동일한 조건에 놓입니다.
■ 부록. 로그와 증거 재현 방법
WireGuard macOS 앱의 로그는 다음 경로에 순환 버퍼로 저장됩니다.
~/Library/Group Containers/L82V4Y2P3C.group.com.wireguard.macos/tunnel-log.bin
포맷은 헤더 8바이트 다음에 { int64 시각(나노초, 리틀엔디언) + char[512] 메시지 } 구조가 2048개 이어지는 링버퍼입니다. 앱 UI의 로그 내보내기 기능으로도 같은 내용을 텍스트로 얻을 수 있습니다.
패킷 캡처는 본문 증거 3의 tcpdump 명령을 사용했습니다. -k IP 옵션으로 패킷별 프로세스 정보가 함께 기록되고, -U 옵션이 없으면 버퍼가 flush되지 않아 파일이 비게 되므로 주의가 필요합니다.
캡처 파일과 파싱한 전체 로그는 요청 시 제공할 수 있습니다.
-
안녕하세요, 유니콘 고객센터입니다.
상세한 재현 절차와 로그, 패킷 캡처 분석까지 함께 공유해 주셔서 감사합니다. 덕분에 문제 상황 파악에 큰 도움이 되었습니다.
말씀해주신 것처럼 유니콘 Pro가 연결된 상태에서 WireGuard로 도메인 기반 서버에 접속할 때 DNS 해석이 실패하여 연결이 되지 않는 현상과 Endpoint를 IP로 직접 입력하면 정상 연결되는 점도 확인했습니다.
공유해주신 내용은 담당 부서에 전달하여 원인 파악 및 개선 가능 여부를 확인하겠습니다.
감사합니다.
0