04. 用 mihomo (Clash.Meta) 解决出口问题

境内 VPS 直连 OpenAI / Google / Anthropic 通常失败,且 ChatGPT 不接受香港、中国大陆出口,必须走美/日/新等支持地区。这一步在服务器上跑一个 mihomo 容器,CPA 通过它出网。

为什么是 mihomo

  • 原版 Clash 仓库已删,mihomo (前 Clash.Meta) 是最活跃的开源继任。
  • 协议覆盖广(Vmess/Vless/Trojan/Shadowsocks/Hysteria/Reality 等)。
  • 自带 RESTful 控制接口 + Web 面板,命令行/浏览器都能切节点。
  • 官方多架构镜像 metacubex/mihomo,开箱即用。

1) 拿到订阅链接

机场卖家会给一个订阅 URL,比如:

https://www.example.com/link/xxxxxxxx?clash=1

clash=1 或者 User-Agent 带 clash 时,服务端会直接返回 clash 格式的 yaml 配置,省掉转换步骤。

2) 下载配置

mkdir -p /root/mihomo && cd /root/mihomo
 
curl -L -A "clash.meta" "你的订阅URL" -o config.yaml
head -20 config.yaml

期望开头能看到类似:

port: 7890
socks-port: 7891
allow-lan: false
mode: Rule
external-controller: '0.0.0.0:9090'
dns:
  enable: true
  listen: '0.0.0.0:53'
  ...

3) 调整两处必改项

a. allow-lan: falsetrue

Docker 容器之间互访需要这个,否则 CPA 容器连不上 mihomo。

sed -i 's/^allow-lan: false/allow-lan: true/' /root/mihomo/config.yaml

b. DNS 端口 53 → 1053

0.0.0.0:53 在 host 网络模式下会和 systemd-resolved 抢端口,启动会失败。换成非常用端口就行。

sed -i "s|listen: '0.0.0.0:53'|listen: '0.0.0.0:1053'|" /root/mihomo/config.yaml

mihomo 的 DNS 监听只是把自己当成一个 DNS server,给本地用。CPA 走 HTTP 代理协议,根本不会用到这个 DNS 监听端口,所以改不改都不影响 CPA。但保留默认 53 会让 mihomo 容器起不来

验证修改

grep -E "^(port|socks-port|allow-lan|external-controller)" /root/mihomo/config.yaml
grep "listen:" /root/mihomo/config.yaml

4) 起容器

最简单方式:host 网络。

docker run -d \
  --name mihomo \
  --restart unless-stopped \
  --network host \
  -v /root/mihomo/config.yaml:/root/.config/mihomo/config.yaml \
  metacubex/mihomo:latest
 
docker logs --tail 20 mihomo

期望日志:

Start initial configuration in progress
Geodata Loader mode: memconservative
Geosite Matcher implementation: succinct
Initial configuration complete, total time: Xms

5) 验证代理能通

curl -x http://127.0.0.1:7890 -I --max-time 10 https://www.google.com
curl -x http://127.0.0.1:7890 --max-time 10 -s https://api.openai.com/cdn-cgi/trace

第二条返回里应有 loc=US,说明出口在美国。如果 loc=HK,ChatGPT 一定登录失败,立刻切节点(下一节)。

6) 通过 RESTful API 切节点

mihomo 的 external-controller 默认监听 :9090。所有切节点操作都是 PUT 到 /proxies/<组名>

列出所有 selector 组

curl -s http://127.0.0.1:9090/proxies | python3 -c "import json,sys;d=json.load(sys.stdin)['proxies'];[print(n,'->',p.get('now'),'(',len(p.get('all',[])),'options)') for n,p in d.items() if p['type']=='Selector']"

输出大概像:

🔰Proxy -> HK IEPL专线 11 | 倍率 3x ( 25 options)
🌍国外媒体 -> 🔰Proxy ( 26 options)
📺Netflix -> 🔰Proxy ( 26 options)
...

看某个组里的所有节点

curl -s "http://127.0.0.1:9090/proxies/🔰Proxy" | python3 -c "import json,sys;d=json.load(sys.stdin);[print(i,n) for i,n in enumerate(d['all'])]"

切节点

curl -X PUT http://127.0.0.1:9090/proxies/🔰Proxy \
  -H "Content-Type: application/json" \
  -d '{"name":"US Pro 11 | 倍率 2x"}'

成功无输出(HTTP 204)。验证:

curl -s http://127.0.0.1:9090/proxies/🔰Proxy | python3 -c "import json,sys;print('now:',json.load(sys.stdin)['now'])"

有些机场把 OpenAI 的流量单独路由到 🌍国外媒体🤖OpenAI 之类的组——如果切了主组还是走错节点,列出所有 selector 看一下,对应组也切到美国节点。

实战:判断应该切哪个组

先列出所有可切换组:

curl -s http://127.0.0.1:9090/proxies | python3 -c "import json,sys;d=json.load(sys.stdin)['proxies'];[print(k, '->', v.get('type'), '->', v.get('now')) for k,v in d.items() if v.get('type') in ['Selector','URLTest','Fallback']]"

本次服务器输出类似:

