每个代码块右上角都有「一键复制」

家宽出口 · 动手玩家版

适合自己有 VPS、喜欢折腾的人。这里讲每个组件的原理、完整的配置文件、怎么改、怎么排错和加固。 所有敏感信息都用占位符表示:<你的VPS_IP>、<UUID>、<私钥>、<公钥>、<shortId>、<GATE_USER>、<GATE_PASS>。


1. 架构

iPhone/Shadowrocket
   │ VLESS + XTLS-Vision + Reality(TCP,外观像访问 www.apple.com 的 TLS 1.3)
   ▼
VPS : <PORT>  独立 Xray 进程(systemd 服务 xray-home-<name>)
   │ outbound socks → 127.0.0.1:<SOCKS_PORT>(带用户名密码)
   ▼
Docker:vpngw-<name>-gateway
   │ microsocks(以 dante 用户运行)──fwmark 0x1──► tun0(OpenVPN 客户端)
   ▼
VPN Gate 节点(志愿者 / VPN Gate 自营)──► 互联网
   ▲
Docker:vpngw-<name>-manager(拉节点列表 → 选点 → 写 client.ovpn → 重启 gateway → 每 60 秒健康检查)

设计原则:

2. 各组件怎么工作

2.1 网关容器(gateway)

2.2 管理容器(manager,vpngate.py)

环节 做法
节点来源 先用官方 https://www.vpngate.net/api/iphone/,失败再用 jsDelivr 镜像
解析 每行 split(",", 14),因为最后一列 base64 前面的字段可能带逗号。ping 列为 - 时当作 9999,不加这一条,GB 等国家的节点会被整批丢掉
过滤 国家在 COUNTRIES 里、配置里有 proto tcp、能解析出 remote
探测 4 秒 TCP 连接,连不上直接跳过
排序和选点 按 score 从高到低、ping 从低到高排序,从前 8 名里随机挑一个,避免大家都挤在同一个节点
改写 ovpn 去掉 redirect-gateway;加 route <节点IP> 255.255.255.255 net_gateway;嵌入 vpn/vpn 公共账号
健康检查 每 CHECK_INTERVAL 秒经 gateway:1080 查一次出口 IP(换着用 4 个查 IP 的网站)
换点条件 连续失败 3 次;出口等于 VPS 自己的 IP(泄漏);用了超过 ROTATE_MINUTES
黑名单 换点前查 gateway 最近 5 分钟的日志,出现 AUTH_FAILED 就把当前节点拉黑 BL_SECONDS 秒

节点的出口 IP 常常和它的连接 IP 不一样(多出口或运营商 NAT),所以只要出口不是 VPS 自己的 IP,就算健康。

2.3 Xray(Reality)

3. 完整文件

目录结构:

~/vpngw-<name>/
├── .env                  # GATE_USER / GATE_PASS,权限 600
├── docker-compose.yml
├── gateway/{Dockerfile,entrypoint.sh,up.sh,down.sh}
├── manager/{Dockerfile,vpngate.py}
├── ovpn/client.ovpn      # manager 生成
└── state/{current.json,blacklist.json}
/etc/xray-home-<name>/{xray,config.json,client.txt}
/etc/systemd/system/xray-home-<name>.service

全部文件内容,以及一键生成它们的方法,都在同目录的 install-home-line.sh 里,可以直接读,也可以直接运行。下面只列出值得讲的部分。

3.1 docker-compose.yml(关键行)

services:
  gateway:
    build: ./gateway
    container_name: vpngw-<name>-gateway
    cap_add: [NET_ADMIN]          # 建 tun、改路由、iptables
    devices: [/dev/net/tun]
    sysctls:
      net.ipv4.ip_forward: "1"
    ports:
      - "127.0.0.1:<SOCKS_PORT>:1080"   # ← 一定要写 127.0.0.1
    volumes: [./ovpn:/vpn, ./state:/state]
    environment:
      GATE_USER: ${GATE_USER}
      GATE_PASS: ${GATE_PASS}
    restart: unless-stopped
  manager:
    build: ./manager
    container_name: vpngw-<name>-manager
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock   # 用来 docker restart gateway
      - ./ovpn:/ovpn
      - ./state:/state
    environment:
      - COUNTRIES=<CC>            # 可以写多个:JP,KR
      - GATEWAY_NAME=vpngw-<name>-gateway
      - GATE_USER=${GATE_USER}
      - GATE_PASS=${GATE_PASS}
      # 可选:CHECK_INTERVAL=60  ROTATE_MINUTES=60  BL_SECONDS=21600
    restart: unless-stopped

