Refine WireGuard client routing and authentication UI
This commit is contained in:
1 parent
eabb3e4472
commit
41fb9d5631
33 files changed
+1709
-469
No files matched your search
+1
-1
@@ -11,7 +11,7 @@
|
||||
## 首次构建与启动
|
||||
|
||||
```bash
|
||||
git clone https://gitee.com/opsx-tools/opsx-vpn.git
|
||||
git clone https://gitea.91ai.io/rlwj-lab/opsx-vpn.git
|
||||
cd opsx-vpn
|
||||
cd frontend && npm ci && npm run build && cd ..
|
||||
go build -ldflags="-s -w" -o opsx-vpn ./backend
|
||||
|
||||
@@ -2,27 +2,28 @@
|
||||
|
||||
| 字段 | 内容 |
|
||||
|---|---|
|
||||
| 版本 / 变更级别 | 0.1 / S3(系统隧道、路由、DNS 与特权执行) |
|
||||
| 版本 / 变更级别 | 0.3 / S3(内置 Go 系统隧道、路由、DNS 与特权执行) |
|
||||
| 设计状态 | Draft,已核对本地实现与 opsxdb / opsxapi 的桌面打包方式 |
|
||||
| 实现状态 | 已引入可打包 Go 版标准 WireGuard 无特权探针和显式启停的本机 SOCKS5 模式;系统 TUN/路由/DNS 尚未完成 |
|
||||
| 实现状态 | Go 探针、SOCKS5 与 macOS 原生 `utun` 数据面、Rust 受限子进程桥接代码已实现;系统工具调用路径已移除,内置直连尚不可用;拨号用户可自动登记至代理启动;macOS 系统 SOCKS 开关已实现,真机验收待办;系统接口地址/路由/DNS 和恢复尚未完成 |
|
||||
| 负责人 / 日期 | opsx-vpn / 2026-10-21 |
|
||||
| 关联需求 | 企业员工安装 OpsX VPN 即可拨号,无需 Homebrew 或手动安装 wireguard-tools |
|
||||
| 批准状态 | 用户已要求先评估设计后开发;具体平台发布签名尚待配置 |
|
||||
|
||||
## 1. 背景、目标与范围
|
||||
|
||||
- **当前行为与证据**:`client/src-tauri/src/tunnel.rs` 使用本机 `wg-quick` 创建接口,macOS 通过管理员授权执行,Linux 依赖 `pkexec`;`system.rs` 只检查工具位置;Windows 返回未实现。`client/src-tauri/tauri.conf.json` 未打包隧道组件。`../opsxdb/src-tauri/tauri.conf.json` 使用 `externalBin` 打包 Go sidecar,`../opsxdb/run.sh` 按目标 triple 复制二进制;`../opsxapi/src-tauri/src/lib.rs` 则在 Rust 内直接实现网络业务。两者均未实现 VPN 特权 helper。现有 `docs/specs/SDD-002-desktop-wireguard-client.md` 中普通拨号用户和设备 API 已进入第二阶段,目标 OS 的真实握手仍待验。
|
||||
- **当前行为与证据**:`client/src-tauri/src/tunnel.rs` 只管理本机密钥与设备记录;此前的 `wg-quick` 执行和 PATH 检测已按用户要求移除。`system.rs` 在特权网络服务完成前始终报告直连未就绪;Go 探针及应用代理仍可用。`client/src-tauri/tauri.conf.json` 打包 Go sidecar;`../opsxdb/src-tauri/tauri.conf.json` 使用 `externalBin` 打包 Go sidecar。现有 `docs/specs/SDD-002-desktop-wireguard-client.md` 中普通拨号用户和设备 API 已进入第二阶段,目标 OS 的真实系统隧道仍待验。
|
||||
- **目标**:Rust 保持唯一的客户端控制面及本机私钥拥有者;按 OS 引入可随应用签名分发的隧道执行层。macOS 优先;用户不必安装 Homebrew 或 WireGuard 命令。Linux 复用内核 WireGuard,但打包必要工具与权限策略;Windows 使用专门的 Windows WireGuard 驱动/服务方案,不把 macOS 的 Go TUN 实现当通用驱动。
|
||||
- **本轮实施边界**:实现可打包的 Go 用户空间 WireGuard netstack 探针和明确标注的本机 SOCKS5 应用代理;Rust 执行层仍明确报告系统工具依赖。完成可签名的特权 helper、路由/DNS 回滚和目标 OS 握手验收之前,不能在 UI 上宣称已实现免依赖系统拨号。
|
||||
- **本轮实施边界**:保留可打包的 Go netstack 探针与本机 SOCKS5 应用代理;客户端不再执行系统 WireGuard 命令。完成特权 helper、路由/DNS 回滚和目标 OS 握手验收之前,UI 只报告内置直连未就绪。
|
||||
- **保留行为**:普通用户认证、设备公钥注册、配置领取及服务端 WireGuard UDP 协议不改;前端不接收私钥,服务端不接收客户端私钥。
|
||||
|
||||
## 2. 需求与约束
|
||||
|
||||
- FR-01:启用免安装分发通道的发布版仅在安装包内存在**正确目标架构、可校验版本**的隧道组件且系统适配就绪时报告“内置引擎就绪”;缺失时应明确报错,绝不悄悄运行 PATH 中同名程序。当前过渡版明确标记为“系统工具模式”,不得作为免安装版分发。
|
||||
- FR-02:开发模式仍可使用系统 `wg-quick`,并明确标记为“系统工具模式”;二者的就绪检测和实际连接路径必须一致。
|
||||
- FR-01:启用免安装分发通道的发布版仅在安装包内存在**正确目标架构、可校验版本**的隧道组件且系统适配就绪时报告“内置引擎就绪”;缺失时应明确报错,绝不运行 PATH 中同名程序。
|
||||
- FR-02:所有模式均不调用本机 `wg` / `wg-quick`;内置网络服务未就绪时直连明确不可用,应用代理单独可用。
|
||||
- FR-03:随包组件只接受固定动作及受约束的本机配置路径,不接受来自 WebView 的任意命令或路径;日志不可含私钥、令牌、完整配置。
|
||||
- FR-04:macOS 正式接入必须完成受控提权、TUN、路由与 DNS 原值保存和恢复;授权取消、进程崩溃、升级或断开均须保持可恢复性。系统辅助程序的签名与安装按目标发行证书验收。
|
||||
- FR-05:Windows/Linux 按各自系统 API 和权限交付;未验收的平台应明确“尚不支持内置隧道”,不能用模拟连接替代。
|
||||
- FR-06:无系统工具的用户登录后可从概览启动 Go 应用代理:先校验最新线路授权、自动登记/刷新本机设备、使用实际 WireGuard 参数启动本机 SOCKS5。UI 必须将代理状态与整机 VPN 状态分开;退出、撤销设备、进程停止后不可仍报代理在线。
|
||||
- NFR-01:正式发布不得从远程地址动态下载可执行文件、不得通过 shell 插入远端配置、不得使用旧飞连魔改 WireGuard 协议。
|
||||
- NFR-02:非管理员安装包、系统提权、代码签名、升级与卸载必须分别测试;不能将可执行文件放进用户可写目录再让 root 执行。
|
||||
|
||||
@@ -30,8 +31,8 @@
|
||||
|
||||
```text
|
||||
React → Tauri invoke → Rust(登录、设备私钥、连接状态)
|
||||
├─ 开发过渡:系统 wg-quick
|
||||
└─ 发布引擎:签名的特权隧道组件
|
||||
├─ 当前:Go 应用代理;直连未就绪
|
||||
└─ 目标:签名的特权隧道组件
|
||||
├─ macOS:WireGuard Go 引擎 + 受控网络配置/恢复
|
||||
├─ Linux:内核 WireGuard + 受控配置服务
|
||||
└─ Windows:WireGuardNT/官方隧道服务适配
|
||||
@@ -46,9 +47,30 @@ React → Tauri invoke → Rust(登录、设备私钥、连接状态)
|
||||
|
||||
## 4. 接口、数据与配置契约
|
||||
|
||||
- `get_system_readiness` 向前兼容既有 `platform/wg_found/wg_quick_found`,新增 `engine_mode`(`bundled` / `system_tools` / `unsupported`)及可读 `engine_detail`;由 Rust 而非 UI 决定。`bundled` 仅可在实际引擎、权限和系统配置均通过验收后返回。
|
||||
### 无 `wg` / `wg-quick` 的 macOS 直连实现
|
||||
|
||||
- Go 子进程在用户权限下使用上游 `tun.CreateTUN("utun", mtu)` 创建接口、启动 WireGuard,并通过一次性 stdout 消息把系统分配的 `utunN` 回传 Rust;本机配置保持 0600。Go 子进程不能仅因为接口创建成功就报告已联网;Rust 完成 IP、路由、DNS 设置后才允许控制面查询真实握手。
|
||||
- 对照 `../demo/clash-verge-rev`:它的 `config/clash.rs` 为 Go 核心 Mihomo 配置 `tun.auto-route/auto-detect-interface/dns-hijack`,`core/service.rs` 由经管理员确认的安装器将核心发布到服务拥有的路径,再用受限 IPC 启动;`feat/tun.rs` 在服务未就绪时停用 TUN。其“rule/global”是 Mihomo 的流量策略,不等于 WireGuard 的 AllowedIPs。OpsX 不直接复用 Mihomo 的代理规则或服务 IPC 协议。
|
||||
- 其他参考边界:corplink-rs 的飞连私有协议字段不适用于本项目标准 WireGuard;Tailscale/NetBird 可以参考服务化与网络生命周期,但不能据此假设它们的路由/DNS 实现已经被本项目核对或移植;AmneziaWG-go 的混淆需服务端同时支持,当前服务端未采用该协议。本机只核对过 Clash Verge Rev 的源码,其他项目目前属于候选参考。
|
||||
- 已核对本地 `../demo/vpn/wireguard-go`(提交 `2631ce9`),与客户端 `go.mod` 锁定的上游模块版本一致。其 `tun/tun_darwin.go` 中 `CreateTUN("utun", mtu)` 让内核分配真实接口名,且可选环境变量 `WG_TUN_NAME_FILE` 会写文件;本项目禁用这一额外写入,仅通过 JSON 回传真实名称。其 `main.go` 用 `device.Wait`、终止信号和 `device.Close` 管理生命周期,但没有替应用设置地址、路由或 DNS;`device/uapi.go` 的 `IpcGet` 会包含私钥,必须在 Go 进程内过滤后才返回 Rust。
|
||||
- Go `tun` 命令仅在 macOS 运行,读取 0600 本机记录,创建接口后只输出 `interface_created` 和系统分配的 `utunN`;其标准输入仅接受 `status`、`stop`,关闭输入、父进程退出或接口关闭时终止。`status` 仅输出握手秒数与收发字节数,不透传包含私钥的 WireGuard UAPI 原文;首次接口事件**不是**握手或路由就绪。普通登录进程不得因为收到这个消息就更新 UI 为已连接。
|
||||
- `interface_created` 同时携带不含密钥的 IPv4 路由计划:先在原物理出口固定网关 endpoint `/32`(如果授权路由覆盖它),再按授权网段安装 TUN 路由;`0.0.0.0/0` 展开为两个 `/1`。域名 endpoint 在动路由前解析成一个 IP,WireGuard Device 使用同一个解析结果,防止多个 DNS 地址导致绕行规则与实际发送目标不一致。若需网关绕行,Go 在启动前只读调用 `/sbin/route -n get` 并提取物理接口和网关;已走 `utun`、无接口或格式非法则拒绝。此结果是快照,特权服务实际安装路由前仍须复核物理出口,防止检查后网络切换。当前只规划 IPv4,出现 IPv6 授权立即拒绝,不把未接管的 IPv6 流量误称为全局模式。仍需特权服务安全执行和恢复,此计划本身不会改变系统路由。
|
||||
- `client/src-tauri/src/native_tunnel.rs` 已能在 macOS 启动 Go 子进程、限制启动与查询等待时长、解析非机密计划与握手计数,断开或 Rust 对象销毁时终止子进程;字段在 Rust 侧再次校验。该模块先保持脱离 UI 的直连路径,待特权服务完成网络事务与恢复并通过握手/流量验收才启用,防止接口创建事件被 UI 当作可联网状态。
|
||||
- Rust 只允许经校验的 IPv4、合法 `utunN`、服务端 `AllowedIPs` 及固定动作传给受控系统服务;服务安装到管理员拥有的固定位置,验签/校验所有权,使用受限本地 IPC 更新路由和 DNS,不能从用户可写路径提权运行 Go 核心,也不通过临时拼接的 AppleScript/shell 命令充当发布版特权服务。专线 endpoint 需为物理网络出口保留路由,防止全局路由自吞。
|
||||
- 系统配置事务顺序:解析 endpoint 的实际地址并记录原物理出口与 DNS 状态;先持久化带有接口名和拥有者标识的恢复记录,再设置接口地址和路由、受控 DNS;核对网络栈实际状态、服务端新握手和授权目标连通性后才发布连接状态。全局路由应避开 VPN endpoint,网络切换时重新计算出口。若启动中途失败,按逆序恢复,只撤回自身仍持有的设置;恢复失败保留记录并提示人工修复,不覆盖后续第三方改动。服务重启时扫描自己的未完成事务并恢复。
|
||||
- 路由/DNS 变更前写入只属于本应用的恢复记录。部分失败立即回滚;断开、注销、设备撤销、Go 进程意外退出、应用退出或下次启动重试恢复,只清理与当前接口/目标一致的配置。恢复失败时明确报告,并阻止把连接显示为成功。网络切换、休眠唤醒、DNS 原值和抢占冲突需要 macOS 真机验证。
|
||||
- 本轮优先 macOS;Linux/Windows 需要独立系统权限与适配,未实现时不得显示内置直连可用。现有 SOCKS5 应用代理保留;任何平台均不回退到系统 WireGuard 工具。
|
||||
|
||||
- `get_system_readiness` 报告平台、Go 代理组件、`engine_mode` 和可读 `engine_detail`;不再检测或展示系统 WireGuard 命令。`bundled` 仅可在实际引擎、权限和系统配置均通过验收后返回。
|
||||
- 内置组件的目标 triple 路径和版本由构建产物显式声明;发布流程核对哈希、签名和可执行权限。不能从用户可写数据目录或 PATH 寻找发布 helper。
|
||||
- 服务端 API、数据库及客户端设备密钥数据模型不变;未来 helper 通过最小化本地 IPC 收到连接参数,不接触用户口令或管理令牌。
|
||||
- 本轮 Go 代理继续显式开启,不自动修改系统代理、路由或 DNS。Rust 将设备准备与整机拨号分离;代理路径复用同一设备授权流程,启动时重新查授权,失败时不遗留假在线状态。试验用户的 `AllowedIPs=10.88.0.0/24` 只可代理该网段,不能宣称可经 VPN 浏览公网。
|
||||
|
||||
### macOS 系统代理扩展(S3,2026-10-21)
|
||||
|
||||
- 用户显式启动应用代理后可单独启用系统 SOCKS 代理。仅当设备配置实际包含 `0.0.0.0/0` 时允许开启;当前私网试验专线应拒绝,避免把普通网页发送到不允许公网访问的隧道。系统代理只影响遵循 macOS 代理设置的应用,不等同于整机 TUN,也不能保证所有 UDP 或 DNS 走隧道。
|
||||
- 启用前枚举网络服务、检查 HTTP/HTTPS/SOCKS/PAC/自动发现是否已有代理;已有代理时拒绝接管。持久记录将要更改的服务与原 SOCKS 配置;启用失败逐项回滚。关闭时只恢复仍指向自身监听端口的服务,绝不覆盖其他程序随后修改的代理。进程意外退出或下次启动应尝试清理残留;无法安全恢复时给出错误并保留恢复记录。网络服务新增或切换不能自动把未授权服务纳入代理范围。
|
||||
- 启用、关闭、注销、退出与隧道切换均需串行操作。恢复失败不得悄悄关闭仍被系统代理引用的本地监听;手动改动代理后状态显示为冲突。验收包括私网路由拒绝、已有代理拒绝、启停还原、部分失败回滚、异常退出后重启恢复及更换网络服务。正式真机验收前不把此能力描述为跨平台整机 VPN。
|
||||
|
||||
## 5. 风险与验收
|
||||
|
||||
@@ -61,16 +83,20 @@ React → Tauri invoke → Rust(登录、设备私钥、连接状态)
|
||||
|
||||
| AC | 对应需求 | 前置条件与操作 | 预期结果 | 验证方式 | 结果 |
|
||||
|---|---|---|---|---|---|
|
||||
| AC-01 | FR-01, FR-02 | 无 Homebrew 的 macOS 打开当前过渡版/未来免安装版 | 过渡版明确显示依赖;免安装版仅在组件和权限实际就绪时显示内置可用 | 单测、构建、真机 | 当前过渡版选择器单测通过;免安装版 Not Run |
|
||||
| AC-01 | FR-01, FR-02 | macOS/Linux 上有或没有系统 WireGuard 命令时打开客户端 | 两种环境均报告内置直连未就绪;客户端不运行系统命令,Go 代理独立可用 | Rust 单测、构建、静态调用核查、真机 | Rust 单测与静态核查通过;真机 Not Run |
|
||||
| AC-01a | FR-01, NFR-01 | 构建固定版本 Go 组件,输入授权路由内/外目标 | 仅支持授权内目标,不输出密钥,打包客户端可定位正确架构组件 | Go 单测、构建和实际出口探测 | Go 单测、macOS arm64 构建、临时授权公网出口与 SOCKS5h 网页请求通过;正式安装包签名 Not Run |
|
||||
| AC-02 | FR-03, NFR-01/02 | 尝试替换组件、传非法路径、控制字符 | 拒绝执行,机密不记录 | 隔离测试、签名检查 | Not Run |
|
||||
| AC-03 | FR-04 | macOS 连接、断开、撤销授权、强制退出及重启 | 真实握手;路由/DNS 保持或恢复 | 两台目标机人工/自动验收 | Not Run |
|
||||
| AC-03a | FR-03/04 | 数据面收到格式错误、过大、符号链接设备记录;模拟全局/分流路由、物理出口与 `utun` 出口;父进程退出或接口意外关闭 | 拒绝错误输入与非物理出口;进程退出并释放接口;接口创建事件不冒充联网完成 | Go 单测、跨平台构建、macOS 故障注入 | 输入与物理出口解析单测、构建通过;macOS 故障注入 Not Run |
|
||||
| AC-04 | FR-05 | Windows/Linux 干净系统安装、拨号、卸载 | 无独立工具安装步骤,未支持平台不假装就绪 | 各平台测试 | Not Run |
|
||||
| AC-05 | FR-06 | 无 `wg-quick`、有拨号用户授权时点击“应用代理” | 自动登记设备并启动 Go 代理;界面展示应用代理地址且整机 VPN 仍未连接;撤销/退出后代理停止 | Rust/Go 单测、macOS 实机检查 | Rust 模拟服务端首次登记/刷新测试通过;Go 私网探测成功,本机代理监听/私网 SOCKS 握手通过;GUI 拨号用户与撤销验收待运行 |
|
||||
| AC-06 | macOS 系统代理扩展 | 私网路由/已有代理拒绝;全局路由下启停、断网、异常退出 | 已占用设置不覆盖;仅恢复自身代理;不把 SOCKS 宣称为所有流量经过 VPN | macOS 真机逐项验收 | Rust 构建/静态状态解析通过;系统设置与公网网页真机验收 Not Run |
|
||||
|
||||
## 6. 发布与恢复
|
||||
|
||||
- 第一阶段仅交付设计与隔离执行层的基础代码,不默认启用内置模式;持续提供现有 `wg-quick` 开发通道。切换正式引擎前收集目标架构及代码签名资料。
|
||||
- 第一阶段关闭系统工具直连,仅交付隔离执行层和 Go 代理;特权网络服务与恢复通过验收后才启用内置直连。切换正式引擎前收集目标架构及代码签名资料。
|
||||
- 2026-10-21 用户明确授权后,使用 0600 临时设备配置将 AllowedIPs 扩为全路由,在服务器仅为 `10.88.0.0/24` 添加临时出口 NAT;Go 数据面实际连接 `223.5.5.5:443` 成功,通过 SOCKS5h 获取 `http://www.baidu.com` 返回 HTTP 200。原始设备记录仍只允许 `10.88.0.0/24`;验证后已删除唯一带 `opsx-vpn-wg4-pilot` 标记的临时 NAT 规则。服务器自身访问 `1.1.1.1:443` 也超时,客户端探测目标改为可达的 `223.5.5.5:443`。这些验证不证明系统 VPN 或路由/DNS 恢复完成。
|
||||
- 2026-10-21 只读复核运行中的 wg4、IPv4 转发与 NAT:IPv4 转发已开启,POSTROUTING 仅有 OpenVPN `10.10.0.0/24` 规则,WireGuard `10.88.0.0/24` 没有持久出口 NAT。全局线路上线需先通过管理后台启用该专线的出口 NAT 并下发经授权的全局 IPv4 路由,再实测浏览器访问;当前不能标为公网验收通过。
|
||||
- 先在独立试验机测试安装与签名,再验证路由/DNS 回滚与握手,然后分批发版;不能仅靠 `cargo check` 或浏览器预览宣称可分发。
|
||||
- 出现网络配置残留时用保存的系统快照恢复并停用有问题的组件;保留旧安装包与开发模式供排障。服务端不变,无数据库回滚。
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
# SDD-004:客户端企业入口与线路选择
|
||||
|
||||
| 字段 | 内容 |
|
||||
|---|---|
|
||||
| 版本 / 级别 | 0.2 / S3(客户端交互、认证配置安全边界与列表展示) |
|
||||
| 设计状态 | 实施中 |
|
||||
| 实现状态 | 客户端企业入口、能力读取、路由标签、实测隧道探测与信号格、WireGuard 认证配置边界和专线列表已实现;WireGuard 浏览器 OIDC 仍未实现 |
|
||||
| 日期 | 2026-10-21 |
|
||||
| 关联需求 | 企业选择、飞书登录入口、模式和节点延迟;WireGuard 认证方式与专线列表操作 |
|
||||
| 批准状态 | 用户提供竞品截图并要求按现有项目继续实现 |
|
||||
|
||||
## 1. 背景、目标与范围
|
||||
|
||||
- 当前 `client/src/App.tsx` 只有服务端地址和密码表单、线路平铺列表。`backend/internal/api/api.go` 的匿名 `/api/client/capabilities` 返回 `oidc: false`;OpenVPN 的 `openvpn-auth-oauth2` 与 WireGuard 客户端会话互不兼容。`backend/internal/api/client.go` 的线路含 `endpoint`,不含节点 RTT;`client_probe` 是经 WireGuard 到固定公网 TCP 目标的诊断。
|
||||
- 目标:以 OpsX 自己的品牌做企业入口、认证选择和节点选择弹窗;用户配置的服务器才是“单位”,不预填竞品单位。只给实测线路显示延迟/颜色和信号格。
|
||||
- 本次不新增身份映射、全局路由覆盖或系统 DNS 修改。登录方式与网络模式必须与真实服务端/本机能力一致。保留已有 HTTP 地址支持、密码/MFA 登录、登录会话、设备私钥和连接状态语义。
|
||||
|
||||
## 2. 需求与约束
|
||||
|
||||
- FR-01:登录页标为“选择接入点”,填写服务端地址并自动带入上次使用的地址;验证真实 `/api/health` 后进入认证界面。切换接入点清除账号口令,不显示历史地址下拉。
|
||||
- FR-02:根据服务端 `/api/client/capabilities` 的 `password/oidc` 能力决定展示入口;`password:false` 不呈现密码表单,`oidc:false` 不呈现飞书入口。`oidc:true` 但客户端浏览器回调未实现时明确禁用,不重定向到 OpenVPN 回调;两者均不可用时提示管理员。未来开启浏览器登录须实现授权码+PKCE、回调一次性交换与稳定 `issuer/sub` 绑定 VPNUser,禁止按邮箱自动授予线路。
|
||||
- FR-03:线路选择展示服务端真实授权线路;极速/全局用线路下发的路由策略判断。未有可切换策略时模式只是当前策略标记,不保存虚构偏好或擅自修改 AllowedIPs。
|
||||
- FR-04:延迟数据必须来自对应线路的实际 Go WireGuard 探针。探测失败/未登记设备时显示不可测,不得用控制面健康请求替代节点 RTT;阈值只用于实测后的色彩映射。
|
||||
- NFR-01:无效授权与离线不应误报已登录、已连接或高质量节点;浏览器预览不调用原生接口。
|
||||
- FR-05:WireGuard 创建和编辑页展示认证方式:现有“客户端账号 + 设备密钥”可选;第三方 OAuth2/OIDC 显示接入说明且不可选。服务端对显式提交未知模式或 oidc 拒绝保存;旧配置无字段保持原行为。静态 Peer 的已有密钥能力不受此选项保护,页面应如实提示。
|
||||
- FR-06:专线列表和详情显示认证方式;列表启动/停止按钮固定可见,并依据实际运行状态切换;其他协议不臆测认证类型。
|
||||
|
||||
## 3. 架构与方案
|
||||
|
||||
前端负责向导和显示;Rust 验证服务端地址、取得 `password/oidc` 能力并调用当前已有的客户端 API;Go 组件执行真实 TCP 出口探测。按用户选择的运行中线路拨号,后端继续按 `VPNUser` 授权隔离。节点列表使用现有 `/api/client/lines`,不造地理位置/服务器名称。OpenVPN OIDC 配置不作为客户端 OIDC 能力来源。
|
||||
|
||||
## 4. 接口、数据与配置契约
|
||||
|
||||
- 匿名 `/api/client/capabilities` 既有 `password/oidc/wireguard` 布尔值;可选 `oidc_provider` 用于将飞书/Lark 与其他 OIDC 提供商区分,旧服务端缺字段按普通“第三方认证”显示。`oidc:false` 时隐藏第三方认证入口,`password:false` 时隐藏密码表单;老服务端返回 HTML 则不进入登录步骤。
|
||||
- `client_probe` 在原结果基础上增加本次成功连接耗时 `latency_ms`(含隧道启动与握手);未成功时不提供延迟。前端只为对应线路显示该值,重新选择单位或重新登录后清除。可选 `latency_probe` 为 VPN 路由内的 IP:端口;未配置时用 `223.5.5.5:443`。线路接口增加由 `AllowedIPs` 推断的 `routing_mode`(`split` / `global_ipv4`);旧服务端缺字段时客户端不推断全局模式。
|
||||
- 不修改 SQLite、设备配置或 WireGuard Peer 授权范围。不新增第三方 logo、虚构地区和用户协议链接。
|
||||
- WireGuard 配置新增可选 `auth_mode=password`;旧记录缺失时按既有客户端账号+设备密钥行为显示。只有 WireGuard 新增服务端枚举校验,`oidc` 不能写入配置;列表从 JSON 配置读取既有 OpenVPN `auth_mode`,未知配置显示未指定。启动/停止使用原有 API。
|
||||
|
||||
## 5. 风险与验收
|
||||
|
||||
| 风险 | 防护 / 恢复 | 可观察信号 |
|
||||
|---|---|---|
|
||||
| 将 OpenVPN OAuth 当 WireGuard 登录 | 飞书入口根据客户端能力禁用;文案区分两者 | `oidc:false` 下无法点击浏览器授权 |
|
||||
| 非法 WireGuard OIDC 配置被误认为安全登录 | 下拉禁选且后端验证拒绝;保留原设备密钥与静态 Peer 流程 | 创建/编辑传 `oidc` 返回校验错误 |
|
||||
| 将 HTTP 或隧道探针误称为节点 RTT | 清楚标注“隧道 TCP 建链耗时”,未测为中性 | 无数据时没有绿色/黄色毫秒值 |
|
||||
| 选择全局模式导致错误路由 | 根据专线真实 AllowedIPs 显示当前策略,未具备切换条件则不可切换 | 不更改系统路由与专线配置 |
|
||||
|
||||
| AC | 对应需求 | 操作与预期 | 验证方式 | 结果 |
|
||||
|---|---|---|---|---|
|
||||
| AC-01 | FR-01 | 新用户配置地址、检测成功、进入认证;切换单位清空表单 | 客户端 UI 构建、桌面窗口启动;真实登录需账号验收 | 构建/启动通过,真实登录待验收 |
|
||||
| AC-02 | FR-02 | `oidc:false` 隐藏第三方入口;`password:false` 隐藏密码入口;`oidc_provider=feishu` 显示飞书标签但保持禁用 | 客户端 UI 构建、能力契约单测;实际账号登录待验收 | 构建与契约单测通过,实际登录待验收 |
|
||||
| AC-03 | FR-03 | 运行中线路可选;全局模式不在私网路由上假激活 | 路由推断 Go 单测、客户端 UI 构建 | 通过 |
|
||||
| AC-04 | FR-04 | 真实探测显示毫秒数;失败显示不可测 | Go/Rust 测试、真机试验 | 测试通过,真机延迟/线路切换待验收 |
|
||||
| AC-05 | FR-05 | WireGuard 创建/编辑显示两种方式,其中 OIDC 不可选;手工请求 `auth_mode=oidc` 被拒;无字段旧配置可验证 | 后台构建、Go 单测 | 通过;界面手工验收待进行 |
|
||||
| AC-06 | FR-06 | 不同协议列表认证标签正确;运行和停止状态有清晰按钮 | 后台构建、代码检查 | 构建通过;界面手工验收待进行 |
|
||||
|
||||
## 6. 发布与恢复
|
||||
|
||||
先验证 Go 服务端再发布前端及桌面客户端;本轮不修改生产网关。若需回退,恢复原服务端/前端构建与桌面客户端构建;新 `auth_mode=password` 对旧服务端为普通附加字段,旧配置仍可读取。无需迁移数据库或撤销现有授权。
|
||||
|
||||
## 7. 待决问题
|
||||
|
||||
正式飞书 OIDC 需要 HTTPS issuer、应用凭据与回调域名,以及管理员确认的飞书 `issuer/sub` 对 VPNUser 授权映射;没有这些配置不得显示可用的一键登录。全局 VPN 需后续系统隧道权限、NAT 与 DNS 恢复验收。
|
||||
@@ -0,0 +1,36 @@
|
||||
# 通过管理 API 创建 WireGuard 专线
|
||||
|
||||
当前后端已经提供 `POST /api/tunnels`,管理后台的“创建专线”向导也调用这个接口;只有 `admin` / `operator` 管理会话有权创建。该接口用于**管理员配置网关**,桌面客户端不能持有管理令牌或直接调用它。创建专线不等于完成终端用户认证、设备授权或桌面拨号。
|
||||
|
||||
## 已创建的停止状态测试专线
|
||||
|
||||
2026-10-21 经用户明确授权,通过 SSH 登录服务器本机管理 API,按下列请求创建测试专线 `opsx-client-pilot`(ID `4`)。创建后再次读取 API 确认 `state: stopped`、`auto_start: false`、监听端口 `51820`、公网端点 `121.4.111.208:51820`,并生成托管测试 Peer `client-1`(`10.88.0.2/32`)。只读系统检查显示 UDP `51820` 未监听、`10.88.0.0/24` 未进入路由表;**没有启动专线,也没有完成云安全组或真实客户端握手测试**。启用前需确认云安全组、业务网段路由和服务器网络策略。`allowed_ips` 只包含测试隧道网段,不会默认接管所有客户端流量。
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "opsx-client-pilot",
|
||||
"protocol": "wireguard",
|
||||
"remark": "桌面客户端标准 WireGuard 测试专线",
|
||||
"auto_start": false,
|
||||
"config": {
|
||||
"address": "10.88.0.1/24",
|
||||
"client_address": "10.88.0.2/32",
|
||||
"listen_port": 51820,
|
||||
"public_endpoint": "121.4.111.208:51820",
|
||||
"allowed_ips": "10.88.0.0/24",
|
||||
"dns": "223.5.5.5",
|
||||
"enable_nat": false,
|
||||
"keepalive": 25
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`POST /api/tunnels` 成功返回专线记录(含 `id`、`state`);设为 `auto_start: false` 时返回 `state: stopped`,不会启动网卡。响应含配置但不应含服务器私钥。失败会返回 `error`,例如地址不合法或端口被占用。服务端创建时会生成首个托管 Peer;这是管理端旧流程,下载的客户端配置包含服务端生成的私钥,**不得分发给未来的桌面设备自注册接口**。创建后公开配置中的旧 `client_address` 字段迁移到 `config.peers[0].client_address`,其值为 `10.88.0.2/32`。
|
||||
|
||||
## 认证、验证与启动边界
|
||||
|
||||
管理 API 登录使用 `POST /api/login`,请求体为 `username`、`password`、按配置需要的 `mfa_code`;返回的 Bearer token 在 `Authorization` 请求头中发送。只通过可信 HTTPS 或已明确授权的加密管理通道提交凭据,不把密码、token 写入仓库、客户端前端代码或命令示例。`http://121.4.111.208:18080` 是明文 HTTP,**不要向公网该地址发送管理员密码或 token**。
|
||||
|
||||
创建前,以管理账号调用 `GET /api/tunnels?protocol=wireguard` 核对既有配置与实际 UDP 端口,并检查网段、系统路由及公网 UDP 放行。创建后用 `GET /api/tunnels/:id` 确认 `state`、`public_endpoint` 和 Peer;需要启用时由管理员单独调用 `POST /api/tunnels/:id/start`,再检查 `GET /api/tunnels/:id/status`、系统 WireGuard 握手和业务路由。`POST /api/tunnels/:id/wireguard/peers` 也是 operator 以上权限,当前实现更新运行中 Peer 会停止再启动隧道,不适合作为桌面设备自动注册接口。
|
||||
|
||||
桌面客户端后续应使用单独的短期用户/设备令牌和客户端生成的公钥,通过专用 API 领取地址、网关公钥、端点与路由;服务器只追加对应 Peer 的公钥,不回传服务器或其他 Peer 的私钥。具体身份映射、撤销和在线更新设计见 [SDD-002](specs/SDD-002-desktop-wireguard-client.md)。
|
||||
Reference in new issue
Block a user