GLOBAL -> Selector -> DIRECT
⚓️其他流量 -> Selector -> 🔰Proxy
🌍国外媒体 -> Selector -> 🔰Proxy
🍎苹果服务 -> Selector -> 🚀直接连接
📟Twitter -> Selector -> 🔰Proxy
📲Telegram -> Selector -> 🔰Proxy
📺Netflix -> Selector -> 🔰Proxy
📺Youtube -> Selector -> 🔰Proxy
🔰Proxy -> Selector -> US Pro 11 | 倍率 2x
🚀直接连接 -> Selector -> DIRECT

这里真正要切的是:

🔰Proxy

因为其它国外流量组都指向 🔰Proxy。只要切 🔰Proxy,OpenAI、Telegram、Twitter、YouTube 等通常都会跟着走新节点。

查看 🔰Proxy 里的节点:

curl -s "http://127.0.0.1:9090/proxies/🔰Proxy" | python3 -c "import json,sys;d=json.load(sys.stdin);print('\n'.join(d.get('all',[])))"

切到某个节点:

curl -X PUT "http://127.0.0.1:9090/proxies/🔰Proxy" \
  -H "Content-Type: application/json" \
  -d '{"name":"US Pro 12 | 倍率 2x"}'

确认当前节点:

curl -s http://127.0.0.1:9090/proxies | python3 -c "import json,sys;d=json.load(sys.stdin)['proxies'];print(d['🔰Proxy']['now'])"

7) 批量测试节点是否适合 OpenAI

只看到:

HTTP/1.1 200 Connection established

不代表 OpenAI 通了。它只说明本机成功连上 mihomo,并且 mihomo 开始建立代理隧道。

如果后面出现:

curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL

说明 TLS 握手失败,节点到 OpenAI 这一段不稳定或被阻断。

测试命令:

curl -x http://127.0.0.1:7890 -I https://api.openai.com --max-time 15

可用节点常见返回:

HTTP/1.1 200 Connection established

HTTP/2 421

HTTP/2 421 对这个 HEAD / 请求来说可以视为连通,说明 TLS 已经通了。403404421200 都比 SSL_ERROR_SYSCALL 好,至少代表能完成 HTTPS 握手。

本次批量测试脚本:

for n in \
"US Pro 12 | 倍率 2x" \
"US Pro 13 | 倍率 2x" \
"US Pro 14 | 倍率 2x" \
"US Pro 11 | 倍率 2x" \
"SG IEPL专线 51 | 倍率 3x" \
"JP Pro 01 | 倍率 2x"
do
  echo "===== $n ====="
  curl -s -X PUT "http://127.0.0.1:9090/proxies/🔰Proxy" \
    -H "Content-Type: application/json" \
    -d "{\"name\":\"$n\"}"
  sleep 2
  curl -x http://127.0.0.1:7890 -I https://api.openai.com --max-time 12 2>&1 | head -8
done

本次结果:

节点OpenAI 测试结果结论
`US Pro 12倍率 2x`HTTP/2 421
`US Pro 13倍率 2x`HTTP/2 421
`JP Pro 01倍率 2x`HTTP/2 421
`US Pro 14倍率 2x`SSL_ERROR_SYSCALL
`US Pro 11倍率 2x`SSL_ERROR_SYSCALL
`SG IEPL专线 51倍率 3x`SSL_ERROR_SYSCALL

最终切到:

curl -X PUT "http://127.0.0.1:9090/proxies/🔰Proxy" \
  -H "Content-Type: application/json" \
  -d '{"name":"US Pro 12 | 倍率 2x"}'

验证:

curl -x http://127.0.0.1:7890 -I https://api.openai.com --max-time 15
curl -x http://127.0.0.1:7890 -I https://registry-1.docker.io/v2/ --max-time 15

判断标准:

测试目标可用表现
OpenAI第二段出现 HTTP/2 421403404200
Docker HubHTTP/2 401,这是未认证 Registry 的正常响应

如果 CPA / Codex / Docker 拉镜像后续又不稳定,优先在这些节点之间切:

US Pro 12 | 倍率 2x
US Pro 13 | 倍率 2x
JP Pro 01 | 倍率 2x

8) Web 面板(可选)

浏览器打开 metacubexd,host 填服务器 IP,port 填 9090,secret 留空。可视化切节点、看流量、测延迟。

⚠️ 9090 端口默认无 secret,必须用安全组或 iptables 限制只允许你自己的 IP,否则任何人都能控制你的代理。也可以通过 SSH 隧道访问:

ssh -L 9090:127.0.0.1:9090 root@服务器IP
# 然后浏览器开 http://127.0.0.1:9090(注意 metacubexd 要填这个本地地址)

9) ChatGPT/OpenAI 友好节点的判断

不是所有美国节点都能登录 ChatGPT,常见拒登原因:

  • 节点 IP 在 datacenter 段(机场普遍)→ 大概率会被风控
  • IP 被滥用(共享池)→ 会要求过 captcha 甚至直接拒登
  • 出口实际是英国/法国虽然 IP 显示美国

实战建议:

  1. 优先选纯净美国住宅 / 商业 IP节点,机场页面通常会标注。
  2. 同一节点登录失败就换一条,不要在同一节点反复试。
  3. 验证节点对 ChatGPT 友好:curl -x http://127.0.0.1:7890 https://chat.openai.com/cdn-cgi/traceloc=UScolo 是美国机房代码(如 IAD SJC LAX)。

10) 让 CPA 走这个代理

回到 03-deploy-cpa.md §5 改 proxy-url

下一步:05-oauth-login.md 完成 OAuth 登录。