Docker 환경에서 Clash를 사용하면 단순히 컨테이너의 HTTP 프록시 변수를 지정하는 것보다 훨씬 넓은 범위의 트래픽을 제어할 수 있습니다. 투명 프록시를 구성하면 애플리케이션마다 프록시 주소를 따로 설정하지 않아도 컨테이너의 TCP 요청을 자동으로 Clash로 전달할 수 있습니다. 특히 GitHub, Docker Hub, 패키지 저장소, AI 도구처럼 연결 대상이 자주 바뀌거나 여러 프로세스가 동시에 통신하는 환경에서 유용합니다.
다만 Docker의 네트워크 네임스페이스, Linux 커널의 TUN 장치, iptables 또는 nftables 규칙이 함께 동작해야 하므로 일반적인 데스크톱 Clash 설정보다 고려할 항목이 많습니다. 이 글에서는 Mihomo를 기준으로 Docker에서 TUN 모드와 iptables 리다이렉션을 조합하는 방법, DNS 누출을 줄이는 방법, 컨테이너별 적용 범위와 장애를 진단하는 순서를 단계별로 설명합니다.
Docker 투명 프록시의 기본 구조
Docker 투명 프록시는 크게 네 가지 계층으로 나누어 생각하면 이해하기 쉽습니다. 첫 번째는 애플리케이션 컨테이너입니다. 이 컨테이너는 일반적인 TCP 연결을 만들지만 프록시 서버의 주소를 알 필요가 없습니다. 두 번째는 Docker 브리지 네트워크입니다. 컨테이너의 패킷이 이 네트워크를 통해 호스트 또는 Clash 컨테이너로 이동합니다. 세 번째는 트래픽을 가로채는 iptables 규칙입니다. 마지막은 TUN 또는 redir 포트에서 패킷을 받아 Clash 규칙 엔진으로 전달하는 계층입니다.
- HTTP/SOCKS 프록시 변수: 설정이 가장 쉽지만 해당 변수를 지원하는 프로그램에만 적용됩니다.
- redir-host 또는 redir 포트: TCP 연결을 iptables로 가로채기 좋으며 일반적인 투명 프록시 구성에 적합합니다.
- TUN 모드: TCP와 UDP를 하나의 가상 네트워크 인터페이스로 처리할 수 있고 도메인 기반 라우팅과 결합하기 좋습니다.
- iptables/nftables: 어떤 패킷을 Clash로 보낼지 결정하는 커널 수준의 전달 규칙입니다.
Docker 컨테이너 안에서 Clash를 실행할 때는 TUN 장치 접근 권한이 핵심입니다. 컨테이너가 호스트의 네트워크와 완전히 분리되어 있으면 컨테이너 내부의 TUN은 해당 네임스페이스 안에서만 보일 수 있습니다. 반대로 network_mode: host를 사용하면 호스트 네트워크를 직접 공유하므로 라우팅은 단순해지지만 포트 충돌과 보안 범위가 커집니다. 운영 환경에서는 먼저 호스트 네트워크 방식으로 검증한 뒤, 필요한 경우 전용 Docker 네트워크와 명시적인 규칙으로 범위를 좁히는 편이 안전합니다.
Mihomo 컨테이너와 TUN 설정 준비
먼저 설정 파일과 규칙 파일을 보관할 디렉터리를 만듭니다. config.yaml은 컨테이너 내부의 고정 경로에 연결하고, 로그와 캐시를 별도 볼륨으로 분리하면 설정을 교체하거나 컨테이너를 재생성할 때 편리합니다. 외부 컨트롤러는 처음부터 모든 인터페이스에 공개하지 말고 로컬 주소에 바인딩하는 것이 좋습니다.
services:
mihomo:
image: metacubex/mihomo:latest
container_name: mihomo
restart: unless-stopped
network_mode: host
cap_add:
- NET_ADMIN
- NET_RAW
devices:
- /dev/net/tun:/dev/net/tun
volumes:
- ./config:/root/.config/mihomo
environment:
- TZ=Asia/Seoul
NET_ADMIN은 TUN 인터페이스와 라우팅, 방화벽 규칙을 관리하는 데 사용됩니다. NET_RAW는 일부 네트워크 진단과 ICMP 처리에 필요할 수 있습니다. /dev/net/tun이 호스트에 없다면 다음 명령으로 TUN 모듈을 활성화할 수 있습니다.
sudo modprobe tun ls -l /dev/net/tun docker compose up -d docker logs -f mihomo
Mihomo 설정에서는 TUN을 활성화하고 자동 경로 설정 여부를 환경에 맞게 결정합니다. auto-route를 켜면 Mihomo가 시스템 라우팅 테이블에 경로를 추가할 수 있지만, Docker 호스트에서 이미 별도의 VPN이나 방화벽을 사용하고 있다면 경로 충돌이 발생할 수 있습니다. 처음에는 자동 경로를 켜고 동작을 확인하되, 기존 라우팅 정책이 복잡한 서버에서는 수동 라우팅을 고려하세요.
mixed-port: 7890
redir-port: 7892
tproxy-port: 7893
allow-lan: false
mode: rule
log-level: info
tun:
enable: true
stack: mixed
device: mihomo
auto-route: true
auto-detect-interface: true
strict-route: true
dns-hijack:
- any:53
- tcp://any:53
dns:
enable: true
listen: 0.0.0.0:1053
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.1/16
nameserver:
- https://1.1.1.1/dns-query
- https://8.8.8.8/dns-query
fake-ip-filter:
- '*.lan'
- '*.local'
- 'localhost.ptlogin2.qq.com'
- 'time.*.com'
- 'ntp.*.com'
Docker 내부 DNS는 보통 Docker의 임베디드 리졸버인 127.0.0.11을 사용합니다. 따라서 컨테이너의 모든 DNS 요청을 단순히 호스트의 53번 포트로 보내면 Clash의 도메인 규칙과 충돌할 수 있습니다. TUN의 dns-hijack과 fake-ip를 함께 사용할 때는 DNS 요청이 실제로 Mihomo의 DNS 리스너에 도달하는지 확인해야 합니다. 로컬 네트워크 검색, 시간 동기화, 게임의 STUN과 같이 실제 IP가 필요한 도메인은 fake-ip-filter에 추가하세요.
iptables로 컨테이너 TCP 트래픽 가로채기
TUN의 자동 라우팅만으로 해결되지 않는 환경에서는 iptables의 NAT 리다이렉션을 사용합니다. 핵심은 Clash 자신이 생성한 연결과 로컬 네트워크 트래픽을 다시 가로채지 않고, 대상 컨테이너의 TCP 연결만 redir-port로 보내는 것입니다. 자기 자신이 만든 패킷까지 다시 프록시로 보내면 무한 루프, 높은 CPU 사용량, 연결 지연이 발생합니다.
아래 예제는 Docker 브리지 네트워크의 컨테이너 트래픽을 대상으로 하는 개념적 규칙입니다. 실제 인터페이스 이름과 서브넷은 docker network inspect 결과에 맞게 변경해야 합니다. 배포 전에 반드시 현재 방화벽 관리 도구가 iptables 규칙을 덮어쓰지 않는지 확인하세요.
# Docker 네트워크와 Clash UID 확인 docker network inspect bridge id mihomo # 예시 값: Docker 브리지 172.17.0.0/16, Clash UID 1000 sudo iptables -t nat -N MIHOMO_REDIRECT sudo iptables -t nat -A MIHOMO_REDIRECT -d 127.0.0.0/8 -j RETURN sudo iptables -t nat -A MIHOMO_REDIRECT -d 10.0.0.0/8 -j RETURN sudo iptables -t nat -A MIHOMO_REDIRECT -d 172.16.0.0/12 -j RETURN sudo iptables -t nat -A MIHOMO_REDIRECT -d 192.168.0.0/16 -j RETURN sudo iptables -t nat -A MIHOMO_REDIRECT -m owner --uid-owner 1000 -j RETURN sudo iptables -t nat -A MIHOMO_REDIRECT -p tcp -j REDIRECT --to-ports 7892 sudo iptables -t nat -A PREROUTING -s 172.17.0.0/16 -p tcp -j MIHOMO_REDIRECT
REDIRECT는 같은 네트워크 네임스페이스 안에서 목적지 포트를 바꾸는 방식입니다. Mihomo가 network_mode: host로 실행 중이면 호스트의 리다이렉트 포트에서 패킷을 받아야 합니다. 별도 네트워크 네임스페이스에서 실행하는 경우에는 목적지 주소, 인터페이스, FORWARD 정책이 달라집니다. 이 차이를 무시하고 규칙을 복사하면 패킷은 규칙에 매칭되지만 Clash에 도달하지 않는 상황이 생깁니다.
UDP까지 투명하게 처리하려면 일반 REDIRECT만으로는 부족할 수 있습니다. UDP는 TPROXY와 정책 라우팅, 또는 Mihomo TUN의 UDP 처리 기능을 함께 사용해야 합니다. Docker Hub 로그인, GitHub API, 대부분의 패키지 다운로드는 TCP 기반이므로 먼저 TCP 투명 프록시를 안정화한 뒤 QUIC이나 DNS 같은 UDP 트래픽을 추가하는 순서가 좋습니다.
curl, DNS 조회, 패키지 다운로드를 각각 확인한 다음 소스 서브넷을 넓히는 방식이 장애 범위를 줄입니다.동작 확인과 단계별 진단 절차
설정 파일을 작성한 뒤에는 “컨테이너가 실행 중인가”와 “트래픽이 프록시를 통과하는가”를 구분해서 확인해야 합니다. 컨테이너 상태가 running이어도 TUN 장치 권한이 없거나 iptables 규칙이 잘못되면 실제 요청은 직접 연결될 수 있습니다. 다음 순서대로 검사하면 원인을 계층별로 좁힐 수 있습니다.
- 프로세스와 장치 확인:
docker logs mihomo에서 TUN 생성 실패, 포트 바인딩 실패, 설정 파싱 오류가 없는지 확인합니다. 호스트에서ip link show mihomo와ss -lntup으로 인터페이스와 리스너를 확인합니다. - DNS 확인: 테스트 컨테이너에서
cat /etc/resolv.conf를 확인하고,getent hosts github.com또는nslookup으로 응답이 반환되는지 검사합니다. DNS만 실패하면 iptables보다 DNS 리스너와 Docker resolver를 먼저 점검합니다. - TCP 연결 확인:
curl -v https://github.com을 실행하고 Mihomo 대시보드의 Connections 화면에서 해당 도메인이 보이는지 확인합니다. 연결은 성공하지만 대시보드에 표시되지 않으면 직접 연결이 우회되고 있을 가능성이 큽니다. - 규칙 매칭 확인: GitHub, Docker Hub, 패키지 저장소, AI 서비스처럼 서로 다른 도메인을 테스트합니다. 로그 레벨을 잠시
debug로 올려 어떤 규칙과 프록시 그룹이 선택되는지 확인한 뒤 운영에서는info로 되돌립니다. - 원래 규칙으로 복구: 문제 발생 시 사용자 정의 체인의 연결만 제거하고 기본 Docker 네트워크를 복구합니다. 방화벽 전체를 초기화하는 명령은 다른 서비스의 보안 정책까지 삭제할 수 있으므로 피해야 합니다.
docker run --rm --network bridge curlimages/curl:latest \ sh -c 'cat /etc/resolv.conf; echo; curl -I -L --max-time 15 https://github.com' docker exec -it mihomo sh -c 'ip link show; ss -lntup' sudo iptables -t nat -vnL MIHOMO_REDIRECT sudo tcpdump -ni any port 7892 or port 53
자주 발생하는 실패 원인
- 연결은 되지만 프록시 IP가 아니다: PREROUTING 규칙이 컨테이너 서브넷에 적용되지 않았거나, 이미 별도의 Docker 체인에서 RETURN되고 있을 수 있습니다.
- DNS는 되지만 도메인 규칙이 적용되지 않는다: 애플리케이션이 DNS 결과를 캐시하거나 IP 주소를 직접 사용하고 있을 수 있습니다. fake-ip와 DNS 하이재킹 상태를 확인하세요.
- Docker Hub만 실패한다: 인증 엔드포인트와 이미지 레이어 다운로드 도메인이 서로 다를 수 있습니다. 한 도메인만 허용하지 말고 연결 로그에서 실제 대상 목록을 확인해야 합니다.
- GitHub는 열리지만 Git 클론이 실패한다: SSH는 TCP 22번 포트를 사용하며 HTTPS와 별도 규칙이 필요합니다. 필요한 경우 SSH를 HTTPS로 전환하거나 22번 포트의 정책을 별도로 구성하세요.
- 컨테이너 재시작 후 규칙이 사라진다: iptables 명령을 수동으로 실행했다면 재부팅이나 방화벽 서비스 재시작 때 삭제될 수 있습니다. 배포 스크립트 또는 호스트의 방화벽 관리 체계에 영구 규칙으로 등록하세요.
운영 환경에서의 보안과 안정성
투명 프록시는 편리하지만 잘못 구성하면 내부 네트워크나 호스트 관리 포트까지 프록시를 통해 접근할 수 있습니다. RFC1918 사설 주소, Docker 내부 서브넷, 호스트의 SSH와 데이터베이스 포트는 기본적으로 직접 연결 또는 차단 정책을 명확히 해야 합니다. 특히 allow-lan: true를 무심코 활성화하면 다른 LAN 장치가 Clash의 포트를 이용할 수 있으므로, 필요한 경우에도 특정 인터페이스와 방화벽 소스만 허용하세요.
- 외부 컨트롤러의
secret을 설정하고 9090 포트를 인터넷에 노출하지 않습니다. - 프록시 포트와 DNS 포트는 Docker 내부 또는 호스트 로컬 주소에만 바인딩합니다.
- 컨테이너별로 프록시 적용이 필요하면 전용 네트워크 서브넷을 만들고 해당 서브넷만 PREROUTING 대상으로 지정합니다.
skip-cert-verify: true는 인증서 문제를 숨길 뿐 해결하지 않으므로 운영 설정에서 사용하지 않습니다.- 정책 변경 후에는 GitHub, Docker Hub, 패키지 저장소, AI API를 각각 테스트하고 직접 연결 여부를 로그로 확인합니다.
성능 측면에서는 모든 트래픽을 무조건 TUN으로 보내기보다 국내 미디어, 사내 서비스, 로컬 레지스트리처럼 직접 연결해도 되는 대상은 명시적으로 DIRECT 처리하는 것이 좋습니다. 반대로 프록시가 필요한 도메인은 도메인 기반 규칙을 우선 사용하고, IP-CIDR 규칙에는 가능하면 no-resolve를 신중하게 적용해야 합니다. DNS와 라우팅 정책을 분리해 관리하면 규칙이 커져도 원인을 추적하기 쉽습니다.
Docker에서 Clash 투명 프록시를 안정적으로 운영하는 핵심은 TUN 설정 하나가 아니라 네트워크 네임스페이스, DNS, 방화벽 예외, 프록시 규칙, 관찰 도구를 함께 설계하는 것입니다. 먼저 TCP와 단일 테스트 컨테이너로 범위를 제한하고, 패킷 캡처와 Mihomo 연결 로그로 실제 경로를 검증한 뒤 UDP와 전체 네트워크로 확장하세요. 이 순서를 지키면 설정 변경으로 인해 모든 컨테이너가 동시에 인터넷 연결을 잃는 상황을 크게 줄일 수 있습니다.
Clash로 트래픽을 완전히 제어하세요
Windows, macOS, Linux, Android, iOS 지원. 유연한 규칙, 간단한 설정.