deploy/gateway/README.md
机场插件 v2 的参考网关实现。适合先跑通协议的服务商直接部署测试和参考实现使用。
它提供:
/oauth/authorize/.well-known/cpx-gateway/enroll、/challenge、/config、/revoke实现只使用 Node.js 内置模块,包括 node:sqlite。镜像构建时不执行 npm install。
相关文档:
默认部署使用一个公网域名,同时作为登录域名和网关域名。
Client browser
-> https://<domain>/oauth/authorize
-> Caddy
-> gateway:8080
Client updater
-> https://<domain>/.well-known/cpx-gateway
-> https://<domain>/challenge
-> https://<domain>/config
-> Caddy
-> gateway:8080
-> hidden subscription origin
Compose 服务:
| 服务 | 作用 |
|---|---|
caddy | 监听 80/443,申请 Let's Encrypt 证书,反代网关 |
gateway | Node.js 网关进程,只暴露在 compose 内部网络 |
数据卷:
| 卷 | 内容 |
|---|---|
gateway_data | SQLite 数据库 /data/gateway.db |
caddy_data | Caddy 证书和状态 |
caddy_config | Caddy 运行配置 |
客户端会校验 HTTPS、公网 host 和 well-known 文件。不要用 IP、localhost、内网域名或自签证书部署给真实用户。
在 VPS 上进入本目录:
cd deploy/gateway
./deploy.sh
首次运行时,脚本会复制 .env.example 为 .env,询问公网域名,并执行:
docker compose up -d --build
等待 Caddy 申请证书后检查发现文件:
curl https://<domain>/.well-known/cpx-gateway
正常响应类似:
{
"spec": "cpx-plugin/2",
"gateway": "https://<domain>",
"endpoints": {
"enroll": "/enroll",
"challenge": "/challenge",
"config": "/config",
"revoke": "/revoke"
}
}
查看容器状态:
docker compose ps
docker compose logs -f gateway
docker compose logs -f caddy
每个账号对应一个隐藏订阅 URL。该 URL 由网关在服务端请求,客户端不会拿到。
添加用户:
docker compose exec gateway cpx-admin add-user alice 'https://origin.example.com/sub?token=xxxx' --limit 3
命令会提示输入密码。密码只保存 scrypt hash。
常用命令:
docker compose exec gateway cpx-admin list-users
docker compose exec gateway cpx-admin list-users --show-sub
docker compose exec gateway cpx-admin set-sub alice 'https://origin.example.com/sub?token=yyyy'
docker compose exec gateway cpx-admin set-limit alice 5
docker compose exec gateway cpx-admin passwd alice
docker compose exec gateway cpx-admin list-devices alice
docker compose exec gateway cpx-admin revoke-device <deviceId>
docker compose exec gateway cpx-admin del-user alice
说明:
list-users 默认只显示订阅 URL 的 host。list-users --show-sub 会打印完整订阅 URL,只在需要排障时使用。revoke-device 删除设备绑定。客户端下次更新会进入重新登录流程。del-user 会删除用户及其设备。.cpx.cpx 是公开插件描述文件,不含用户信息、token、网关密钥或订阅 URL。所有用户可以使用同一份文件。
在仓库根目录运行:
node scripts/plugin/gen-cpx.mjs https://<domain>/oauth/authorize "Your Airport" https://<domain> your-airport.cpx
分发 your-airport.cpx。用户在 Clash Party 中导入后,会通过系统浏览器打开登录页。登录成功后,客户端注册设备并拉取该账号绑定的 Clash YAML。
首次登录:
https://<domain>/.well-known/cpx-gateway。deviceId。https://<domain>/oauth/authorize?...。code,绑定 PKCE、redirect_uri、client_id,TTL 默认 60 秒。/enroll,提交 code、PKCE verifier、设备公钥和 deviceId。订阅更新:
/challenge 领取 nonce。/config。删除插件:
/revoke。升级:
git pull
cd deploy/gateway
./deploy.sh
账号和设备数据保存在 gateway_data 卷中,升级容器不会删除。
备份数据库到当前目录:
docker compose exec -T gateway cat /data/gateway.db > gateway.db.backup
恢复数据库:
docker compose down
docker run --rm -i -v gateway_gateway_data:/data busybox sh -c 'cat > /data/gateway.db' < gateway.db.backup
docker compose up -d
网关退役:
if grep -q '^RETIRED=' .env; then
sed -i.bak 's/^RETIRED=.*/RETIRED=true/' .env
else
printf '\nRETIRED=true\n' >> .env
fi
docker compose up -d
退役后,/challenge 和 /config 返回 410 / gateway_retired。客户端会回到登录域名重新发现网关。
配置文件为 .env。可参考 .env.example。
| 变量 | 默认值 | 说明 |
|---|---|---|
DOMAIN | 无 | 公网域名,必填 |
PUBLIC_ORIGIN | https://$DOMAIN | 写入 /.well-known/cpx-gateway 的 gateway origin |
DEVICE_LIMIT_DEFAULT | 3 | 新用户默认设备数上限,可被 add-user --limit 覆盖 |
CLOCK_SKEW_MS | 300000 | /config、/revoke 签名时间戳允许偏差 |
CODE_TTL_MS | 60000 | authorize code 有效期 |
NONCE_TTL_MS | 60000 | challenge nonce 有效期 |
NONCE_POOL_MAX | 8 | 单设备待用 nonce 数上限 |
LOGIN_MAX | 10 | 单 IP 登录尝试次数上限 |
LOGIN_WINDOW_MS | 60000 | 登录限流窗口 |
SUB_TIMEOUT_MS | 30000 | 拉取隐藏订阅的超时 |
SUB_MAX_BYTES | 10485760 | 隐藏订阅响应体上限 |
RETIRED | false | 设置为 true 时返回网关退役信号 |
ORIGIN_CA_FILE | 空 | 订阅 origin 使用私有 CA 时,在容器内指定 CA 文件路径 |
PORT | 8080 | gateway 容器内监听端口 |
DB_PATH | /data/gateway.db | SQLite 数据库路径 |
如果登录域名和网关域名需要拆开,保持 .cpx 里的 loginUrl 指向登录域名,同时把登录域名上的 /.well-known/cpx-gateway 的 gateway 指向新的公网网关 origin。当前参考部署默认两者使用同一个域名。
本目录不需要安装依赖。需要 Node.js >= 22.5.0。
cd deploy/gateway
npm test
npm run check-vectors
npm start
说明:
npm test 使用 node:test 跑网关测试。npm run check-vectors 使用客户端签名向量检查 Ed25519 互通。npm start 在本地启动 HTTP 网关,默认监听 :8080。真实客户端接入仍需要公网 HTTPS 和合法 well-known。/config 和 /revoke 使用 Ed25519 设备签名和一次性 nonce 防重放。