在 Docker 中运行 Clash 或 Mihomo,并不等于容器里的流量会自动经过代理。容器拥有独立的网络命名空间,应用看到的默认网关、DNS 服务器和路由表,通常都与宿主机不同。要实现真正可用的透明代理,必须同时处理容器网络、TUN 或 iptables 转发、DNS 解析、规则匹配以及权限隔离。本文从流量路径出发,搭建一套适合开发环境的 Clash Docker 透明代理方案,并重点说明 Git、npm、Docker Registry、语言包下载和 SSH 等工具如何分流,最后给出连接超时、DNS 泄漏与容器无法联网的排障方法。

先理解 Docker 透明代理的流量路径

普通的 Docker 网络大致是:容器通过虚拟网卡连接到 Docker bridge,数据包经宿主机上的网桥和 NAT 转换后访问外网。容器默认使用 Docker 内置 DNS(通常是 127.0.0.11),而代理程序如果只监听宿主机的 127.0.0.1,容器并不能直接访问它。

透明代理的目标,是让应用继续使用原来的连接方式,不需要为每个工具单独设置 HTTP_PROXY 或 HTTPS_PROXY。Clash 在网络层截获 TCP、UDP 或 DNS 请求,再依据域名、目标 IP 和进程无法直接获得的其他信息进行转发。常见实现方式有两种:

  • TUN 模式:由 Mihomo 创建虚拟网卡,接管经过宿主机路由表的 IP 流量,配置相对集中,适合现代 Linux 和开发机。
  • iptables 重定向:通过 NAT 规则把 TCP 流量转发到 Clash 的 redir-port 或 tproxy-port,兼容性较好,但需要手动处理排除地址、UID 和 DNS。
  • 显式代理:应用设置代理环境变量后连接 Clash 的 mixed-port,最容易验证,但不是真正的透明代理,某些不支持代理的工具仍会绕过它。

如果只希望 Docker 容器访问代理,而不想改变宿主机全部流量,建议先采用共享宿主机网络或网关转发的方案验证链路,再逐步启用 TUN。若使用 network_mode: host,容器会直接使用宿主机网络命名空间,配置简单,但端口隔离能力下降;如果保留 bridge 网络,则需要让容器能够访问宿主机上的 Clash 监听地址。

不要直接把 7890 绑定到公网:Docker 端口发布会把服务暴露到宿主机所有接口。代理端口必须配合防火墙、监听地址和认证策略使用,尤其不要让未鉴权的 mixed-port 成为开放代理。

准备 Docker Compose 与运行权限

下面示例使用 Mihomo 镜像。不同镜像的配置目录和启动参数可能略有区别,实际部署前请核对镜像文档;核心思路并不依赖某一个镜像名称。TUN 和 iptables 需要访问网络设备、修改路由表,因此容器通常需要 NET_ADMIN 能力以及访问 /dev/net/tun。

docker-compose.yml
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/Shanghai

宿主机需要先确认 TUN 设备存在:

检查 TUN 设备与容器日志
ls -l /dev/net/tun
docker compose up -d
docker logs -f mihomo

如果系统没有 /dev/net/tun,可以尝试加载内核模块:

加载 tun 模块
sudo modprobe tun
lsmod | grep tun

生产环境不建议无条件使用 privileged: true。它会授予容器几乎全部主机权限,排障时虽然方便,却扩大了攻击面。优先使用 NET_ADMIN、NET_RAW 和 TUN 设备映射;只有在明确知道镜像需要额外能力时,才逐项增加权限。

配置 TUN、DNS 与自动路由

TUN 模式的关键是让 Mihomo 接管宿主机发出的 IP 流量,同时避免自己的出站连接再次被自己捕获。auto-route 负责注入路由,auto-detect-interface 用于自动识别真实网卡,strict-route 则让路由行为更严格。Docker 环境中如果发现宿主机无法联网,通常要先关闭严格路由或检查默认网卡名称。

Mihomo TUN 与 DNS 基础配置
mixed-port: 7890
redir-port: 7892
tproxy-port: 7893
allow-lan: true
bind-address: "*"
mode: rule
log-level: info

tun:
  enable: true
  stack: system
  auto-route: true
  auto-detect-interface: true
  strict-route: false
  dns-hijack:
    - any:53
    - tcp://any:53

dns:
  enable: true
  listen: 0.0.0.0:1053
  ipv6: false
  enhanced-mode: fake-ip
  fake-ip-range: 198.18.0.1/16
  nameserver:
    - https://223.5.5.5/dns-query
    - https://doh.pub/dns-query
  proxy-server-nameserver:
    - tls://1.1.1.1:853
  fake-ip-filter:
    - "*.lan"
    - "*.local"
    - localhost
    - "*.docker.internal"

