왜 “그냥 켜두면” 위험한가

Clash Metamihomo 계열 코어에는 트래픽을 다루는 프록시 포트와 별개로, 설정을 읽고 바꾸는 REST APIexternal-controller가 있습니다. GUI 클라이언트의 웹 대시보드·원격 패널·스크립트 자동화가 이 경로를 통해 구독 갱신, 규칙 전환, 모드 변경까지 수행합니다. 문제는 이 인터페이스가 미인증 접근 상태로 넓은 주소에 열려 있으면, 같은 망의 다른 기기나(설정에 따라) 인터넷 쪽 스캐너가 설정을 읽거나 악의적으로 바꿀 수 있다는 점입니다. 프록시 사용자 입장에서는 “연결만 되면 된다”고 생각하기 쉬운데, 관리 API는 프록시 포트와 다른 공격면입니다. 본문에서는 주소를 먼저 묶고(bind-address) → Secret으로 잠그고 → 마지막에만 노출 범위를 넓히는 순서로 정리합니다. 코어 전반 이해는 Clash Meta 업그레이드 가이드와 맞춰 읽으면 설정 키 이름이 한결 익숙해집니다.

기본 스케치: 로컬 전용으로 시작하기

가장 보수적인 출발점은 본인 PC 안에서만 대시보드와 API를 쓰는 것입니다. 이때 external-controller는 보통 127.0.0.1:포트 형태로 두고, 운영체제 방화벽으로도 불필요한 인바운드를 막아 둡니다. 아래는 예시 스케치이며, 실제 키 이름·들여쓰기는 사용 중인 클라이언트가 생성한 YAML과 동일하게 맞춰야 합니다.

# Sketch — local-only controller; set a strong secret before any LAN exposure
external-controller: 127.0.0.1:9090
secret: "replace-with-long-random-string"

secret을 비워 두거나 짧은 단어로 두면, 브라우저나 스크립트가 Authorization 헤더 없이 요청을 내도 통과하는 구성이 될 수 있습니다. 대시보드 URL을 북마크만 해 두고 “나만 쓴다”고 느껴도, 망 안의 다른 프로세스·악성 확장·같은 Wi-Fi의 단말이 같은 주소로 접근할 수 있으니 항상 길고 예측하기 어려운 값을 쓰는 편이 낫습니다. 비밀번호 관리 도구로 생성한 난수 문자열을 그대로 붙여 넣는 방식이 실무에서 가장 단순합니다.

1단계: bind-address로 “누가 들을 수 있는지” 정하기

일부 구성에서는 external-controller와 별도로 바인딩 주소bind-address 키로 지정합니다. 의미는 단순합니다. 127.0.0.1이면 루프백에만 귀를 기울이고, 0.0.0.0이나 *에 가깝게 넓히면 해당 머신의 모든 인터페이스에서 같은 포트가 열립니다. 스마트폰에서 PC의 웹 패널을 보고 싶다고 해서 곧바로 *로 열어두면, 동시에 LAN 전체에 관리 포트가 광고되는 셈이 됩니다. 랜에서 프록시만 나눠 쓰는 주제는 랜 프록시 공유 가이드에서 다루지만, 관리 포트는 프록시 포트와 분리해서 생각해야 합니다. allow-lan을 켠 뒤 mixed-port를 열었다고 해서, 컨트롤러까지 같은 위험도로 열려 있어야 하는 것은 아닙니다.

실무에서는 (1) 우선 127.0.0.1만으로 대시보드가 필요한지 확인하고, (2) 정말 다른 기기의 브라우저에서 열어야 한다면 그때 특정 LAN IP에만 바인딩하거나, (3) 리버스 프록시·SSH 터널·VPN 같은 별도 제어 채널 뒤에 두는 방식을 고려하는 것이 좋습니다. 서버형으로 리눅스에만 올린다면 Linux·systemd 설치 글의 흐름과 함께, systemd 유닛과 방화벽으로 “어떤 인터페이스에서 9090이 열리는지”를 명시적으로 제한하는 편이 안전합니다.

체크 포인트: 설정을 고친 뒤 OS 도구로 해당 포트가 127.0.0.1에만 LISTEN인지, 아니면 0.0.0.0으로 열렸는지 한 번 확인하면 “의도와 다른 노출”을 빨리 잡을 수 있습니다.

2단계: Secret과 REST 호출 습관

secret이 설정되면, 클라이언트·대시보드·curl 등은 보통 Authorization: Bearer <secret> 형태로 토큰을 붙여 보냅니다. 브라우저에서 주소만 치고 들어가도 되는 UX를 유지하려는 템플릿이 있다면, 그게 토큰을 쿼리 문자열에 실어 보내는지 여부를 확인하세요. 쿼리에 비밀값이 남으면 서버 로그·프록시 로그·북마크 공유에 새어 나갈 수 있습니다. 가능하면 헤더 기반 인증을 쓰는 클라이언트와 문서를 따르는 편이 낫습니다.