实际部署中踩过的坑:manager 必须拿到 GATE_USER/GATE_PASS,否则健康检查永远失败。曾经有一条线路因此 3 小时里换了 56 次节点。

3.2 Xray config.json

{
  "log": {"loglevel": "warning"},
  "inbounds": [{
    "port": <PORT>, "protocol": "vless",
    "settings": {"clients": [{"id": "<UUID>", "flow": "xtls-rprx-vision"}], "decryption": "none"},
    "streamSettings": {"network": "tcp", "security": "reality",
      "realitySettings": {"dest": "www.apple.com:443", "serverNames": ["www.apple.com"],
        "privateKey": "<私钥>", "shortIds": ["<shortId>"]}}
  }],
  "outbounds": [
    {"protocol": "socks", "settings": {"servers": [{"address": "127.0.0.1", "port": <SOCKS_PORT>,
      "users": [{"user": "<GATE_USER>", "pass": "<GATE_PASS>"}]}]}},
    {"tag": "block", "protocol": "blackhole"}
  ],
  "routing": {"rules": [{"type": "field", "network": "udp", "outboundTag": "block"}]}
}

3.3 systemd 单元

[Unit]
Description=Standalone Xray xray-home-<name>
After=network-online.target docker.service
[Service]
ExecStart=/etc/xray-home-<name>/xray run -c /etc/xray-home-<name>/config.json
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target

3.4 客户端链接

vless://<UUID>@<你的VPS_IP>:<PORT>?encryption=none&flow=xtls-rprx-vision&security=reality&sni=www.apple.com&fp=chrome&pbk=<公钥>&sid=<shortId>&type=tcp#Home-<CC>

新版 xray x25519 输出 PrivateKey 和 Password (PublicKey),其中 Password 就是 pbk;旧版输出 Private key 和 Public key。

4. 定制

4.1 国家和多条线路

一条线路占用:一个 NAME、一个 SOCKS_PORT(只在 127.0.0.1 上)、一个对外 PORT(要在云安全列表放行),外加一套独立的 UUID、密钥和 shortId。改 install-home-line.sh 顶部的变量再运行一次就是新线路。

建议的端口规划(示例):

国家 NAME SOCKS_PORT PORT
JP jp 8083 2083
KR kr 8084 2052
US us 8085 2087
GB uk 8086 10086

看现在各国有多少节点:

curl -s https://www.vpngate.net/api/iphone/ | awk -F, 'NR>2 && NF>14 {print $7}' | sort | uniq -c | sort -rn

日本、韩国的节点通常最多。美国、英国和欧盟国家常常只有 1 到 4 个,挂了就没有备用,可以考虑 COUNTRIES=JP,KR 这种多国家兜底。

4.2 健康检查参数(manager 环境变量)

变量 默认 说明
CHECK_INTERVAL 60 检查间隔(秒)
ROTATE_MINUTES 60 主动换点周期(分钟)。志愿者的会话常有时长限制。想让出口 IP 保持稳定就调大
BL_SECONDS 21600 AUTH_FAILED 节点的拉黑时长
FAIL_THRESHOLD 3 写在代码里,连续失败几次后换点

改完运行 docker compose up -d 生效。

4.3 黑名单

state/blacklist.json 的格式是 {"节点IP": 过期的unix时间戳}。手动拉黑或解封:

cd ~/vpngw-<name>
python3 - <<'EOF'
import json,time
p='state/blacklist.json'
try: bl=json.load(open(p))
except Exception: bl={}
bl['<节点IP>']=time.time()+86400      # 拉黑 1 天;解封就改成 bl.pop('<节点IP>',None)
json.dump(bl,open(p,'w'))
EOF
docker restart vpngw-<name>-manager

4.4 SNI 的选择

4.5 为什么一定要绑定 127.0.0.1

