Files
Telegram-Panel/docs/developer/wgcf-wireproxy.md

207 lines
18 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# wgcf 共享运行器合同
适用于 v1.31.83 的内置运行器和独立 `tools/wgcf-warp`;部署和成功判据见
[运维步骤](../deployment/wgcf-wireproxy.md)。本版支持显式共享、全局引用、多选分类和受管删除;
旧 Docker WARP 运行管理仍已退役。沿用 v1.31.81 的受管出口标识,模块 ABI 不变。
## 生命周期与安全边界
- `warpctl.py` 只依赖 Python 3.11+ 标准库。Linux 容器为生产运行目标,Windows 仅运行
不涉及 POSIX 子进程信号的测试。所有上游 CLI 使用参数数组启动,禁用 shell 和标准输入。
- `register` 仅在显式 `--accept-tos` 时发起注册。首次调用先持久化尝试标志;中断后存在完整
TOML 则复用,否则进入人工核对状态。每 profile 独立 CLI 进程,不把 wgcf 的全局认证客户端
当成多账户 SDK。上游进程单次最多 45 秒,超时杀死并回收子进程。
- `generate` 只接受 Interface/Peer 白名单字段,要求注册私钥一致和 IPv4/IPv6 全路由,
使用固定 wireproxy `-n` 检查。禁止配置钩子、WGConfig、TunnelDomains 和环境变量插值。
- 目录 0700,文件 0600,进程 umask 0077;同目录 fsync + replace 原子落盘,拒绝符号链接
和路径穿越。共享数据卷只允许本工具访问,不视为恶意本机管理员间的隔离边界。
- CLI 操作互斥;supervisor 使用独立进程锁,读取原子元数据,不受注册网络等待阻塞。
每个 profile 一个 wireproxy 子进程,停止和退出时 terminate/wait/kill;异常退出冷却 30 秒。
单份元数据损坏或进程无法创建只标记该档案 `failed`,不终止其它正常出口。
每次启停写入随机修订号,运行器执行后回写同一修订号;曾启动的档案只有最新停止请求
被确认且状态为 `stopped` 才能重新生成,防止异步 stop/generate/start 保留旧密码进程。
进程日志不持久化,不把配置、Token、Cookie、私钥或上游错误文本转发到 API/终端。
- 用户态 WireGuard 只在进程内部处理流量。共享容器不持有 NET_ADMIN、TUN 或 Docker
Socket,不建立宿主接口;共享容器故障会影响其全部出口,不能宣称进程级高可用。
## 面板内置运行器
`WgcfWarpService` 仅在 Linux 主容器中启用,使用发布目录内的 `warpctl.py` 与镜像固定的
wgcf 2.3.0、wireproxy 1.1.3。它以 `/data/wgcf-warp`(或配置的持久根)保存材料,在
子进程退出时以 TERM、超时、进程树清理的顺序结束监督器。依赖缺失会使专用 API 返回不可用,
不能阻塞面板启动。
`POST /api/panel/proxies/wgcf` 必须提供 UUID 请求标识和条款接受标志,名称可为空;空名称由
服务按 `web-<uuid>` 派生稳定的“轻量 WARP-XXXXXXXX”名称。返回已持久化的档案状态;同一
标识对应 `web-<uuid>`,后台 `provision` 只会注册、生成并开始期望运行一次。`GET` 返回白名单
状态,绝不返回 Token、私钥、密码、配置或上游输出。`start`、`stop`、`test`、`resume` 也是
专用端点。创建、检测和启停可超过常规 API 时限,因此页面只轮询状态,不以 HTTP 连接存活
作为完成判据。
已有修订号的档案重复执行 `provision` 会保留期望启停状态和注册材料;恢复后台任务会显式
执行 `start`,而不是依赖配置命令隐式启用。网页启停以状态锁、工具锁的固定顺序串行化,
读取和持久化期间保持状态锁,避免并发恢复的 `creating` 被旧启停结果覆盖。
`OutboundProxies.ManagedWgcfProfile` 是唯一、可空的受管标识。常规创建/导入/编辑仍拒绝伪造
该标识或同一 loopback 端口。显式 `existing`(包括登录、导入、单个及批量绑定)和 `global`
允许共享同一个已就绪出口;服务端在变更锁内复核,不再以已有账号引用作为拒绝理由。
首次连接及失败清理全过程持有 claim,未完成的首次连接或清理占用不能共享。
正式绑定完成、租约释放后,出口可共享;以后解除全部绑定也不因原临时归属标记而永久禁止复用。
崩溃重启会停止未绑定的临时档案。状态不足或路由无效时连接失败,不回退直连。
迁移 `20261009010000_AddManagedWgcfProfile` 在 SQLite 以原生 DROP COLUMN 回滚;
仅回退到不识别此字段的旧版本时,才需要先将账号迁往可用路由、停止出口并备份数据库与私有目录。
## 统一列表与旧运行器退役(v1.31.82)
代理页通过 `ProxyBoundAccountsDialog` 按代理编号分页读取专属绑定账号;查询接口只返回
账号编号、备注、昵称、手机号后四位与路由并发快照。轻量运行器未就绪也允许查看、解除旧绑定。
解绑及换绑复用 `POST /accounts/{id}/proxy`,必须携带列表中的 `expectedProxyId` 与
`expectedUseGlobalProxy`,并明确选择 `direct`、`global` 或 `existing`,不能先清空绑定再设置目标。
后端继续在变更锁内校验目标和原路由,严格断开旧客户端后保存新路由;不删除或停止原 WARP 材料。
目标选择排除旧 WARP、自身、停用代理和未达到 `shareEligible` 的轻量档案,提交时服务端仍复核。
账号列表仅包含显式 `ProxyId` 绑定,不能将全局继承人数冒充列表条目;全局使用数和提示单独展示。
验收覆盖空/非空列表、分页、路由并发冲突、三类解绑策略、共享目标、请求超时回读、重复点击及
查询乱序。修改期间锁定弹窗,完成或失败后清除旧选择并回读,不自动重放修改请求;刷新失败时
阻止再次提交。无数据库迁移;回滚前先按下文合同迁走目标版本不支持的路由。
代理页将常规代理 DTO 与 `GET /proxies/wgcf` 的档案状态合并为一张表,以
`managedWgcfProfile` / `profile` 关联。同一出口只显示一行;尚未生成代理记录的创建中或
失败档案也必须显示,不能因缺少 `proxyId` 消失。启动、停止、恢复、检测和单账号绑定在
对应表格行内执行。已有代理编号的受管出口可多选并批量分类;单个及批量删除按受管路径处理,
不允许普通编辑接口改写其运行参数。尚未落库的档案可单行或勾选删除,仍需经过占用检查。
历史 `kind=warp` 记录已停止连接和运行维护,但仍可在代理统一列表中勾选删除;只有未被账号或全局代理引用的旧记录才会删除,仍在使用的记录会保留并显示后端返回的占用原因。
`DELETE /api/panel/proxies/wgcf/{profile}` 返回 `OperationResultDto`。在持有档案占用和变更锁时
再次确认没有账号、启用的全局引用或其它 claim,然后请求停止并等待对应修订回执确认,才能删除
本地代理与档案材料。工具 `delete` 同样要求确认停止;不能把“已写停止意图”当成进程已退出。
停止或清理失败保留可重试状态,不返回成功;重复操作不得误删仍被引用的资源。批量删除逐项执行,
失败项保留并回报原因,不阻止其它可删除项。此操作不保证注销 Cloudflare 远端注册。
旧 Docker WARP 管理器、后台维护与状态/刷新 API 已移除,不再挂载 Docker Socket 或加载
旧 WARP Compose 覆盖。数据库中历史 `kind=warp` 记录只用于识别和迁移,不再作为可用路由;
账号或全局配置仍引用它时必须在连接前失败,禁止降级直连。不能用普通 SOCKS/HTTP 参数
重新包装旧记录绕过该限制。
`warp_pool` 仅候选已启用、`ready`、监听就绪、检测成功且无账号/全局引用的
`managedWgcfProfile` 出口。选取后取得独占租约,再核对实际状态;该自动分配策略每份出口只领取给
一个账号,已占用或未就绪的候选不能复用,池为空时在首次 Telegram 连接前失败。
`poolEligible` 不等于显式共享资格;`shareEligible` 允许已正式绑定的就绪出口复用。
档案 DTO 同时返回 `isGlobal` 和 `usageCount`,后者包含全局继承使用情况,`accountCount` 仍表示显式绑定数。
受管轻量出口不参与普通代理的定时 HTTP 健康巡检;只有验证 `warp=on` 的专用检测
才能将其标记为可分配,避免普通网页可访问时误报 WARP 已就绪。
领取资格在持有档案 claim 后按列表 `poolEligible` 的同一规则复核:有停止意图、
`desired=false`、临时档案、账号绑定或启用的全局代理引用均不可领取,即使旧运行回执
仍显示 `listening`。只忽略本次领取自己的 claim,失败须释放 claim 并继续下一候选。
全局引用按统一配置解析:`Enabled=false` 不占用池;未显式设置 `Enabled` 且
`SourceMode=existing` 有有效代理编号时按启用处理。验收同时比较列表资格和实际领取,
再验证首个失效候选不会阻断后续可用出口;异常时保留私有材料并检查停止意图、运行回执
与全局设置,不通过清空档案绕过检查。
`/accounts/login?legacy=1` 与 `/accounts/import?legacy=1` 的 Razor 兼容页同样读取
`WgcfWarpService.ListAsync` 的 `poolEligible` 快照,并与代理 ID、受管档案标识交叉匹配。
运行器不可用或状态读取失败时空闲池显示为零;旧 `kind=warp` 不进入可选路由。
已有代理下拉使用 `shareEligible` 判断轻量出口,不能复用空闲池资格排除共享出口。
页面快照仅控制选项和预检,提交后的独占领取或共享绑定仍由后端再次核验。
验收需覆盖就绪空闲出口可选、不可领取/缺失档案/全局引用不可选,以及零候选时首次连接被阻止。
本修复无接口或数据库迁移;回滚应用会恢复旧兼容页缺陷,可改用新版 `/ui/accounts/login`
和 `/ui/accounts/import`,不要恢复已退役的旧容器路由。
升级前逐账号迁移并记录旧路由、新代理 ID、出口检测及只读 Telegram 资料验证结果。
全部受影响账号验证通过后,才移除旧全局引用、旧代理记录及旧容器;不由应用启动自动清理。
回滚至 v1.31.82 或更早版本前,先把受管共享和全局路由迁回该版本支持的路由,否则旧版会失败闭锁。
保留迁移后的数据库和 `wgcf-warp` 材料,不能恢复迁移前数据库而把账号重新指向已删除容器。
本次无需新增数据库迁移;已删除本地材料需从独立备份恢复,回滚应用不会自动复原。
## 统一创建与首次连接(v1.31.81)
所有新建入口使用 `IManagedWarpProvisioner`,固定 SOCKS5,不再创建官方 WARP Docker 容器。
v1.31.82 已移除旧容器的运行管理与路由使用。登录、导入和逐账号绑定的
`warp_per_account` 必须提供 `acceptWarpTerms=true`;条款未接受或环境不可用时不得连接 Telegram。
Core 通过宿主接口创建,不引用 Web。运行器等待 `ready`、`listening`、检测成功及出口 IP 后
返回代理和独占租约。不可预测的租约 token 只在进程内部传递,不能由公开 DTO 提交。
`TemporaryWarpClaimStore` 阻止其它账号流程和网页启停、恢复抢占首连出口。登录重发验证码、
二维码重生成沿用冻结代理。正式绑定完成后释放租约;激活失败仍保留已绑定出口,账号保持停用。
失败或取消先严格断开客户端,再停止未绑定出口,保留注册材料。队列取消保存停止意图,即使
后台注册稍后完成也要停止。重启仅停止带临时归属且没有正式绑定的档案,保留普通网页出口。
内部请求标识映射为确定性档案名,UUID 只负责幂等,不授予出口归属。网页一键创建会自动使用
已接受的条款标志;登录、导入和账号绑定仍必须由调用方显式传入 `acceptWarpTerms=true`。
首次 HTTP 响应丢失后
不得凭公开 UUID接管其它登录会话;保留已停止材料供核对。暂不提供跨新登录会话的自动接管。
验收包括拒绝未接受条款、未就绪不连 Telegram、租约互斥、取消收尾、重试复用和重启分类清理。
网页启停和恢复须在整个操作期间持有档案占用;获取首连租约后重新读取数据库与运行态,
防止读取旧启用状态后被并发停止。已正式绑定的临时档案允许故障恢复,仍保留原请求归属。
导入清理若严格断开客户端失败,进程内保留租约及档案占用,拒绝其它流程抢用出口。
此时不停止仍可能被客户端使用的出口;需重启面板并核对账号绑定与档案状态后人工恢复。
## 独立工具集成与恢复
凭据 JSON 包含精确 `panelUrl` 和管理员 Cookie,Linux 限制为 0400/0600。拒绝来源重定向,
默认 HTTPS;明文内网需显式选择。每个请求设置 15 秒连接/读超时和总响应期限,最后一次
阻塞读取可能再占用最多 15 秒;响应限制 4 MiB,不保存原始响应。
绑定流程:分页读取账号列表 → 记录旧代理 ID/原全局模式 → 持久化唯一代理名称和创建意图 →
创建 `wireguard_warp/socks5` → 面板出口检测 → CAS 绑定 → 回读账号代理。API 返回 HTTP 200
仍需检查 `success=1/failed=0`,不得仅靠状态码宣布成功。网络超时可能发生在提交之后,
重试通过唯一名称和当前账号绑定恢复;找不到已尝试创建的代理时停止,避免重复资源。
`POST /proxies/{id}/test` 未加载账号导航且 `isGlobal` 为默认值,不能用来判定占用;工具在
提交绑定前及绑定后都用 `GET /proxies` 的完整 DTO 重新检查账号数量和全局用途。
独立工具仍拒绝自动迁移旧受管 WARP 账号;这类迁移由运维在升级前逐账号完成并验收。
新出口已有其它账号或全局用途时拒绝绑定。已有宿主 API 只对 `ExpectedProxyId` 比较,不区分并发从直连到全局
(二者 ID 都为 0)的瞬间切换;运维者应避免同时编辑待迁移账号。跨工具/面板 UI 的并发
一对一占用不是数据库唯一约束,本工具不宣称提供这种保证。
档案保留旧代理引用用于回滚;不自动删除面板代理、WARP 注册或密钥。上游注册失败之后的
清理语义不是事务回滚,缺少材料时必须人工核对,不允许自动循环创建。
## 验证
```bash
timeout 60s python -m unittest discover -s tools/wgcf-warp/tests -v
mkdocs build --strict
```
`.github/workflows/wgcf-tool.yml` 在 Linux 验证注册部分成功、超时、配置注入、端口冲突、
独立密码、敏感输出、子进程启停/重启恢复,以及本地模拟面板的出口失败、绑定失败、丢失
创建响应、人工换绑和重定向。镜像构建校验固定二进制 SHA256,再执行真实 wireproxy
`configtest`,不注册或建立真实 WireGuard 隧道。
云端验收必须额外执行运维文档中的小样本注册和 Telegram 路径;未执行时 PR/Release
明确写“未实网账号绑定验收”,不能据此关闭完整实网功能诉求。v1.31.83 需验证共享绑定、
全局引用、自动池排除占用项,以及使用中停止/删除拒绝、停止失败保留档案和混合批量失败结果。
应用回滚保留持久卷,并先按前述合同迁回目标版本支持的路由。
## 代理表操作布局(v1.31.84)
Proxies.vue 将行操作统一放入 click 触发的 Element Plus Dropdown,菜单传送至 body 以避免表格裁切。使用情况列提供150px宽度与可换行账号链接;104px右固定操作列使用主题不透明背景,防止横向滚动时显示下方内容。保持原控制器调用和权限/占用判断,不新增接口或数据格式。
验收:宽屏/窄屏、横向滚动、展开菜单、打开绑定账号弹窗;禁止执行的启停/删除仍为disabled并展示原因。回滚只恢复前端资源,不涉及路由或材料迁移。API参考无需更新,因为请求和响应合同未变。
## HTTP 浏览器请求编号兼容(v1.31.85)
创建出口、登录、导入、单账号及批量换绑统一使用 `createRequestId`。
HTTPS 支持时调用原生 `crypto.randomUUID`;普通 HTTP 下使用 `crypto.getRandomValues`
生成符合 UUID v4 版本位与变体位的请求编号,不使用弱随机数。
重试保留现有请求编号与载荷冻结规则,避免超时后重复注册。
验收:模拟缺少 randomUUID 的浏览器,创建弹窗可打开;提交超时后重试编号相同;
登录、导入、账号操作仍携带 UUID。后端 API 和持久化格式未变,无需接口参考更新。
失败排查:若连 getRandomValues 也不可用,需升级浏览器;HTTP 兼容不等于传输加密,
公开部署仍按部署文档配置 HTTPS。回滚镜像保留数据卷即可,但会恢复普通 HTTP 下的此缺陷。
## 代理名称的权威来源(v1.31.86)
已落库出口的用户显示名称以 `OutboundProxy.Name` 为准;运行器 DTO 优先读取数据库名称。
档案中的原始 Name 继续服务原创建请求的幂等校验,不因重命名改写请求标识或重新注册。
恢复或重新启用已有代理时保留数据库名称。尚未落库的档案继续使用档案名称,暂不开放重命名。
重命名由独立的元数据接口执行,使用代理变更锁,只更新名称及更新时间。不得复用全量代理编辑来
修改受管出口,也不得获取会限制在用出口的运行租约或重启进程。名称在 trim 后校验 1–80 字符。
验收覆盖在用出口名称持久化、运行态/数据库一致、账号绑定和连接参数保留、无效名称拒绝。
本次没有数据库迁移;回滚应用不回滚数据库名称,旧版 DTO 可能重新显示原档案名称。