Secret을 바꾼 직후에는 GUI가 이전 값을 캐시해 401을 내는 경우가 있으니, 클라이언트를 한 번 재시작하거나 설정 동기화 메뉴를 확인합니다. 팀이나 가족과 설정을 공유할 때 “편의상 짧은 비밀번호”를 쓰기 쉬운데, 이 경우가 바로 스캐너 사전 공격에 걸리기 쉬운 지점입니다. 최소 길이와 기호 혼합을 스스로 규칙으로 정해 두면 좋습니다.

3단계: “필요할 때만” LAN·원격으로 넓히기

모바일 브라우저로 집 PC의 패널을 열고 싶다면, 우선순위는 다음과 같이 잡는 것을 권합니다. (A) PC 쪽 방화벽에서 관리 포트 단일 TCP만 신뢰 서브넷에 허용할지, (B) 공유기에서 포트 포워딩을 열어 인터넷에 노출할지는 별도의 위험 계층입니다. (B)는 스캔 트래픽이 곧바로 닿으므로, Secret만으로는 부족하다고 보는 편이 안전합니다. 가능하면 VPN으로 집망에 먼저 들어온 뒤 로컬 주소로 붙거나, SSH 포워딩으로 127.0.0.1에 터널링하는 쪽이 공격면을 훨씬 줄입니다.

공용 Wi-Fi·기숙사·카페에서는 관리 API를 LAN 폭으로 열어두지 마세요. 같은 세그먼트의 타인이 이론상 패널과 REST API에 접근할 수 있습니다.

동작 확인과 흔한 실수

설정 반영 후에는 같은 기기에서 127.0.0.1:포트로 헬스나 버전 API를 호출해 보고, Secret을 빼면 401·403 계열로 막히는지 확인합니다. 반대로 Secret을 넣었는데도 열려 있다면, 다른 프로필이나 GUI가 덮어쓴 런타임 설정을 의심합니다. 클라이언트마다 “런타임 설정”과 “디스크에 저장된 YAML”이 분리되어 있는 경우가 많아, 편집기로 고친 파일이 실제로 코어에 주입되지 않는 일이 있습니다. 로그에 찍힌 external-controller 줄을 기준으로 최종 상태를 보는 습관이 좋습니다.

또 하나의 실수는 프록시 포트와 컨트롤러 포트를 혼동하는 것입니다. mixed-port는 트래픽 중계용이고, 컨트롤러는 설정 관리용입니다. 둘 다 방화벽에서 다루되, “누구에게 열 것인가” 기준은 다르게 잡아야 합니다. TUN·DNS까지 한꺼번에 손대는 환경이라면 TUN 모드 가이드와 함께, 트래픽 경로와 관리 경로를 머릿속에서 분리해 두면 이후 트러블슈팅이 수월합니다.

한눈에 보는 체크리스트

  • external-controller의도한 주소·포트에만 리스닝하는지(OS 도구로 확인)
  • bind-address 또는 동등 옵션으로 루프백 vs LAN vs 전체 인터페이스가 명확한지
  • secret이 충분히 길고, REST API 호출 시 헤더·클라이언트 설정이 일치하는지
  • Windows·macOS·Linux 방화벽에서 관리 포트 인바운드가 최소 권한인지
  • 공유기 포트 포워딩·클라우드 VM 공인 IP처럼 인터넷에 직접 노출하지 않았는지(가능하면 VPN·터널 우선)

정리: “열기 전에 묶고, 열 때는 최소만”

Clash Metaexternal-controller는 편의성이 큰 만큼, 잘못 열리면 미인증 접근으로 이어질 수 있는 관리 채널입니다. bind-address로 듣는 범위를 먼저 정하고, Secret으로 인증을 강제한 뒤, 정말 필요한 시나리오에서만 LAN이나 그 바깥으로 범위를 넓기는 순서가 사고를 줄이는 지름길입니다. 같은 계열 도구들과 비교해 보면 Clash·mihomo 쪽은 YAML과 GUI를 병행하기 쉬워, 한 번 안전한 패턴을 잡아 두면 이후에도 재현하기 좋습니다.

→ Clash 공식 클라이언트 무료 다운로드 — 최신 빌드에서 대시보드·API 옵션을 직접 맞춰 보면, 문서만 읽을 때보다 “어디에 포트가 열리는지” 감각이 훨씬 빨리 잡힙니다.