Docker 发布端口时会在 nat/DOCKER 链里加 DNAT,并在 FORWARD 链放行。这些规则不经过宿主机的 INPUT 链,所以 ufw deny 和 iptables -A INPUT ... DROP 都拦不住。写成 "8085:1080" 就等于全网都能扫到一个 SOCKS 代理,就算带密码也会被爆破、被滥用。正确写法:

ports:
  - "127.0.0.1:8085:1080"

自检,下面只应该看到 127.0.0.1:<SOCKS_PORT>:

sudo ss -lntp | grep -E ':(808[3-9])\b'

再从外部机器确认连不上:nc -vz <你的VPS_IP> 8085

5. 排错

5.1 一次性看完各项状态

N=us; P=2087; S=8085
. ~/vpngw-$N/.env
systemctl is-active xray-home-$N
sudo ss -lntp | grep -E ":($P|$S)\b"
docker ps --filter name=vpngw-$N --format '{{.Names}} {{.Status}}'
docker logs --tail 20 vpngw-$N-manager
docker logs --tail 20 vpngw-$N-gateway | grep -E 'Initialization Sequence|AUTH_FAILED|microsocks|error' || true
curl -s -m 20 --socks5-hostname 127.0.0.1:$S --proxy-user "$GATE_USER:$GATE_PASS" https://api.ipify.org; echo
cat ~/vpngw-$N/state/current.json; echo

5.2 现象对照表

现象 定位 处理
客户端超时,VPS 本机 ss 能看到在监听 云安全列表没放行 加入站规则 TCP ,然后从外部用 nc -vz 验证
外部连接超时,本机也连不上 iptables 里有 REJECT 规则(甲骨文镜像默认就有) iptables -I INPUT -p tcp --dport <PORT> -j ACCEPT 后运行 netfilter-persistent save
curl: (97) SOCKS 认证或握手失败。microsocks 没启动(隧道没建好),或者密码不对 gateway 日志里有没有 Initialization Sequence Completed;核对 .env 和 Xray 出站
AUTH_FAILED 节点拒绝 vpn/vpn manager 会自动拉黑;节点少的国家只能等
no usable [...] TCP nodes 这个国家现在没有可用节点 换国家,或者 COUNTRIES 写多个
health fail 反复出现、频繁换点 manager 没拿到 GATE_USER/PASS;或者查 IP 的网站被节点屏蔽 检查 compose 里的 environment
LEAK! 出口等于 VPS 自己的 IP 会自动换点;检查 up.sh 的策略路由有没有生效:docker exec vpngw-$N-gateway ip rule
Reality 握手失败 pbk、sid、uuid 不匹配,SNI 不一致,或者没设 flow xray run -test -c;对照 client.txt
能连上但特别慢 节点带宽小 docker restart vpngw-$N-manager 换个节点,或者把 ROTATE_MINUTES 调小

5.3 Xray 日志

journalctl -u xray-home-<name> -n 50 --no-pager
# 临时把 loglevel 改成 debug 看握手过程,看完记得改回 warning

6. 优化

7. 安全加固

  1. .env、config.json、client.txt 的权限都设为 600,脚本已经这样处理。
  2. 不同线路用不同的 UUID、密钥和 shortId。哪条泄露了,只重新生成那一条: bash /etc/xray-home-<name>/xray x25519; /etc/xray-home-<name>/xray uuid; openssl rand -hex 8 # 改 config.json 后: sudo systemctl restart xray-home-<name>
  3. 云安全列表只放行对外的 Reality 端口。SOCKS 端口和 Docker 的内部端口一律不放行。
  4. 定期审计哪些端口在对外监听:sudo ss -lntp | awk '$4 !~ /^127\./',对照云安全列表,删掉不再用的规则(先确认没在用)。
  5. SSH 只用密钥登录,关闭密码登录。端口转发(ssh -R)开完就关。
  6. manager 挂载了 docker.sock,几乎相当于 root 权限。只运行自己审计过的代码,不要把 manager 暴露到网络上。
  7. 法律和道德:遵守当地法律和云厂商、VPN Gate 的使用条款;别跑 BT,别占满别人的带宽。

文件内容和一个实际在跑、能正常使用的部署保持一致,manager 只做了参数化(加了 GATEWAY_NAME 变量)。云控制台的界面以实际看到的为准。