dns-hijack 的作用是拦截应用直接发出的 53 端口请求,避免程序绕过 Clash 使用宿主机或运营商 DNS。fake-ip 能让域名规则在连接建立前生效,但局域网域名、服务发现和部分硬编码 IP 的程序可能不兼容,因此要通过 fake-ip-filter 排除。Docker 内部服务之间若依赖服务名解析,还应保留 *.local、内部域名或自定义后缀,不能把所有 DNS 请求一律交给外部解析器。

如果使用 bridge 网络而不是 network_mode: host,容器的 DNS 配置还需要单独处理。可以让应用容器把 DNS 指向宿主机在 Docker 网桥上的地址,或者在 Compose 中设置 dns;但必须确认该地址确实能访问 Clash 的 DNS 监听端口。仅仅把 listen 写成 0.0.0.0:1053,并不代表容器一定能访问,主机防火墙和 Docker 网络策略仍然可能拦截请求。

验证 DNS 是否被接管:先在容器内执行 cat /etc/resolv.conf,再使用 nslookup github.com 或 dig github.com。同时观察 Mihomo 日志中是否出现 DNS 查询。如果容器能解析但日志没有请求,说明 DNS 仍然绕过了代理链路。

为开发工具设计分流规则

开发环境常见的错误,是把所有流量都塞进同一个代理组。这样虽然容易配置,但会导致国内镜像访问变慢、公司内网无法连接,甚至让 SSH、数据库和 Kubernetes API 走到不适合的出口。更稳妥的方式是按用途拆分策略组,再按域名后缀和网络范围匹配。

开发工具策略组与规则示例
proxy-groups:
  - name: 开发代理
    type: select
    proxies:
      - 自动选择
      - DIRECT

  - name: 自动选择
    type: url-test
    proxies:
      - 节点-日本
      - 节点-新加坡
    url: https://www.gstatic.com/generate_204
    interval: 300
    tolerance: 80

  - name: 国内直连
    type: select
    proxies:
      - DIRECT
      - 开发代理

rules:
  - DOMAIN-SUFFIX,github.com,开发代理
  - DOMAIN-SUFFIX,githubusercontent.com,开发代理
  - DOMAIN-SUFFIX,githubassets.com,开发代理
  - DOMAIN-SUFFIX,docker.io,开发代理
  - DOMAIN-SUFFIX,docker.com,开发代理
  - DOMAIN-SUFFIX,npmjs.org,开发代理
  - DOMAIN-SUFFIX,pypi.org,开发代理
  - DOMAIN-SUFFIX,pythonhosted.org,开发代理
  - DOMAIN-SUFFIX,registry-1.docker.io,开发代理
  - DOMAIN-SUFFIX,googleapis.com,开发代理
  - DOMAIN-SUFFIX,openai.com,开发代理
  - DOMAIN-SUFFIX,company.internal,DIRECT
  - DOMAIN-SUFFIX,lan,DIRECT
  - IP-CIDR,10.0.0.0/8,DIRECT,no-resolve
  - IP-CIDR,172.16.0.0/12,DIRECT,no-resolve
  - IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
  - GEOIP,CN,国内直连
  - MATCH,开发代理

规则顺序非常重要。Clash 按从上到下的顺序匹配,内网域名和私有地址必须放在通用规则之前,否则可能被后面的代理规则覆盖。对于 Docker Registry,还要注意实际连接可能使用多个域名,例如登录、令牌交换和镜像层下载并不一定全部发生在 docker.io。出现拉取镜像卡住时,应在 Dashboard 的连接页面查看真实目标域名,再补充精确规则。

SSH 通常不遵循 HTTP 代理环境变量。如果希望 Git over SSH 经过代理,需要单独配置 OpenSSH 的代理命令;如果只是访问公司 Git 服务器,则更建议将内网地址明确设为直连。npm、pip、Go Module 和 Docker CLI 则可以按项目需要选择显式代理或依赖 TUN 接管,避免同时设置多套代理造成循环转发。

为命令行工具设置临时代理
export HTTP_PROXY=http://127.0.0.1:7890
export HTTPS_PROXY=http://127.0.0.1:7890
export ALL_PROXY=socks5://127.0.0.1:7890

npm config set proxy http://127.0.0.1:7890
npm config set https-proxy http://127.0.0.1:7890

git config --global http.proxy http://127.0.0.1:7890
git config --global https.proxy http://127.0.0.1:7890

显式代理只适合确认链路或处理不支持 TUN 的特殊程序。长期使用时,要记得区分宿主机地址与容器地址:容器内的 127.0.0.1 指向容器自己,不是宿主机。如果服务采用 bridge 网络,通常需要使用宿主机网关地址,例如 Docker 网桥的网关 IP,或者在支持的系统上使用 host.docker.internal。

用 Rule Provider 维护开发域名

开发工具的域名会持续变化,直接把所有规则写进主配置文件很快就会难以维护。可以把代理域名、直连域名和企业内网域名拆成独立的 Rule Provider,并设置合理的更新周期。对于内部规则,使用本地文件更安全;对于公开规则,使用 HTTP Provider 时应确认来源可信,并保留本地缓存。

