跳转至

wgcf 共享运行器合同

适用于 v1.31.83 的内置运行器和独立 tools/wgcf-warp;部署和成功判据见 运维步骤。本版支持显式共享、全局引用、多选分类和受管删除; 旧 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 注册或密钥。上游注册失败之后的 清理语义不是事务回滚,缺少材料时必须人工核对,不允许自动循环创建。

验证

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 可能重新显示原档案名称。