Mihomo DNS 설정 가이드: nameserver, fallback 및 DNS 하이재킹
Mihomo DNS의 nameserver, fallback, fallback-filter, dns-hijack 설정과 fake-ip·redir-host 선택 기준을 설명하고 바로 적용할 수 있는 YAML 예제를 제공합니다.
DNS 설정의 세 단계: 요청 유입, 이름 해석, 연결
Clash Meta(mihomo)의 DNS 문제를 해결하려면 먼저 세 가지 과정을 구분해야 합니다. 앱이 DNS 요청을 어디로 보내는지, 코어가 어느 상위 DNS 서버에 질의하는지, 응답을 받은 뒤 연결에 어떤 규칙이 적용되는지를 따로 확인하세요. 웹페이지가 열리지 않는 원인은 이 중 어느 단계에나 있을 수 있습니다. nameserver만 바꿔서는 시스템이 DNS 요청을 코어로 전달하지 않는 문제를 해결할 수 없습니다.
- 요청 유입:
dns.listen은 코어의 DNS 수신 포트와 주소를 지정합니다. TUN 환경에서는dns-hijack이 조건에 맞는 DNS 트래픽을 가로챕니다. - 이름 해석:
nameserver,fallback,nameserver-policy가 질의할 상위 서버와 응답 선택 방식을 결정합니다. - 연결: 앱이 IP 주소를 받은 뒤 연결을 시작하면 코어가 프록시 모드, 규칙, 정책 그룹에 따라 트래픽을 처리합니다.
시스템 HTTP 프록시를 켜도 모든 앱의 DNS 요청이 자동으로 전달되는 것은 아닙니다. 브라우저의 보안 DNS, Android 비공개 DNS, 앱에 내장된 DoH는 별도의 경로를 사용할 수 있습니다. 따라서 시스템 프록시가 켜져 있다는 사실만으로 모든 DNS 조회가 Mihomo를 거친다고 볼 수는 없습니다.
nameserver와 default-nameserver: 상위 DNS 서버와 초기 조회
nameserver는 일반 도메인 조회에 사용할 상위 DNS 서버 목록입니다. IP 주소를 그대로 입력하면 보통 일반 DNS를 사용합니다. https://는 DoH, tls://는 DoT를 뜻합니다. 일반 DNS의 기본 포트는 53, DoT는 853이며, DoH는 보통 HTTPS의 443 포트를 사용합니다.
상위 서버를 https://dns.alidns.com/dns-query로 지정하면 코어가 먼저 dns.alidns.com의 IP 주소를 알아내야 합니다. default-nameserver는 DNS 서버 자체의 도메인을 조회하는 초기 해석용 설정이지, 모든 조회에 우선 적용되는 기본 서버가 아닙니다. 이 항목에는 IP 주소를 지정해 아직 해석되지 않은 도메인에 다시 의존하는 상황을 피할 수 있습니다.
| 필드 | 용도 | 흔한 오해 |
|---|---|---|
listen | 로컬 DNS 수신 주소와 포트 지정 | 설정하면 시스템이 자동으로 이 주소를 사용한다 |
default-nameserver | 상위 DNS 서버의 도메인 이름 조회 | 모든 도메인에 우선 적용되는 DNS 서버다 |
nameserver | 일반 DNS 조회 처리 | 여러 주소를 목록 순서대로 하나씩 시도한다 |
proxy-server-nameserver | 프록시 노드 도메인 전용 조회 | 프록시를 통해 접속하는 모든 웹사이트를 처리한다 |
nameserver-policy | 조건에 맞는 도메인의 조회 서버 지정 | 웹사이트 연결에 사용할 노드를 직접 결정한다 |
프록시 노드 자체를 도메인으로 지정했다면 해당 도메인도 조회해야 합니다. proxy-server-nameserver를 사용해 연결 가능한 상위 DNS 서버를 별도로 지정하면, 프록시 주소를 조회하기 위해 먼저 프록시에 연결해야 하는 순환 문제를 줄일 수 있습니다. 이 설정은 웹사이트 연결 규칙에 따른 라우팅과 별개이며 서로 대체할 수 없습니다.
fallback과 fallback-filter: 병렬 조회 후 응답 선택
fallback을 기존 DNS의 응답이 시간 초과된 뒤에만 사용하는 예비 서버로 이해해서는 안 됩니다. fallback에 따른 응답 선택이 적용되는 일반 조회 경로에서는 코어가 관련 상위 서버에 병렬로 질의한 뒤 필터 조건에 따라 fallback 응답을 선택할 수 있습니다. 따라서 fallback을 추가하면 한 번의 도메인 조회가 두 그룹의 DNS 서버에 전달될 수 있습니다.
자주 쓰는 필터 조건 세 가지
geoip: IP 주소의 지역 정보를 바탕으로 판단하도록 설정합니다.geoip-code로 지역 코드를 지정합니다. 예를 들어CN으로 설정하면 일반 조회 결과가 해당 지역 조건에 맞지 않을 때 fallback 응답이 선택됩니다. 이 판단에는 GeoIP 데이터가 필요합니다.ipcidr: 일반 조회 결과가 지정된 IP 대역에 포함되면 fallback 응답을 사용합니다. IP 대역을 대상으로 하며 도메인 매칭 규칙은 아닙니다.domain: 지정한 도메인이 일치하면 fallback 응답을 사용합니다. 예제의+.github.com은 루트 도메인과 하위 도메인을 모두 뜻합니다.
아래 전체 DNS 예제에서는 geoip: false를 명시해 GeoIP 데이터 업데이트 문제와 분리하고, IP 대역 및 도메인 조건만 설명합니다. 0.0.0.0/8과 240.0.0.0/4는 비정상 응답을 필터링하는 예시일 뿐, 오염된 IP 목록 전체를 포함하거나 모든 잘못된 DNS 응답을 찾아내지는 않습니다.
dns:
enable: true
listen: 127.0.0.1:1053
ipv6: false
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.1/16
fake-ip-filter-mode: blacklist
fake-ip-filter:
- localhost
- "+.lan"
- "+.local"
default-nameserver:
- 223.5.5.5
- 119.29.29.29
proxy-server-nameserver:
- https://dns.alidns.com/dns-query
nameserver:
- https://dns.alidns.com/dns-query
fallback:
- https://cloudflare-dns.com/dns-query
fallback-filter:
geoip: false
ipcidr:
- 0.0.0.0/8
- 240.0.0.0/4
domain:
- "+.github.com"
이 예제는 지정한 상위 DNS 서버에 현재 네트워크와 라우팅 환경에서 연결할 수 있다는 전제하에 작성되었습니다. DoH는 전송을 암호화하지만, 암호화된다고 해서 자동으로 프록시를 통과하는 것은 아닙니다. 상위 서버 연결 경로는 관련 설정에 따라 달라집니다. 특정 서버에 연결할 수 없다면 먼저 접근 가능한 것으로 확인된 서버로 바꾸고, 확인되지 않은 주소를 여러 개 한꺼번에 추가하지 마세요.
fake-ip와 redir-host: 앱에 반환되는 주소의 차이
enhanced-mode: fake-ip는 해당하는 DNS 조회에 매핑 주소를 반환합니다. 앱이 이 주소로 연결하면 코어가 매핑 정보에서 도메인을 찾아 규칙에 따라 처리합니다. 위 예제의 198.18.0.1/16은 벤치마크용 주소 대역입니다. 이 대역의 응답을 받았다고 해서 곧바로 DNS 오염으로 판단해서는 안 됩니다.
redir-host는 앱에 실제 DNS 조회 결과를 반환하고, 도메인과 IP 주소의 연결 정보를 활용해 트래픽을 처리합니다. 기존 DNS 동작과 더 비슷하지만, 여러 도메인이 같은 IP를 공유하거나 앱이 DNS 캐시를 사용하는 경우 도메인 연결 정보가 fake-ip만큼 직접적이지 않을 수 있습니다.
| 항목 | fake-ip | redir-host |
|---|---|---|
| 앱에 표시되는 주소 | 일반적으로 매핑 주소(필터 대상 제외) | 상위 DNS 서버가 반환한 실제 주소 |
| 우선 확인하기 좋은 상황 | TUN 가로채기와 도메인 기반 규칙 라우팅 | 앱에 실제 IP 주소가 필요한 호환성 문제 |
| 중점 확인 사항 | 연결 트래픽도 매핑 정보를 보유한 코어로 돌아오는지 | DNS 조회와 연결을 서로 연관 지을 수 있는지 |
필터 목록은 반환하는 주소 유형만 바꿉니다
예제의 fake-ip-filter-mode: blacklist는 목록과 일치하는 항목에 fake-ip를 반환하지 않는다는 뜻입니다. LAN 도메인 접미사를 필터에 추가하면 해당 이름에 매핑 주소가 반환되는 것을 방지할 수 있지만, 이름이 반드시 조회된다는 보장은 없습니다. .local은 보통 mDNS를 사용합니다. LAN 기기는 라우터 DNS에만 등록되어 있을 수 있으며, 공용 DNS 서버에는 해당 정보가 없습니다.
따라서 NAS 이름이 여전히 조회되지 않는다면 먼저 mDNS와 라우터 DNS 중 어느 경로에 의존하는지 확인하고, 필요에 따라 LAN 전용 조회 정책을 설정하세요. 문제를 피하려고 모든 도메인을 필터에 추가하지 마세요. 두 모드를 전환한 뒤에는 기존 DNS 캐시를 지우고 연결을 새로 만들어 앱이 이전에 받은 주소를 계속 사용하지 않도록 하세요.
TUN의 dns-hijack: 53번 포트 DNS 요청 가로채기
dns-hijack은 최상위 tun 항목에 있으며 dns 항목 안에 넣지 않습니다. 아래 설정은 해당 TUN 설정을 지원하는 데스크톱 클라이언트용입니다. TUN을 생성할 권한이 필요하며, 클라이언트에 따라 서비스 모드나 권한 보조 프로그램으로 이 설정을 관리할 수 있습니다.
tun:
enable: true
stack: mixed
auto-route: true
auto-detect-interface: true
dns-hijack:
- any:53
- tcp://any:53
any:53은 TUN으로 들어와 조건에 맞는 UDP 53번 포트 DNS 요청을 가로챕니다.tcp://any:53을 추가하면 TCP 53번 포트를 사용하는 일반 DNS 조회도 처리할 수 있습니다.auto-route는 관련 라우트를 자동으로 설정합니다. 특정 앱까지 적용되는지는 시스템 라우팅과 클라이언트 구현에 따라 달라집니다.auto-detect-interface는 출구 인터페이스를 자동으로 감지합니다. 여러 네트워크 어댑터를 사용하거나 라우팅이 복잡한 환경에서는 실제 출구를 추가로 확인하세요.
여기서 “any”는 네트워크상의 모든 DNS 요청을 가로챈다는 뜻이 아닙니다. 지정된 가로채기 경로로 들어오는 트래픽만 처리할 수 있으며, 브라우저가 HTTPS 443번 포트로 보내는 DoH를 일반 DNS로 자동 변환하지도 않습니다. DoT의 853번 포트도 위 두 항목의 대상이 아닙니다.
위 예제는 DNS 수신 주소를 127.0.0.1:1053으로 지정해 로컬 테스트에 적합합니다. 일반적인 시스템 DNS 설정 화면은 주소만 입력할 수 있고 포트는 지정할 수 없는 경우가 많습니다. 여기에 127.0.0.1만 입력하면 1053이 아닌 53번 포트로 질의합니다. TUN 내부의 DNS 가로채기와 시스템 DNS를 직접 설정하는 것은 서로 다른 요청 유입 경로이므로 구분해야 합니다.
순서대로 확인하기: 수신, 상위 서버, 가로채기, 규칙
1단계: 최종 설정과 수신 포트 확인
설정을 다시 불러온 뒤 클라이언트 로그에서 YAML 파싱 실패, 미지원 필드, 포트 바인딩 실패가 있는지 먼저 확인하세요. 최종 적용된 설정에서 dns.enable, listen, enhanced-mode를 중점적으로 살펴보세요. 클라이언트마다 메뉴 이름은 다르므로 구독 원문만 확인하지 말고 실제 실행 설정이나 설정 미리보기를 찾아야 합니다.
macOS, Linux 또는 dig가 설치된 환경에서는 1053번 포트로 직접 조회할 수 있습니다. 첫 번째 명령은 UDP, 두 번째는 TCP를 테스트합니다. 이는 확인용 명령이며 사전에 실행한 테스트 결과를 뜻하지 않습니다.
dig @127.0.0.1 -p 1053 example.com A
dig @127.0.0.1 -p 1053 example.com A +tcp
fake-ip 모드에서 필터링되지 않은 A 레코드 조회는 일반적으로 매핑 주소 대역의 주소를 반환합니다. 이는 현재 DNS 요청 경로가 응답했다는 의미일 뿐, 원격 웹사이트에 접속할 수 있다는 것을 단독으로 증명하지는 않습니다. 해당 연결이 같은 코어로 들어오는지, 노드가 연결을 설정할 수 있는지도 확인해야 합니다.
2단계: 상위 서버 연결 실패와 요청 미가로채기 구분
- 1053번 포트로 직접 조회해도 시간 초과: 먼저 수신이 정상적으로 시작되었는지 확인한 다음 상위 서버 연결 오류를 살펴보세요. 필요하면 접근 가능한 것으로 확인된 nameserver 하나만 잠시 남겨 원인을 좁히세요.
- 1053번 포트 조회에는 응답이 있지만 앱이 다른 DNS를 사용: TUN이 활성화되어 있는지, 라우팅이 적용되었는지, 브라우저의 보안 DNS가 별도 경로를 사용하는지 확인하세요.
- 도메인은 조회되지만 웹페이지가 열리지 않음: 연결 로그에서 규칙 일치 결과, 정책 그룹 선택, 노드 오류를 확인하세요. DNS 서버를 계속 무작정 바꾸지 마세요.
- LAN 이름만 조회되지 않음: 라우터 DNS 또는 mDNS의 조회 경로를 확인하고 LAN 전용 정책이 필요한지 살펴보세요.
3단계: 캐시를 지운 뒤 다시 테스트
Windows에서는 터미널에서 ipconfig /flushdns를 실행할 수 있습니다. macOS에서는 아래 두 명령을 차례로 실행하세요. 두 번째 명령에는 관리자 권한이 필요합니다. 브라우저와 앱에도 별도 캐시가 있을 수 있으므로 시스템 캐시를 지운 뒤 기존 연결을 종료하고 다시 접속하세요.
sudo dscacheutil -flushcache
sudo killall -HUP mDNSResponder
예상하지 않은 서버로 DNS 조회가 전송되는지 확인하려면 시스템, 브라우저, 코어의 실제 동작을 함께 살펴봐야 합니다. DoH 주소가 보이거나 DNS 테스트 웹페이지를 한 번 방문했다는 사실만으로 모든 프로세스의 조회 경로를 설명할 수는 없습니다. fallback의 병렬 조회, 브라우저 자체 DoH, 시스템 백그라운드 요청은 각각 따로 확인해야 합니다.
설정 병합과 이후 조정
하나의 YAML 파일에 최상위 dns:나 tun: 항목을 두 번 작성하지 마세요. 중복 키를 파서가 거부하거나 한쪽 설정이 다른 쪽을 덮어쓸 수 있습니다. 예제의 필드를 기존 항목에 병합하고 클라이언트의 오버라이드 적용 순서를 확인하세요. 구독을 업데이트한 뒤에도 실행 설정이 유지되는지 다시 확인해야 합니다.
- 먼저 정상 작동 기준을 만드세요: 단일 상위 DNS 서버에 연결할 수 있고 로컬 1053번 포트 조회가 정상인지 확인한 뒤 fallback과 TUN 가로채기를 추가하세요.
- 그다음 특정 도메인을 설정하세요: 도메인별 DNS 서버를 지정하려면 nameserver-policy를 사용하고, 연결을 분기하려면 rules를 수정하세요. 두 설정은 각각 따로 검증해야 합니다.
- 마지막으로 IPv6를 확인하세요: 예제의
dns.ipv6: false는 DNS에서 AAAA 응답을 비활성화합니다. 운영체제의 IPv6 기능 전체를 끄는 것은 아니며, 앱이 이미 보유한 IPv6 주소를 사용하는 것도 막지 않습니다. - 복구 방법을 마련해 두세요: 인터넷 연결이 모두 끊기면 TUN을 먼저 끄고 기존 시스템 DNS와 프록시 설정을 복원한 다음 백업을 불러오세요. 확인되지 않은 설정을 계속 추가하지 마세요.
규칙과 TUN 설정의 관계를 더 확인하려면 DNS 설정 안내서와 TUN 설정 설명을 참고하세요. 문제 해결의 핵심은 한 번에 한 단계씩 바꾸는 것입니다. 먼저 요청이 코어에 들어오는지 확인하고, 다음으로 상위 DNS 서버의 응답을 확인한 뒤, 마지막으로 연결 규칙을 살펴보세요. 그래야 변경 사항이 어떤 문제를 해결했는지 판단할 수 있습니다.