开发域名 Rule Provider
rule-providers:
  developer-proxy:
    type: http
    behavior: domain
    url: https://example.com/rules/developer-proxy.txt
    path: ./rules/developer-proxy.txt
    interval: 86400

  company-direct:
    type: file
    behavior: classical
    path: ./rules/company-direct.yaml

rules:
  - RULE-SET,company-direct,DIRECT
  - RULE-SET,developer-proxy,开发代理
  - MATCH,开发代理

behavior: domain 适合每行一个域名的列表,索引效率较高;behavior: classical 适合混合使用 DOMAIN、DOMAIN-SUFFIX、IP-CIDR 等规则。修改 Provider 后,可以通过 Dashboard 手动更新,也可以重启容器验证加载结果。若规则集下载失败,Clash 往往会继续使用旧缓存,因此排障时要同时检查 URL 可达性、文件权限和 YAML 缩进。

常见故障的定位顺序

透明代理问题不应一上来就修改大量规则。建议按照“容器网络—DNS—端口—路由—规则”的顺序逐层验证,每一步只确认一个事实。

  1. 确认容器是否有基础网络:在容器内执行 ip route、ping 网关,使用 curl -I https://example.com 测试。不通时先检查 Docker bridge、防火墙和默认路由,不要先改 Clash 规则。
  2. 确认代理端口可达:从容器内测试宿主机的 7890 或 DNS 端口。连接被拒绝通常是监听地址、端口映射或防火墙问题;连接超时则更像是路由或网络策略问题。
  3. 确认 DNS 路径:检查 /etc/resolv.conf,使用 dig 查询,并观察 Mihomo 日志。解析成功但打开网站失败,可能是 DNS 结果与代理出口不一致;解析失败则先处理 DNS,不要调整策略组。
  4. 确认 TUN 设备和权限:查看容器是否能访问 /dev/net/tun,检查日志中是否出现 permission denied、route add failed 或 device not found。
  5. 确认规则命中:打开 Dashboard 的 Connections,检查目标域名、命中规则和实际策略组。若请求走了 DIRECT,说明规则未匹配;若走了代理但仍超时,再检查节点、SNI 和远端 DNS。
现象常见原因优先检查
容器完全无法联网默认路由、Docker 防火墙或网络模式错误ip route、网关连通性、Docker 日志
域名能解析但连接超时规则走错策略、节点不可用或出口 DNS 异常Connections、策略组、节点测速
出现 DNS 泄漏应用绕过 DNS 劫持,或 fake-ip 未启用resolv.conf、dns-hijack、fake-ip-filter
Docker 拉取镜像失败Registry 使用了未覆盖的令牌或 CDN 域名实时连接中的真实域名与规则
内网服务打不开私有网段被代理,或内部 DNS 被外部解析RFC1918 规则、公司域名和 DNS 排除项

修改配置后,先使用 Mihomo 的配置检查功能或查看启动日志,确认 YAML 没有缩进错误,再重载配置。排障期间将 log-level 临时设为 debug 可以获得更多信息,但不要长期保持,因为日志可能包含域名、连接信息和错误细节。确认问题后恢复为 info,并清理不必要的端口暴露。

上线前的安全与稳定性清单

一套可用的透明代理不仅要能访问外网,还要在重启、节点切换和网络变化后保持可预测。上线前建议完成以下检查:

  • Clash 的控制接口只绑定到可信地址,并设置足够复杂的 secret。
  • 代理端口不直接暴露到公网,Docker 发布端口时明确指定监听地址。
  • 为 10.0.0.0/8、172.16.0.0/12、192.168.0.0/16 等内部地址设置直连规则,并根据实际网络补充公司网段。
  • 为 Docker、公司内网和局域网域名配置 DNS 排除项,避免 fake-ip 破坏服务发现。
  • 开发代理、国内直连和内网直连使用不同策略组,避免一个节点故障影响全部业务。
  • Rule Provider 使用固定来源和本地缓存,更新失败时仍能使用上一次有效规则。
  • 不要在配置文件中提交真实订阅链接、UUID、密码和 API 密钥,使用权限受控的挂载文件或环境变量。

当 TUN、DNS 和规则分流三层都验证完成后,Docker 中的 Git、npm、pip、镜像拉取和各种开发依赖就能以统一方式工作。遇到问题时,先判断流量是否进入 Clash,再判断 DNS 是否正确,最后才分析节点和规则;按照这个顺序排查,通常比反复更换代理节点更快找到根因。

立即开始

用 Clash 掌控你的流量

支持 Windows、macOS、Linux、Android 与 iOS,灵活规则,开箱即用。

免费下载 查看设置指南 →
下载 Clash 全平台支持,一键流量控制,无需复杂配置
免费下载