01 / POLICY
프록시 그룹 유형과 실제 조합
먼저 노드, 프록시 그룹, 최종 정책을 구분하기
Clash 설정에서 프록시 노드는 구체적인 연결 매개변수이고, 프록시 그룹은 노드나 다른 프록시 그룹을 구성하는 방식입니다. 규칙 행 끝에 작성하는 값은 최종 정책 이름입니다. 세 이름이 화면에 함께 표시될 수 있지만 역할은 서로 다릅니다. 예를 들어 구독에 노드가 여러 개 있다면 먼저 ‘노드 선택’이라는 select 그룹에 넣고, ‘개발 서비스’와 ‘스트리밍’ 같은 업무 그룹에서 ‘노드 선택’을 참조할 수 있습니다. 규칙은 업무 그룹만 가리키면 되므로 각 노드에 일일이 연결할 필요가 없습니다. 이렇게 하면 노드를 바꿀 때 규칙을 수정하지 않아도 되고, 구독이 업데이트되어도 라우팅 로직을 한 줄씩 다시 정리할 필요가 없습니다.
프록시 그룹 이름은 공백과 대소문자를 포함해 규칙의 대상과 완전히 일치해야 합니다. 설정이 파싱된다고 해서 해당 정책이 반드시 존재하는 것은 아닙니다. 일부 클라이언트는 로드 시 정책을 찾을 수 없다고 바로 알리지만, 어떤 경우에는 규칙이 실제로 매칭된 뒤에야 문제가 드러납니다. 이름을 바꾼 뒤에는 설정을 다시 로드하고, 연결 상세 정보나 로그에서 규칙이 매칭한 대상 그룹을 확인하세요. 웹 페이지가 열리는지만으로 판단하지 마세요. 브라우저 캐시, 기존 연결 재사용, 시스템 DNS 캐시 때문에 이전 결과가 잠시 유지될 수 있습니다.
자주 쓰는 네 가지 그룹의 역할
| 유형 | 선택 방식 | 적용 위치 | 주의할 점 |
|---|---|---|---|
select |
사용자가 직접 선택 | 전체 진입점, 업무별 그룹 | 자동으로 전환되지 않아 결과를 가장 쉽게 통제할 수 있음 |
url-test |
주기적으로 테스트하고 응답이 적절한 후보를 선택 | 같은 유형의 노드를 자동 선택 | 테스트 결과는 테스트 주소만 반영하며 모든 사이트의 사용 환경을 의미하지 않음 |
fallback |
후보 순서에 따라 현재 사용 가능한 항목을 선택 | 주 회선과 예비 회선 전환 | 순서는 우선순위를 나타내며, 가장 낮은 응답 시간만을 목표로 하지 않음 |
load-balance |
정책에 따라 서로 다른 연결을 여러 후보에 분배 | 여러 출구를 병렬 사용 | 같은 서비스에서 출구가 달라지면 로그인 보안 검사에 걸릴 수 있음 |
url-test는 서로 대체 가능한 노드 그룹에 적합합니다. 주요 매개변수로는 테스트 주소 url, 테스트 간격 interval, 허용 오차 tolerance가 있습니다. 간격이 너무 짧으면 추가 연결과 배터리 소모가 발생하며 모바일에서 특히 두드러집니다. 오차가 너무 작으면 선택 결과가 자주 바뀔 수 있습니다. 테스트 주소는 안정적이고 응답 크기가 매우 작아야 합니다. 테스트에 성공했다는 것은 현재 네트워크에서 후보 노드를 거쳐 테스트 주소까지의 경로를 사용할 수 있다는 뜻일 뿐, 대상 서비스의 핸드셰이크, 지역 판정 또는 계정 상태까지 정상이라는 의미는 아닙니다.
fallback은 순서를 더 중시합니다. ‘주 회선을 우선 사용하고, 주 회선을 사용할 수 없을 때만 예비 회선으로 전환’하는 상황에 적합합니다. 반면 load-balance는 동시 연결을 여러 노드에 분산합니다. 접속 출발지 주소를 안정적으로 유지해야 하는 로그인, 결제, 실시간 통신과 장기 연결 서비스는 부하 분산 그룹에 바로 넣지 않는 것이 좋습니다. 일관성 해시를 사용하더라도 도메인 변경, 연결 재생성, 규칙 변경으로 출구가 바뀔 수 있다는 점을 이해해야 합니다.
유지 관리가 쉬운 계층형 구성
proxy-groups:
- name: 노드 선택
type: select
proxies:
- 자동 선택
- 장애 조치
- DIRECT
- name: 자동 선택
type: url-test
use:
- airport-main
url: https://www.gstatic.com/generate_204
interval: 600
tolerance: 80
- name: 장애 조치
type: fallback
use:
- airport-main
url: https://www.gstatic.com/generate_204
interval: 600
- name: 개발 서비스
type: select
proxies:
- 노드 선택
- 자동 선택
- DIRECT
- name: 최종 매칭
type: select
proxies:
- 노드 선택
- DIRECT
이 구조는 ‘노드를 어떻게 선택할지’와 ‘업무 트래픽을 어디로 보낼지’를 분리합니다. use는 proxy-providers 이름을 참조하고, proxies는 구체적인 노드나 다른 프록시 그룹 이름을 참조합니다. 둘은 서로 바꿔 쓸 수 없습니다. 구독 노드가 많다면 provider와 필터 조건으로 후보를 관리하는 편이 좋습니다. 그러면 구독이 바뀔 때마다 proxies를 수동으로 수정하지 않아도 됩니다. 클라이언트의 오버라이드 기능이 프록시 그룹에 항목을 추가할 수 있다면 같은 계층을 유지하세요. 노드 출처는 provider가 관리하고, 업무 의미는 로컬 프록시 그룹이 관리해야 합니다.
그룹끼리 순환 참조를 만들면 안 됩니다. 예를 들어 ‘노드 선택’에 ‘자동 선택’을 포함하고, ‘자동 선택’의 proxies에 다시 ‘노드 선택’을 넣으면 최종 출구를 결정할 수 없습니다. 실제로 수정할 때는 규칙의 끝에서 역방향으로 확인하면 됩니다. 규칙은 업무 그룹을 가리키고, 업무 그룹은 전체 진입점을 가리키며, 전체 진입점은 최종적으로 노드나 DIRECT를 가리켜야 합니다. 모든 경로는 반드시 끝나야 합니다. REJECT와 DIRECT 같은 내장 정책은 같은 이름의 노드를 다시 만들 필요가 없습니다.
프록시 그룹을 검증할 때는 먼저 클라이언트 화면에서 예상 항목을 명확히 선택한 다음 대상 앱의 기존 연결을 닫고 요청을 새로 시작하세요. 이후 연결 기록에서 규칙 유형, 규칙 내용, 프록시 그룹과 최종 노드를 확인합니다. 화면에 이전 그룹 이름이 표시된다면 현재 실행 중인 설정이 아직 다시 로드되지 않았을 가능성이 큽니다. 그룹에 새 노드가 없다면 provider 업데이트 성공 여부와 필터 표현식이 모든 노드를 제외하지 않았는지 확인하세요. 규칙 우선순위에 대한 자세한 설명은 사용자 지정 규칙 문법과 매칭 우선순위에서 이어서 확인할 수 있습니다.
02 / RULE PROVIDERS
규칙 집합 구독 관리
규칙 내용을 메인 설정과 분리하기
규칙 수가 늘어난 뒤에도 모든 항목을 메인 설정의 rules에 계속 작성하면 세 가지 문제가 생깁니다. 설정 업데이트가 로컬 수정을 덮어쓰기 쉽고, 중복 도메인을 추적하기 어려우며, 로드 실패 시 어느 규칙 묶음에 문제가 있는지 확인하기 어렵습니다. rule-providers는 외부 규칙 집합의 출처, 동작 유형, 파일 경로와 업데이트 주기를 선언합니다. 메인 규칙 영역은 RULE-SET을 통해 매칭 순서에서의 위치와 매칭 후 사용할 프록시 그룹만 결정합니다.
규칙 집합도 위에서 아래로 진행되는 매칭 방식을 우회하지 않습니다. RULE-SET,developer,개발 서비스가 국내 직결 규칙보다 앞에 있는지 뒤에 있는지에 따라 중복 도메인의 결과가 바로 달라집니다. 계획을 세울 때는 먼저 업무 우선순위를 정한 뒤 규칙 집합을 배열하세요. 다운로드 파일의 이름이나 크기 순으로 정렬해서는 안 됩니다. 확실한 사용자 지정 도메인은 앞에 두고, 로컬 네트워크와 필수 직결 규칙도 앞쪽에 배치하며, 범위가 넓은 지역 집합은 뒤로 보냅니다. 마지막에는 MATCH로 매칭되지 않은 트래픽을 처리합니다.
provider 필드별 설명
rule-providers:
developer:
type: http
behavior: domain
format: yaml
path: ./ruleset/developer.yaml
url: https://example.com/rules/developer.yaml
interval: 86400
private-network:
type: file
behavior: ipcidr
format: text
path: ./ruleset/private-network.txt
rules:
- RULE-SET,developer,개발 서비스
- RULE-SET,private-network,DIRECT,no-resolve
- GEOIP,CN,DIRECT
- MATCH,최종 매칭
type: http는 코어가 url에서 가져와 캐시한다는 뜻이고, type: file은 로컬 파일만 읽는다는 뜻입니다. 예시 도메인은 구조를 보여 주기 위한 것이므로 실제로는 직접 접속 가능한지 확인한 규칙 출처로 바꿔야 합니다. path는 캐시 또는 로컬 규칙 파일의 위치입니다. 같은 설정의 provider가 하나의 경로를 공유하면 안 됩니다. 나중에 기록된 내용이 앞선 파일을 덮어쓸 수 있기 때문입니다. 상대 경로의 기준은 클라이언트가 코어에 지정한 작업 디렉터리에 따라 달라집니다. 데스크톱 클라이언트는 보통 이를 자체 설정 디렉터리로 연결하며, 사용자가 현재 열어 둔 터미널 디렉터리를 기준으로 삼지 않습니다.
behavior는 규칙 데이터의 해석 방식을 결정합니다. domain은 도메인 집합에 적합하고, ipcidr은 IPv4 및 IPv6 네트워크 대역에 적합하며, classical은 각 줄에 완전한 규칙 유형을 포함할 수 있습니다. 잘못 선택하면 파일 다운로드는 성공해도 예상대로 매칭되지 않을 수 있습니다. 예를 들어 DOMAIN-SUFFIX,example.com이 포함된 classical 콘텐츠를 domain으로 선언하면 해석 방식이 맞지 않습니다. 먼저 규칙 출처가 제공하는 원본 형식을 확인하고, 파일 확장자만 보고 behavior를 추측하지 마세요.
format은 데이터 인코딩을 설명하며, 일반적으로 yaml, text 또는 코어가 지원하는 바이너리 규칙 형식을 사용합니다. 형식마다 콘텐츠 구조가 다릅니다. YAML domain provider는 최상위 payload 배열을 사용하는 경우가 많고, 텍스트 형식은 보통 항목을 한 줄씩 저장합니다. 구독 출처에서 Mihomo 형식을 명시했다면 그 설명에 따라 선택하세요. 일반 hosts 파일, 광고 차단 문법 또는 브라우저 확장 규칙을 Clash 규칙 집합으로 바로 사용해서는 안 됩니다.
interval의 단위는 초이며 업데이트 확인 주기를 제어합니다. 설정을 로드한 뒤 처음 가져올 때 반드시 이 시간만큼 기다린다는 뜻은 아닙니다. 주기를 지나치게 짧게 설정하지 마세요. 규칙 출처는 보통 노드 상태만큼 자주 업데이트되지 않으므로 잦은 요청은 시작 시간과 네트워크 비용만 늘립니다. 클라이언트에 ‘규칙 집합 업데이트’ 기능이 있다면 출처를 수정한 뒤 한 번 수동 실행하고, 로그에서 HTTP 상태, 파싱 결과와 캐시 경로를 확인하세요.
도메인 집합, IP 집합과 no-resolve
도메인 규칙은 요청에 도메인 정보가 남아 있을 때 가장 직접적으로 작동합니다. IP-CIDR 규칙은 대상 IP를 대상으로 합니다. IP 규칙 뒤에 no-resolve가 붙으면 해당 규칙을 매칭할 때 대상 IP를 얻기 위해 도메인을 능동적으로 해석하지 않는다는 뜻입니다. 이는 ‘DNS 비활성화’가 아니며 앱이 이미 수행한 해석을 막지도 않습니다. 순수 IP 연결에서는 코어가 대상 IP를 직접 매칭할 수 있습니다. 이 매개변수를 추가할지는 앞선 DNS 및 스니핑 과정에서 충분한 정보가 제공되는지, 그리고 추가 해석을 실행할 가치가 있는 규칙인지에 따라 결정하세요.
| 규칙 집합 콘텐츠 | behavior | 대표 데이터 | 주요 용도 |
|---|---|---|---|
| 도메인 및 도메인 접미사 | domain |
example.com、+.example.org |
웹사이트와 서비스 그룹화 |
| IPv4/IPv6 네트워크 대역 | ipcidr |
192.0.2.0/24 |
지역 네트워크 대역, 사설 네트워크 |
| 완전한 규칙 행 | classical |
DOMAIN-SUFFIX,example.com |
여러 규칙 유형 혼합 |
업데이트 실패 시 확인 순서
먼저 ‘다운로드 실패’와 ‘파싱 실패’를 구분하세요. 다운로드 실패는 보통 로그에 연결, 인증서, 시간 초과 또는 HTTP 상태와 관련된 정보로 나타납니다. 파싱 실패는 파일을 가져오기는 했지만 필드, 들여쓰기, behavior 또는 format이 요구 사항과 맞지 않는다는 뜻인 경우가 많습니다. 다음으로 현재 네트워크 경로에서 URL에 접속할 수 있는지 확인하세요. 규칙 provider의 다운로드 트래픽이 어떻게 라우팅되는지는 현재 코어의 시작 단계와 설정에 영향을 받습니다. 최초 로드 시 아직 생성되지 않은 정책에 의존하면 순서 문제가 발생할 수 있습니다.
그다음 캐시 디렉터리에 쓰기 권한이 있는지 확인하세요. 그래픽 인터페이스로 설정을 관리하는 데스크톱 클라이언트에서는 path를 시스템 보호 디렉터리로 지정하지 않는 것이 좋습니다. 파일 이름은 서로 달라야 하며, 아직 생성되지 않은 절대 경로에 디렉터리 구조를 의존해서도 안 됩니다. 마지막으로 메인 규칙에 해당 RULE-SET이 실제로 있고 이름이 provider 키와 일치하는지 확인하세요. provider 다운로드에 성공했어도 rules에서 참조하지 않으면 캐시만 차지할 뿐 라우팅에는 참여하지 않습니다.
규칙 집합 출처가 불안정할 때는 최소한의 실행 가능한 설정을 유지하세요. 기본 직결, 필수 프록시와 최종 규칙을 모두 원격 파일에 의존하게 만들지 마세요. 그러면 provider가 일시적으로 새로 고쳐지지 않아도 기존 캐시나 로컬 기본 규칙으로 설정을 시작할 수 있습니다. 중국 본토는 직결하고 그 밖의 트래픽은 프록시로 보내는 구성을 전체 순서로 정리하려면 중국 본토 및 해외 트래픽 라우팅 구성 방법을 참고한 뒤, 이 장의 방법에 따라 안정적인 대형 집합을 provider로 나누세요.
03 / DNS
DNS 설정 최적화와 누수 점검
라우팅 체인에서 DNS의 위치 이해하기
DNS 설정은 단순히 해석 서버를 바꾸는 작업이 아닙니다. Mihomo DNS를 켜면 코어는 어느 upstream에 질의할지, 질의 트래픽이 어떤 네트워크 경로를 사용할지, 실제 주소와 Fake-IP 중 무엇을 반환할지, 해석 결과를 규칙 매칭에 어떻게 사용할지를 결정해야 합니다. 앱이 시스템 DNS에 직접 요청할 수도 있고 자체 암호화 DNS를 사용할 수도 있습니다. 브라우저에는 보안 DNS가 켜져 있을 수 있습니다. 먼저 요청이 코어로 들어오는지 확인해야 이후 nameserver를 조정하는 의미가 생깁니다.
일반적인 흐름은 다음과 같습니다. 앱이 도메인을 요청하면 시스템 또는 TUN이 DNS 요청을 코어에 전달하고, 코어는 도메인 규칙에 따라 upstream을 선택해 결과를 얻거나 Fake-IP를 할당합니다. 이후 연결을 만들 때 도메인을 복원하고 라우팅을 수행합니다. 앱이 시스템 해석을 우회해 자체 DNS 서버에 직접 접근한다면 TUN 라우팅, DNS 하이재킹 또는 앱 설정으로 요청을 같은 경로에 포함해야 합니다. 시스템 DNS 주소만 특정 공용 서비스로 바꾼다고 해서 모든 질의가 자동으로 Clash 정책을 따르는 것은 아닙니다.
문제 해결에 유용한 기본 설정
dns:
enable: true
listen: 0.0.0.0:1053
ipv6: false
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.1/16
fake-ip-filter-mode: blacklist
fake-ip-filter:
- "*.lan"
- "localhost.ptlogin2.qq.com"
- "+.stun.*.*"
default-nameserver:
- 223.5.5.5
- 1.1.1.1
nameserver:
- https://dns.alidns.com/dns-query
- https://cloudflare-dns.com/dns-query
proxy-server-nameserver:
- https://dns.alidns.com/dns-query
direct-nameserver:
- https://dns.alidns.com/dns-query
respect-rules: true
default-nameserver는 주로 암호화 DNS upstream 자체의 도메인을 해석하는 데 사용됩니다. 따라서 보통 직접 접근할 수 있는 IP 주소를 입력해 ‘DNS 서버 도메인을 먼저 해석해야 하는데, 이를 위해 다시 해당 서버에 연결해야 하는’ 순환 의존을 피합니다. 모든 업무 도메인의 기본 최종 응답을 제공하는 항목은 아닙니다. 일반 질의의 주요 upstream은 nameserver이며, IP 형식의 기존 DNS나 코어가 지원하는 암호화 DNS 주소를 사용할 수 있습니다.
proxy-server-nameserver는 프록시 서버 자체의 도메인을 해석하는 데 사용됩니다. 노드 주소를 도메인으로 작성했다면 프록시 연결을 만들기 전에 실제 IP를 얻어야 하며, 이 단계는 아직 만들어지지 않은 프록시 체인에 의존할 수 없습니다. 해당 upstream은 현재 직결 환경에서 안정적으로 접근할 수 있어야 합니다. 노드 주소 자체가 IP라면 중요성은 낮아지지만, 명확한 초기 해석 경로를 남겨 두면 구독 출처를 바꿀 때 유리합니다.
direct-nameserver는 직결될 것으로 예상되는 도메인에 해석 경로를 지정할 수 있습니다. respect-rules와 함께 사용할 때는 규칙과 DNS 사이의 의존성을 특히 주의해야 합니다. 설정이 지나치게 복잡하면 질의가 규칙을 먼저 알아야 하고, 규칙은 다시 질의 결과에 의존하는 상황이 생길 수 있습니다. 문제 해결 단계에서는 접근이 확실한 upstream 몇 개만 사용해 일반 질의와 프록시 노드 해석이 정상인지 확인한 뒤, 도메인별 라우팅을 위한 nameserver-policy를 추가하는 것이 좋습니다.
nameserver-policy 정확히 사용하기
dns:
nameserver-policy:
"geosite:cn":
- https://dns.alidns.com/dns-query
"+.example.internal":
- 192.168.1.1
"rule-set:developer":
- https://cloudflare-dns.com/dns-query
nameserver-policy는 도메인에 따라 해석 upstream을 선택하며, 사설 네트워크 도메인, 특정 서비스 또는 지역 도메인의 해석 요구에 적합합니다. 이 항목이 결정하는 것은 ‘어디에 질의할지’이며 연결 트래픽의 프록시 정책과 직접 같은 의미는 아닙니다. 도메인을 로컬 DNS로 해석한다고 해서 연결이 반드시 직결되는 것은 아닙니다. 최종 결정은 여전히 rules가 내립니다. 반대로 원격 암호화 DNS로 주소를 얻었다고 해서 업무 연결이 반드시 프록시를 사용해야 하는 것도 아닙니다. 해석 경로와 연결 경로를 분리해 이해하면 모순처럼 보이는 매칭 결과를 많이 줄일 수 있습니다.
사설 네트워크 도메인을 라우터나 기업 DNS가 반드시 응답해야 한다면 명확한 접미사에 로컬 네트워크 DNS를 지정할 수 있습니다. 범위가 넓은 와일드카드를 모두 내부 서버로 보내지는 마세요. 해당 네트워크를 벗어나면 대량의 시간 초과가 발생할 수 있습니다. 노트북으로 네트워크를 자주 바꾼다면 사무실 전용 규칙을 별도 오버라이드에 넣고 필요할 때 활성화하는 편이 좋습니다. 모든 환경의 메인 설정에 영구적으로 기록하지 않아도 됩니다.
IPv6, 캐시와 폴백 동작
ipv6: false는 보통 DNS 모듈이 AAAA 결과를 반환하지 않는다는 뜻이며, 운영체제 수준에서 IPv6를 끈다는 의미는 아닙니다. 앱이 다른 해석 경로로 IPv6 주소를 얻거나 IPv6에 직접 연결하면 예상한 경로를 우회할 수 있습니다. 네트워크의 IPv6가 불안정하거나 프록시 노드가 IPv6를 지원하지 않거나 규칙 집합이 IPv4만 다룬다면 DNS의 IPv6 반환을 먼저 끄는 것이 연결 대기를 줄이는 데 도움이 됩니다. IPv6가 꼭 필요하다면 TUN 라우팅, 규칙 집합과 출구의 지원 여부도 함께 확인해야 합니다.
DNS 캐시 때문에 변경한 결과가 즉시 반영되지 않을 수 있습니다. 설정을 다시 로드한 뒤 클라이언트 내부 캐시를 지우세요. 필요하면 운영체제와 브라우저 캐시도 지우고 연결을 새로 만들어야 합니다. 닫지 않은 브라우저 탭에서 계속 새로 고침하는 방식으로 규칙을 판단하지 마세요. HTTP/2, HTTP/3와 연결 풀이 기존 연결을 계속 재사용할 수 있습니다. 확실한 방법은 대상 앱의 연결을 닫고 캐시를 지운 뒤 설정을 다시 로드하고, DNS 로그와 연결 기록을 함께 보며 다시 테스트하는 것입니다.
| 현상 | 우선 확인할 항목 | 일반적인 원인 |
|---|---|---|
| 노드 도메인을 해석할 수 없음 | default-nameserver、proxy-server-nameserver |
초기 해석 순환 또는 upstream 직결 불가 |
| 사설 네트워크 도메인이 열리지 않음 | nameserver-policy, Fake-IP 필터 |
사설 네트워크 질의가 공용 upstream으로 전송됨 |
| 규칙을 바꿔도 이전 경로로 연결됨 | DNS 캐시, 기존 연결 | 이전 해석 결과와 연결 풀이 계속 재사용됨 |
| 일부 앱이 로그에 나타나지 않음 | 앱 보안 DNS, TUN 라우팅 | 앱이 시스템 DNS 또는 프록시 설정을 우회함 |
DNS가 예상대로 작동하는지 판단할 때는 네 가지 사실을 기록해야 합니다. 앱이 질의한 도메인, 요청이 코어로 들어온 방식, 실제로 사용한 upstream, 연결이 최종적으로 매칭한 규칙입니다. ‘검사 사이트에 특정 DNS가 표시된다’는 사실만으로는 브라우저 보안 DNS, 시스템 캐시, upstream 전달과 코어 설정 중 무엇이 원인인지 찾을 수 없습니다. 복잡한 문제는 도움말 센터에서 DNS, 시스템 프록시, 연결 로그 순서로 계속 확인하세요.
04 / TUN & FAKE-IP
TUN 모드와 Fake-IP 연동
시스템 프록시와 TUN의 적용 범위
시스템 프록시는 운영체제 프록시 설정을 따르는 앱에만 영향을 줍니다. 브라우저와 대부분의 데스크톱 소프트웨어는 지원하지만 명령줄 도구, 게임, 가상 머신, 일부 스토어 앱과 자체 네트워크 스택을 구현한 소프트웨어는 무시할 수 있습니다. TUN 모드는 가상 네트워크 인터페이스를 만들고 시스템 라우팅을 통해 더 많은 TCP 및 UDP 트래픽을 코어에 전달하므로 적용 범위가 더 넓습니다. 대신 라우팅, DNS와 인터페이스 선택에 관여하므로 설정이 잘못되면 일반 시스템 프록시보다 영향도 더 큽니다.
TUN을 켜기 전에 일반 프록시 모드에서 노드, 규칙과 DNS가 모두 작동하는지 확인하세요. 그렇지 않으면 TUN에 들어간 뒤 노드 불가, DNS 순환과 라우팅 충돌이 한꺼번에 발생해 원인을 판단하기 어렵습니다. 권장 순서는 노드 연결 확인, 규칙 매칭 확인, TUN 활성화, DNS 하이재킹과 Fake-IP 활성화입니다. 매번 한 그룹의 변수만 바꾸고 복구할 수 있는 설정 사본을 남기세요.
TUN 기본 매개변수
tun:
enable: true
stack: mixed
dns-hijack:
- any:53
- tcp://any:53
auto-route: true
auto-detect-interface: true
strict-route: true
mtu: 1500
dns:
enable: true
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.1/16
stack은 TUN 트래픽을 어떤 네트워크 스택으로 처리할지 결정합니다. Mihomo에서 자주 선택하는 값은 system, gvisor, mixed입니다. 시스템 스택은 보통 성능 경로가 직접적이고, gVisor 사용자 공간 스택은 플랫폼이나 특수 네트워크에 따라 호환성이 다르며, mixed는 두 방식을 조합해 처리합니다. 모든 시스템에 맞는 고정 답은 없습니다. 기본 설정이 안정적이라면 매개변수를 복잡하게 만들기 위해 바꿀 필요가 없습니다. UDP, LAN 접근 또는 특정 게임에 문제가 있을 때만 네트워크 스택을 별도 변수로 두고 비교 테스트하세요.
auto-route는 코어가 필요한 라우팅을 자동으로 추가하게 하고, auto-detect-interface는 현재 기본 출구를 식별하는 데 사용됩니다. 노트북이 유선, 무선, 핫스팟과 VPN 사이를 전환한다면 자동 감지가 편리합니다. 서버, 다중 NIC 호스트 또는 정책 라우팅 환경에서는 인터페이스를 명시해야 할 수 있습니다. 로그에서 트래픽이 반복해서 TUN으로 들어가거나 프록시 노드 연결 자체가 다시 TUN으로 전송된다면 출구 인터페이스, 라우팅 제외와 기존 VPN이 순환을 만드는지 확인하세요.
strict-route는 트래픽이 TUN을 거치도록 더 엄격하게 제한합니다. 구체적인 효과와 권한 요구 사항은 운영체제마다 다릅니다. 일부 우회를 줄이는 데 도움이 되지만, 시스템이 자동으로 처리하던 라우팅 충돌을 드러낼 수도 있습니다. 활성화한 뒤 LAN 프린터, 공유 디렉터리 또는 가상 머신 네트워크에 접근할 수 없다면 모든 사설 네트워크 주소를 프록시 정책에 추가하기보다 사설 대역 규칙과 라우팅 제외를 먼저 확인하세요.
dns-hijack는 지정 포트의 DNS 트래픽을 코어로 전달합니다. any:53은 일반적인 UDP 질의를 처리하며, TCP 형식을 추가하면 잘린 응답 뒤의 TCP 재시도를 처리할 수 있습니다. HTTPS나 TLS 기반으로 앱이 자체적으로 사용하는 모든 DNS를 자동으로 가로채지는 못합니다. 이런 트래픽은 겉으로는 일반 암호화 연결이므로 앱 설정, 도메인 규칙 또는 전체 라우팅 경로로 처리해야 합니다. 하이재킹이 필요한지는 앱이 시스템 DNS를 따르는지에 따라 결정하세요.
Fake-IP 작동 원리
Fake-IP 모드에서는 도메인 질의를 받았을 때 실제 대상 주소를 앱에 즉시 전달하지 않고 예약된 주소 풀에서 매핑 주소를 할당합니다. 앱이 해당 주소에 연결하면 코어가 매핑을 통해 원래 도메인을 복원한 뒤 도메인 규칙과 프록시 전달을 수행합니다. 따라서 앱이 이후 IP만 전달하더라도 코어는 도메인 맥락을 유지할 수 있습니다. 도메인 기반 라우팅이 더 안정적이고, 로컬에서 실제 주소를 먼저 얻은 뒤 정책을 결정해야 하는 의존성도 줄어듭니다.
198.18.0.0/15는 벤치마크 용도의 예약 주소 범위이며 Fake-IP에 자주 사용됩니다. 설정의 주소 풀은 기존 LAN, 컨테이너 네트워크, 테스트 네트워크 또는 기업 라우팅과 충돌해서는 안 됩니다. 현재 네트워크가 우연히 같은 범위를 사용하면 시스템이 Fake-IP를 실제 라우팅 대상으로 취급할 수 있습니다. 이 경우 충돌하지 않는 예약 범위로 바꾸고 관련 연결을 재시작한 뒤 DNS 캐시를 지워 이전 매핑이 더 이상 사용되지 않도록 하세요.
일부 프로토콜은 실제 주소가 필요하거나 DNS 동작을 검증하므로 Fake-IP 반환에 적합하지 않습니다. 대표적으로 LAN 호스트명, STUN, 네트워크 연결성 검사, 특정 게임 검색 프로토콜과 일부 기기 제어 서비스가 있습니다. 이런 대상은 fake-ip-filter에 넣어야 합니다. 필터 항목은 최대한 정확하게 작성하고, 먼저 로그에서 도메인을 확인한 뒤 접미사나 와일드카드 패턴을 추가하세요. 광범위한 도메인을 모두 제외하면 Fake-IP의 도메인 보존 장점이 크게 줄어듭니다.
MTU, UDP와 LAN 접근
MTU가 너무 크면 일부 터널, 모바일 네트워크 또는 VPN이 겹친 환경에서 단편화와 패킷 손실이 발생할 수 있습니다. 웹 페이지의 일부 리소스가 멈추거나 TLS 핸드셰이크가 시간 초과되고 UDP가 불안정해지는 식으로 나타납니다. 너무 작으면 패킷 수와 처리 비용이 늘어납니다. 특정 웹사이트가 느리다는 이유만으로 MTU를 무작정 낮추지 마세요. 먼저 문제가 TUN에서만 발생하는지 확인한 뒤 네트워크와 프로토콜을 비교하고, 로그에 재전송이나 핸드셰이크 실패가 뚜렷한지 살펴보세요. 조정 폭은 기록할 수 있게 유지하고 TUN을 재시작한 뒤 테스트하세요.
LAN 접근은 규칙과 라우팅을 함께 고려해야 합니다. 규칙에서 RFC1918 사설 대역, 링크 로컬 주소와 실제 LAN 도메인을 DIRECT로 지정할 수 있지만 DIRECT는 연결이 프록시 노드를 거치지 않게 할 뿐 운영체제 라우팅이 올바른 인터페이스를 가리킨다는 보장은 없습니다. TUN 자동 라우팅이 LAN 경로를 가로채면 라우팅 제외와 strict-route 설정도 확인해야 합니다. 같은 네트워크 대역의 기기에 프록시를 공유하려면 리스닝 주소, allow-lan과 방화벽을 추가로 설정해야 하며, 혼합 포트와 LAN 공유를 참고할 수 있습니다.
| 문제 범위 | 비교 테스트 | 다음 단계 |
|---|---|---|
| 시스템 프록시만 정상 | TUN을 끄면 복구됨 | 라우팅, 인터페이스 감지와 권한 확인 |
| 도메인 규칙이 작동하지 않음 | Fake-IP 매핑과 스니핑 결과 확인 | DNS가 실제로 코어에 들어오는지 확인 |
| LAN 기기에 접근할 수 없음 | 대상 대역과 출구 인터페이스 확인 | 직결 규칙과 라우팅 제외 추가 |
| UDP 앱 이상 | 네트워크 스택을 바꾸고 별도로 테스트 | 노드의 UDP 지원과 MTU 확인 |
05 / SNIFFER
도메인 스니핑과 연결 복원
스니핑으로 해결하는 문제
규칙 시스템은 도메인 기준 분류에 강하지만, 일부 앱은 연결을 만들 때 대상 IP만 코어에 노출합니다. 도메인 스니핑은 연결 초기 데이터에서 HTTP Host, TLS ClientHello의 SNI 또는 지원 프로토콜의 대상 도메인을 식별한 뒤 복원한 도메인을 규칙 매칭에 사용합니다. HTTPS 본문을 복호화하거나 페이지 내용을 읽는 기능은 아닙니다. 확인 가능한 정보는 프로토콜 핸드셰이크 단계에서 원래 전달되는 도메인 필드에서 나옵니다.
스니핑과 Fake-IP는 모두 도메인 맥락을 보존하지만 경로가 다릅니다. Fake-IP는 DNS 질의 단계에서 도메인과 매핑 주소의 관계를 만들고, 스니핑은 연결 단계에서 프로토콜 데이터로부터 도메인을 복원합니다. 둘을 함께 사용할 수 있습니다. Fake-IP는 코어 DNS를 거친 연결을 처리하고, 스니핑은 해당 해석 과정을 우회하거나 대상 IP를 직접 사용하는 상황을 보완합니다. DNS가 이미 안정적으로 매핑을 제공한다면 모든 문제를 스니핑 탓으로 돌릴 필요는 없습니다.
프로토콜과 포트로 범위 제한
sniffer:
enable: true
force-dns-mapping: true
parse-pure-ip: true
override-destination: false
sniff:
HTTP:
ports:
- 80
- 8080-8880
override-destination: true
TLS:
ports:
- 443
- 8443
QUIC:
ports:
- 443
skip-domain:
- "Mijia Cloud"
- "+.push.apple.com"
skip-src-address:
- 192.168.0.0/16
skip-dst-address:
- 192.168.0.0/16
parse-pure-ip는 처음부터 순수 IP로 보이는 대상 연결에도 스니핑을 시도하게 합니다. 도메인 기반 라우팅을 보완하는 대표적인 상황입니다. force-dns-mapping은 DNS 매핑과 협력해 이미 존재하는 매핑 관계를 활용합니다. 클라이언트마다 포함된 코어 설정 템플릿이 기본값을 조정할 수 있으므로 수정하기 전에 구독 원문만 보지 말고 현재 실제 실행 설정을 확인하세요.
override-destination은 도메인을 식별한 뒤 스니핑 결과로 후속 연결의 원래 대상을 덮어쓸지 결정합니다. 활성화하면 일부 도메인 라우팅이 개선되지만 잘못 식별했을 때 영향도 직접적입니다. 처음부터 전역 덮어쓰기를 사용하기보다 HTTP처럼 명확한 상황을 프로토콜 하위 항목에서 먼저 활성화하는 것이 좋습니다. 수정 후에는 연결 상세 정보에서 원래 대상, 스니핑된 도메인과 최종 규칙을 확인해 변화가 예상과 맞는지 검증하세요.
포트 범위가 넓다고 식별 성능이 더 좋아지는 것은 아닙니다. 스니퍼는 연결 앞부분의 데이터를 프로토콜에 맞게 분석해야 하므로 HTTP, TLS 또는 QUIC가 아닌 포트에서 무리하게 시도하면 오판과 처리 비용만 늘어납니다. 일반 포트 외의 서비스는 실제 앱에 맞춰 추가하세요. 예를 들어 내부 HTTPS 서비스가 9443에서 실행된다면 해당 포트를 TLS 범위에 넣을 수 있습니다. 이를 포함하려고 모든 포트를 TLS 스니핑에 넘겨서는 안 됩니다.
QUIC, ECH와 보이지 않는 경계
QUIC는 보통 UDP를 기반으로 하며 식별 가능 여부는 코어, 네트워크 스택과 핸드셰이크 정보를 사용할 수 있는지에 따라 달라집니다. 브라우저는 네트워크가 바뀐 뒤 TCP/TLS로 폴백할 수 있으므로 같은 사이트의 연결 기록에 서로 다른 프로토콜이 나타날 수 있습니다. 문제를 확인할 때는 한 번의 접속만 보지 말고 TCP와 UDP를 따로 관찰하세요. 노드나 네트워크의 UDP 지원이 불안정하다면 비교를 위해 앱의 QUIC를 잠시 끌 수 있지만, 이는 위치를 확인하는 방법일 뿐 실제 UDP 경로 점검을 대신하지는 않습니다.
암호화된 클라이언트 인사와 같은 메커니즘은 중간 계층에서 볼 수 있는 도메인 정보를 줄입니다. 핸드셰이크에 식별 가능한 평문 도메인이 없으면 스니핑으로 업무 이름을 임의로 복원할 수 없습니다. 이때는 코어 DNS, Fake-IP 매핑, 앱 프로세스 규칙 또는 대상 IP 규칙에 의존해야 합니다. 설정 목표는 스니핑이 모든 연결을 처리하도록 요구하는 것이 아니라 여러 신뢰할 수 있는 정보 출처가 서로 보완하게 만드는 것입니다.
CDN의 공유 IP도 스니핑이 필요한 이유 중 하나입니다. IP만 기준으로 판단하면 하나의 주소에 완전히 다른 도메인이 여러 개 호스팅될 수 있습니다. 넓은 네트워크 대역으로 라우팅하면 다른 업무까지 잘못 처리하기 쉽습니다. 도메인을 얻을 수 있다면 도메인 규칙을 우선 사용하세요. 연결에 도메인 맥락이 전혀 없을 때만 IP-CIDR, GEOIP 또는 최종 규칙으로 폴백합니다.
건너뛰기 목록과 오판 처리
skip-domain은 스니핑이나 대상 덮어쓰기에 적합하지 않은 것으로 알려진 도메인을 건너뛰는 데 사용합니다. skip-src-address와 skip-dst-address는 특정 출발지 및 대상 대역을 제외할 수 있습니다. 스마트홈, 화면 전송, LAN 검색과 제조사 푸시 서비스에 문제가 생기면 먼저 로그로 구체적인 연결을 찾은 뒤 가장 좁은 범위로 제외하세요. 사설 네트워크 전체를 바로 건너뛰면 간단하지만 도메인 라우팅이 필요한 로컬 컨테이너나 개발 환경의 정보까지 잃을 수 있습니다.
식별된 도메인이 앱의 예상과 다르면 먼저 대상이 CDN, 리디렉션 또는 타사 정적 리소스를 거치는지 확인하세요. 한 페이지가 주 도메인, 로그인 도메인, 이미지 도메인과 통계 엔드포인트에 동시에 연결하는 것은 정상입니다. 실제 오판은 덮어쓰기를 켠 뒤 원래 작동하던 연결이 실패하고, 해당 프로토콜의 override-destination을 끄면 복구되는 형태로 나타나는 경우가 많습니다. 로그의 스니핑 결과가 인증서나 서비스 대상과 뚜렷하게 다르다면 특히 의심해야 합니다.
| 로그 현상 | 의미 | 처리 방향 |
|---|---|---|
| 대상에 IP만 있고 도메인이 나타나지 않음 | 사용 가능한 매핑이나 프로토콜 정보가 없음 | DNS 경로, 포트 범위와 프로토콜 지원 확인 |
| 도메인을 식별했지만 여전히 IP 규칙에 매칭됨 | 규칙 순서나 덮어쓰기 설정이 도메인을 사용하지 않음 | 도메인 규칙 위치와 override 설정 확인 |
| 스니핑을 끄면 앱이 복구됨 | 오판 또는 대상 덮어쓰기 호환성 문제 가능성 | 포트 범위를 줄이고 정확한 건너뛰기 항목 추가 |
| TCP는 정상이고 UDP는 이상함 | QUIC 경로와 TLS 경로가 다름 | TUN, 노드 UDP와 QUIC를 따로 확인 |
프로세스 규칙은 보완 수단이 될 수 있지만 플랫폼마다 프로세스 정보를 얻는 능력, 권한과 정확성이 다릅니다. 특히 모바일 앱 샌드박스, 시스템 서비스와 컨테이너 환경에서는 주의해야 합니다. 안정적인 도메인 규칙으로 해결할 수 있다면 도메인을 우선하세요. 도메인이 없고 프로세스 정보가 신뢰할 만할 때만 프로세스를 고려하고, 둘 다 사용할 수 없을 때 IP 범위와 최종 규칙을 사용하세요. 이처럼 단계적으로 폴백하는 방식이 모든 트래픽을 앱 이름에 묶는 것보다 크로스 플랫폼 유지 관리에 유리합니다.
06 / OVERRIDES
로컬 오버라이드와 다중 구독 병합
원격 구독은 완성품이 아니라 입력으로 취급하기
원격 구독은 주로 노드를 제공하며, 때로는 프록시 그룹, 규칙과 DNS도 포함합니다. 구독이 생성한 YAML을 직접 수정하면 다음 업데이트에서 로컬 내용이 덮어써지는 경우가 많습니다. 더 안정적인 구조는 구독을 업데이트 가능한 입력으로 취급하고, 장기적으로 유지할 규칙, 정책 이름, DNS와 TUN 매개변수를 로컬 오버라이드 계층에 보관하는 것입니다. 클라이언트는 구독을 업데이트할 때마다 오버라이드를 다시 적용하므로 실행 설정을 일관되게 유지할 수 있습니다.
클라이언트마다 오버라이드의 명칭과 기능은 완전히 같지 않습니다. 스크립트, 확장 설정, 병합 설정 또는 설정 전처리 형태로 제공될 수 있습니다. Clash Plus 등의 클라이언트는 실제 화면에 제공되는 설정 진입점을 기준으로 사용하세요. 어떤 방식을 쓰든 원격 원본 구독, 로컬 오버라이드 소스와 코어 최종 실행 설정을 구분해야 합니다. 문제를 해결할 때 가장 중요한 것은 마지막 항목입니다. 화면에서 저장에 성공했다고 해서 병합 결과가 예상과 같다는 뜻은 아니기 때문입니다.
매핑과 배열의 병합 차이
YAML 매핑은 키와 값으로 구성됩니다. 예를 들어 dns 아래의 enable이 그렇습니다. 배열은 순서가 있는 항목으로 구성되며 rules와 proxy-groups가 그 예입니다. 매핑은 보통 키 단위로 덮어쓸 수 있지만 배열은 교체, 앞에 추가, 뒤에 추가와 중복 제거를 고려해야 합니다. 병합 도구가 배열을 전체 교체 방식으로 처리한다면 로컬 규칙 한 줄만 작성해도 구독의 기존 규칙이 모두 삭제될 수 있습니다. 추가 방식이라면 중요한 사용자 지정 규칙을 끝에 넣었을 때 영원히 매칭되지 않을 수 있습니다.
따라서 먼저 클라이언트의 병합 의미를 확인한 뒤 오버라이드 구조를 결정해야 합니다. 규칙 배열에는 보통 ‘앞에 추가’ 기능이 필요합니다. 정확한 로컬 규칙이 범위가 넓은 규칙보다 앞에 와야 하기 때문입니다. 프록시 그룹 배열은 이름으로 교체하거나 추가하는 방식이 자주 필요하고, DNS 매핑은 키 단위 덮어쓰기에 적합합니다. 이름에 Merge가 들어간 모든 기능이 같은 동작을 한다고 가정하지 마세요. 클라이언트를 업데이트하거나 플랫폼을 옮긴 뒤에는 최종 설정을 다시 내보내 비교해야 합니다.
# 로컬에서 관리하는 논리 예시이며, 구체적인 병합 진입점은 클라이언트에 따름
prepend-rules:
- DOMAIN-SUFFIX,example.internal,DIRECT
- DOMAIN-SUFFIX,github.com,개발 서비스
override:
mode: rule
log-level: info
dns:
enable: true
enhanced-mode: fake-ip
append-proxy-groups:
- name: 로컬 서비스
type: select
proxies:
- DIRECT
- 노드 선택
위의 prepend-rules, override와 append-proxy-groups는 병합 의도를 설명하기 위한 것이며 Mihomo 메인 설정에서 사용할 수 있는 일반적인 최상위 키가 아닙니다. 실제 클라이언트는 그래픽 폼, JavaScript 처리 스크립트 또는 자체 확장 문법을 사용할 수 있습니다. 이 내용을 코어 설정에 그대로 붙여 넣지 마세요. 코어에 전달되는 결과는 여전히 표준 rules, proxy-groups와 dns 등의 필드여야 합니다.
provider로 여러 노드 출처 조합하기
proxy-providers:
provider-a:
type: http
url: https://example.com/subscription/a
path: ./providers/a.yaml
interval: 21600
health-check:
enable: true
url: https://www.gstatic.com/generate_204
interval: 600
provider-b:
type: http
url: https://example.com/subscription/b
path: ./providers/b.yaml
interval: 21600
filter: "(?i)홍콩|HK|Hong Kong"
health-check:
enable: true
url: https://www.gstatic.com/generate_204
interval: 600
proxy-groups:
- name: 전체 노드
type: select
use:
- provider-a
- provider-b
- name: 홍콩 자동
type: url-test
use:
- provider-a
- provider-b
filter: "(?i)홍콩|HK|Hong Kong"
url: https://www.gstatic.com/generate_204
interval: 600
proxy-providers를 사용하면 여러 구독을 독립적으로 업데이트하고 캐시한 뒤 프록시 그룹에서 use로 참조할 수 있습니다. 여러 구독 텍스트를 직접 이어 붙이는 것보다 문제를 찾기 쉽습니다. 한 출처가 실패해도 다른 provider는 계속 로드할 수 있기 때문입니다. 각 provider는 서로 다른 path를 사용해야 합니다. 구독 링크는 민감한 설정이므로 로그 스크린샷, 공개 규칙 저장소 또는 공유 설정에 복사하지 마세요. 예시 주소는 구조를 보여 주기 위한 것일 뿐입니다.
filter는 보통 노드 이름으로 필터링하므로 정규식은 구독의 실제 이름 규칙을 고려해야 합니다. 필터링 후 그룹이 비는 문제는 다중 구독 병합에서 매우 흔합니다. 먼저 provider가 실제로 로드한 노드 이름을 확인한 뒤 표현식을 테스트하세요. 지역의 한국어 이름만 보고 작성해서는 안 됩니다. 지원되는 표현식에서는 (?i)로 라틴 문자 대소문자를 무시할 수 있지만, 중국어 별칭, 국기 기호와 약어는 출처에 맞게 조정해야 합니다.
두 출처에 같은 이름의 노드가 있으면 화면 식별과 정책 참조가 모호해질 수 있습니다. 가장 안정적인 방법은 전처리 단계에서 노드에 출처 접두사를 붙이거나 구독 제공자가 고유한 이름을 유지하게 하는 것입니다. 클라이언트가 provider 단위 접두사를 지원한다면 병합 계층에서 ‘A-’, ‘B-’처럼 짧은 표식을 통일해 추가할 수 있습니다. 노드 배열 순서로 구분하지 마세요. 구독이 업데이트되면 순서가 바뀔 수 있습니다.
다중 구독 업데이트의 장애 격리
업데이트에 실패하면 모든 캐시를 한 번에 삭제하지 말고 provider를 하나씩 확인하세요. 먼저 요청 성공 여부를 보고, YAML 파싱 여부를 확인한 다음 필터링 후에도 노드가 남아 있는지 확인하고, 마지막으로 프록시 그룹이 올바르게 참조하는지 살펴봅니다. 특정 provider의 실패 때문에 기본 설정에서 DIRECT와 로컬 장애 그룹까지 사라져서는 안 됩니다. 전체 진입점에 여러 독립 provider가 만든 그룹을 함께 넣으면 한 출처에 문제가 생겼을 때 수동으로 전환할 수 있습니다.
병합 후에는 포트 충돌, 중복 정책 이름과 누락된 규칙 대상도 확인해야 합니다. 여러 완성형 구독을 그대로 병합하면 mixed-port, external-controller, DNS 리스닝 주소와 ‘노드 선택’이라는 그룹이 중복 정의되는 일이 많습니다. 노드 출처는 여러 개일 수 있지만 제어 포트와 핵심 정책 구조는 하나의 권위 있는 정의만 가져야 합니다. 이런 전역 필드를 로컬 계층에 고정하고 원격 입력은 노드만 담당하게 하는 방식이 유지 관리 비용이 가장 낮습니다.
| 내용 | 권장 소유 위치 | 이유 |
|---|---|---|
| 노드 매개변수 | 원격 provider | 구독과 함께 업데이트해야 함 |
| 업무별 프록시 그룹 | 로컬 오버라이드 | 이름이 로컬 규칙과 장기간 안정적으로 대응해야 함 |
| DNS 및 TUN | 로컬 오버라이드 | 기기와 현재 네트워크 환경에 따라 달라짐 |
| 대형 공개 규칙 집합 | rule provider | 독립적으로 업데이트하고 메인 설정 크기를 줄임 |
| 소수의 정확한 규칙 | 로컬 규칙 앞부분 | 우선순위를 제어하고 빠르게 수정하기 쉬움 |
클라이언트를 옮길 때 구독 링크만 내보내지 마세요. 로컬 프록시 그룹 이름, 규칙 집합 출처, 오버라이드 순서, Fake-IP 필터와 provider 경로도 기록해야 합니다. 클라이언트마다 지원하는 확장 병합 문법은 다를 수 있지만 표준 Mihomo 설정 부분은 재사용할 수 있습니다. 새 클라이언트에서 먼저 최소 설정을 만든 다음 provider, 정책, 규칙과 TUN을 계층별로 옮기면 많은 확장 필드를 한 번에 가져온 뒤 시작할 수 없는 문제를 피할 수 있습니다.
07 / CONTROLLER
외부 제어 패널과 보안 경계
제어 인터페이스로 할 수 있는 일
Mihomo의 외부 제어 인터페이스는 그래픽 클라이언트나 웹 패널이 실행 상태와 연결 및 로그를 조회하고 정책을 전환하며 설정을 다시 로드할 수 있게 합니다. 일반 프록시 포트가 아니며 권한이 훨씬 높습니다. 데스크톱 클라이언트의 내장 화면은 보통 로컬 제어 인터페이스를 통해 코어를 관리합니다. 독립 웹 패널, 원격 운영 또는 다른 도구의 연결이 필요할 때만 수신 범위를 수동으로 조정하세요.
제어 인터페이스와 패널 정적 파일은 서로 다른 요소입니다. external-controller는 API 리스닝 주소를 정의하고, external-ui는 로컬 패널 파일 디렉터리를 가리킵니다. 브라우저에서 패널을 열어도 패널은 정책과 연결을 표시하기 위해 API에 연결해야 합니다. 페이지는 열리지만 데이터가 비어 있다면 보통 패널 파일이 손상된 것이 아니라 제어 주소, 인증, CORS 허용 또는 프로토콜 불일치가 원인입니다.
로컬 사용을 위한 최소 설정
external-controller: 127.0.0.1:9090
secret: "your-password"
external-ui: ./ui
external-ui-name: metacubexd
external-controller-cors:
allow-origins:
- http://127.0.0.1
- http://localhost
allow-private-network: true
로컬에서만 관리한다면 127.0.0.1에 우선 바인딩하세요. 그러면 제어 포트가 LAN 인터페이스에 직접 노출되지 않습니다. secret은 API 인증에 사용되므로 예시 값은 강력한 무작위 문자열로 바꾸고 로컬 설정에 보관해야 합니다. 제어 패널에서 키를 입력하라고 할 때 사용하는 값이 바로 이것입니다. 변경 후에는 코어를 재시작하거나 다시 로드하고 패널 연결 설정도 함께 업데이트하세요.
external-ui는 패널 정적 리소스 디렉터리입니다. 일부 클라이언트는 패널을 직접 다운로드하고 관리하므로 사용자가 수동으로 설정할 필요가 없습니다. 직접 배포할 때는 디렉터리가 존재하고 코어 프로세스에 읽기 권한이 있는지 확인하세요. external-ui-name의 적용 여부는 현재 코어와 다운로드 방식에 따라 달라집니다. 화면에 이전 내용이 계속 표시되면 실제 디렉터리, 브라우저 캐시와 클라이언트가 이 필드를 덮어쓰는지 확인하세요.
CORS 허용은 브라우저에서 어떤 웹 출처가 제어 API를 호출할 수 있는지 결정합니다. 허용 출처는 광범위하게 열지 말고 정확한 프로토콜, 호스트와 포트 조합으로 작성해야 합니다. 패널을 로컬 정적 서버로 열면 브라우저 출처가 http://127.0.0.1:포트일 수 있습니다. 파일 프로토콜로 직접 열면 제한이 달라집니다. 먼저 브라우저 개발자 도구에서 거부된 Origin을 확인한 뒤 정확한 항목을 추가하세요.
LAN 관리 시 추가 제약
external-controller: 0.0.0.0:9090
secret: "your-password"
external-controller-cors:
allow-origins:
- http://192.168.1.20:8080
allow-private-network: true
0.0.0.0에 바인딩하면 제어 인터페이스가 사용 가능한 모든 네트워크 인터페이스에 연결됩니다. LAN 관리가 실제로 필요할 때만 이렇게 설정하세요. 시스템 방화벽에서도 출처를 신뢰할 수 있는 네트워크 대역이나 지정된 관리 기기로 제한해야 하며, 패널의 로그인 입력란에만 의존해서는 안 됩니다. 패널에 입력한 키는 API 호출에 사용되지만 네트워크 계층에서 신뢰할 수 없는 기기가 포트에 접근하는 것부터 차단해야 합니다.
기기가 공용 Wi-Fi에 연결될 수 있다면 모든 인터페이스에서 장기간 수신하는 위험이 더 커집니다. 운영체제 방화벽으로 개인 네트워크와 공용 네트워크를 구분하거나, 필요하지 않을 때 루프백 수신으로 되돌리세요. 인터넷을 통한 원격 관리를 위해 제어 포트를 직접 노출해서는 안 됩니다. 먼저 통제된 사설 네트워크 터널을 만든 뒤 LAN 서비스에 접근하듯 연결하고 API 인증도 계속 유지하는 방식이 더 적합합니다.
프록시 포트의 allow-lan과 제어 인터페이스의 수신 범위는 같은 의미가 아닙니다. LAN 기기가 mixed-port를 사용하도록 허용해도 제어 API까지 LAN에 공개할 필요는 없습니다. 반대로 제어 인터페이스가 모든 주소에서 수신한다고 해서 프록시 포트를 자동으로 사용할 수 있게 되는 것도 아닙니다. 프록시 수신, 제어 수신, 시스템 방화벽과 인증을 각각 확인해 한 항목을 고치려다 다른 항목의 접근 범위까지 넓히지 않도록 하세요.
패널 연결 시 문제 해결 순서
첫 번째로 코어가 예상한 주소와 포트에서 수신 중인지 확인하세요. 포트를 다른 프로그램이 사용 중이면 코어 로그에 보통 bind 실패가 나타납니다. 클라이언트가 자체 제어 포트로 자동 변경할 수도 있으므로 최종 값은 실행 설정과 로그를 기준으로 판단해야 합니다. 두 번째로 같은 기기에서 API에 접근할 수 있는지 테스트한 뒤 패널을 테스트하세요. API 자체에 접근할 수 없다면 브라우저 캐시를 반복해서 지우기보다 수신 설정과 방화벽을 먼저 처리해야 합니다.
세 번째로 인증을 확인하세요. ‘인증되지 않음’ 응답은 보통 키가 비어 있거나 잘못 입력되었거나 패널이 요구된 방식으로 전송하지 않았다는 뜻입니다. 복사할 때 추가 공백이 들어가지 않았는지 확인하고 YAML 바깥쪽의 따옴표를 키 내용으로 착각하지 마세요. 네 번째로 CORS를 확인합니다. 브라우저 콘솔에 교차 출처 거부가 나타나도 API는 실제로 응답했지만 브라우저가 패널의 읽기를 차단했을 수 있습니다. 이때는 모든 출처 제한을 끄지 말고 패널의 정확한 출처를 추가하세요.
다섯 번째로 프로토콜과 주소를 확인하세요. HTTPS 페이지가 HTTP 제어 인터페이스를 호출하면 브라우저가 혼합 콘텐츠 정책에 따라 요청을 차단할 수 있습니다. 패널에 localhost를 입력하면 Mihomo가 실행 중인 기기가 아니라 브라우저를 실행한 기기를 가리킵니다. 패널을 휴대폰에서 열었다면 localhost는 컴퓨터가 아닌 휴대폰입니다. LAN으로 접근하려면 컴퓨터의 해당 LAN 주소를 입력하고 방화벽이 휴대폰의 연결을 허용하는지 확인하세요.
| 현상 | 가능한 계층 | 확인할 작업 |
|---|---|---|
| 패널 페이지를 열 수 없음 | 정적 파일 또는 웹 서비스 | external-ui 디렉터리와 접근 주소 확인 |
| 페이지는 열리지만 데이터가 없음 | API 주소, 인증 또는 CORS | 브라우저 네트워크 요청과 콘솔 확인 |
| 로컬에서는 되지만 휴대폰에서는 안 됨 | 리스닝 주소 또는 방화벽 | 127.0.0.1에만 바인딩되어 있는지 확인 |
| 인증되지 않음 응답 | secret 불일치 | 키를 다시 입력하고 공백 확인 |
| 정책 전환 후 즉시 복구됨 | 설정 재로드 또는 클라이언트 관리 | 클라이언트가 구독 설정을 다시 적용했는지 확인 |
로그, 연결 정보와 최소 노출
제어 패널에는 접속 도메인, 대상 주소, 프로세스 정보와 정책 선택이 표시될 수 있으며 이는 모두 기기의 네트워크 사용 정보입니다. 문제 해결 스크린샷을 공유하기 전에 구독 이름, 노드 주소, 제어 키, 사설 주소와 문제와 관련 없는 접속 기록을 가리세요. 로그 수준은 문제 해결에 필요한 범위로 유지하면 됩니다. 지나치게 상세한 로그를 장기간 켜 두면 저장 공간과 정보 노출이 늘어납니다.
타사 패널을 사용하기 전에 패널은 API 클라이언트일 뿐 코어 설정을 대신하지 않는다는 점을 이해해야 합니다. 정책 전환은 보통 실행 상태에 대한 작업이며 설정을 다시 로드한 뒤 유지되는지는 클라이언트와 그룹 유형에 따라 달라집니다. 정책을 장기간 고정하려면 로컬 설정이나 클라이언트의 영구 저장 기능에서 지정하세요. 웹 패널에서 한 번 클릭하는 것만으로는 부족합니다. 구독 업데이트가 프록시 그룹을 다시 생성하면 이름이 바뀌어 이전 선택을 복원하지 못할 수도 있습니다.
설정을 완료한 뒤 한 번의 전체 검증을 수행하세요. 클라이언트를 재시작하고 제어 포트가 정상적으로 수신되는지 확인합니다. 허용된 기기에서 패널을 열고 키로 연결한 뒤 select 그룹을 전환해 새 연결을 관찰합니다. 설정을 다시 로드한 후 상태가 예상과 맞는지 확인하고, 마지막으로 신뢰할 수 없는 네트워크 인터페이스에서 포트에 접근할 수 없는지 테스트하세요. 이렇게 하면 ‘패널이 열리는지’뿐 아니라 인증, 접근 경계와 설정 영구성까지 검증할 수 있습니다.
제어 인터페이스를 수정한 뒤 클라이언트가 시작되지 않으면 먼저 루프백 주소로 되돌리고 외부 UI 확장 필드를 일시적으로 제거해 핵심 설정이 로드되도록 하세요. 그런 다음 항목을 하나씩 다시 추가합니다. 흔한 원인은 포트 점유, YAML 들여쓰기 오류, 클라이언트가 같은 필드를 이미 관리하는 경우 또는 잘못된 패널 디렉터리입니다. 그 밖의 시작 및 설정 로드 문제는 도움말 센터에서 오류 로그로 검색하세요. 재설치가 필요하다면 다운로드 페이지에서 현재 플랫폼의 클라이언트를 선택하세요.