Files
opsx-vpn/docs/specs/SDD-002-desktop-wireguard-client.md
T

119 lines
16 KiB
Markdown
Raw 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.
# SDD-002:opsx-vpn 桌面客户端与 WireGuard 接入评估
| 字段 | 内容 |
|---|---|
| 版本 / 变更级别 | 0.1 / S3(未来涉及设备凭据、系统路由和隧道权限) |
| 设计状态 | Draft |
| 实现状态 | 第一阶段骨架已完成;第二阶段终端用户 API、设备 Peer 登记/撤销与桌面连接代码已集成,尚待目标 OS 真实握手验收 |
| 负责人 / 日期 | 待指定 / 2026-10-21 |
| 关联需求 | 仓库根目录新增 `client/`,Rust + Tauri v2 桌面客户端,优先对接 WireGuard |
| 批准状态 | 用户要求评估并新增客户端;后续接口契约尚待确认 |
## 1. 背景、目标与范围
- **当前行为与证据**:[服务端路由](../../backend/internal/api/api.go)的 `/api/login` 是管理账号登录,`/api/tunnels/:id/wireguard/peers` 的读写与配置下载需要 operator 以上角色;[WireGuard 实现](../../backend/internal/service/wireguard/wireguard.go)可以为管理端生成 Peer 私钥和 `.conf`,新增/删除 Peer 会停止再启动正在运行的隧道。节点 API `/api/nodes` 是管理节点信息,尚不是面向终端用户的可用线路目录。
- **目标**:参考 `../opsxshell` 与 `../opsxapi` 的桌面项目形态,在 `client/` 建立独立 Tauri v2 + Rust + React/TypeScript 项目;第一阶段只做可验证的真实服务健康检查和清晰的接入状态;给出后续控制面/数据面改造顺序。
- **非目标**:第一阶段不使用管理员令牌给终端用户创建 Peer,不自动安装驱动、修改本机 DNS/路由或连接 VPN,不兼容飞连私有协议,不宣称飞书/LDAP 用户已经能领取 WireGuard 设备配置。
- **保留行为**:现有 `frontend/` 管理后台、现有 WireGuard 隧道和已经签发的 Peer 均不改变;服务端 Go 与桌面 Rust 独立构建。
## 2. 需求与约束
- FR-01:桌面端可输入控制面 HTTP 或 HTTPS 根地址,使用 Rust 命令调用现有 `/api/health` 并显示真实结果;HTTP 允许用户保存和检测,但显著提示明文传输风险。
- FR-02:界面将“服务已可达”与“用户已登录/隧道已连接”分开显示;尚无客户端 API 时连接控制不可触发拨号,线路页必须显示不可获取状态,不得展示虚构节点或延时。
- FR-02a:桌面端采用蓝白灰连接概览与窄侧栏;只有真实健康检查可展示控制面往返耗时,且明确不等于 VPN 节点延时。
- FR-02b:托盘明确显示未连接,并提供打开窗口与退出;设置页只读检查本机 PATH 中是否存在 `wg`、`wg-quick`,不得把工具存在等同于可拨号或已连接。
- FR-03(后续):专门的终端用户认证、MFA/OIDC 与短期设备令牌;区分管理员和拨号用户,过期、退出及设备撤销有可验证行为。
- FR-04(后续):由桌面端生成并保管 WireGuard 私钥,只上传公钥;服务端按用户/设备身份分配地址、Peer、路由及 DNS,签发有限时配置并处理回收。
- FR-05(后续):针对 Windows/macOS/Linux 的系统隧道、权限、路由/DNS 回滚和断线恢复逐平台交付;首版仅标准 WireGuard UDP。
- FR-06(后续):终端用户在客户端登录后,客户端自动注册设备并取得该设备专属 WireGuard 参数,无需管理员或用户手动下载、导入 `.conf`;连接按钮只有在系统确认接口、路由、DNS 和握手时才能显示已连接。
- NFR-01:应用不能请求广泛的 shell/filesystem 权限;客户端机密不进入前端源码、日志或仓库。桌面应用与管理 API 使用不同鉴权边界。
- NFR-02:健康检查超时且 TLS 默认验证证书;失败时展示可读原因,不把网络失败当 VPN 连接失败。
## 3. 架构与取舍
```text
React 页面 → Tauri invoke → Rust 命令/服务 → opsx-vpn 控制面 HTTPS
│ 登录/授权/设备与 Peer(后续)
└→ 标准 WireGuard UDP 网关
Rust 系统隧道适配层(后续) ← 仅在本机保存的 WireGuard 私钥
```
- 客户端采用 `client/src` + `client/src-tauri`,参考同族项目的轻量 Tauri v2 壳,不复制其 SSH/HTTP 工作台或许可证逻辑。UI 使用 React + TypeScript;网络请求由 Rust 执行,避免将控制面令牌交给 WebView。
- UI 参考用户提供的蓝白灰截图与 Clash Verge Rev 的桌面交互分层思路;Clash Verge Rev 是 GPL-3.0-only,未复制其源码和视觉资源。概览只使用已保存地址的真实健康检查;线路页在缺少终端用户线路接口时显示不可获取状态,不伪造节点、延时或连接。
- `corplink-rs`、`Client_UI_for_Corplink` 与 PinkD 的 `wireguard-go` 面向飞连私有控制协议;现有 opsx-vpn 使用 Linux `wg`/`wg-quick` 和标准 WireGuard。不要引入 `protocol_version` 等私有扩展或假设它们兼容。SOCKS5/netstack、WireGuard over TCP 另行评估。
- 可选的近期验证路线:由管理员在现有管理端下载独立测试 Peer 配置,再用目标 OS 的标准 WireGuard 客户端验证网关;桌面端的自动拨号必须等设备 API 和权限模型明确后再做。
### Clash Verge Rev 参考范围与接入顺序
已只读核对本地 `../demo/clash-verge-rev` 的 `src-tauri/src/lib.rs`、`ipc.rs`、`feat/tun.rs`、`core/tray/`、`core/service.rs`、`core/updater.rs` 与 Tauri 配置。其 Tauri IPC 类型与命令集中管理、托盘状态、服务可用性检测、配置写入失败恢复以及按平台拆分是可参考的**设计方式**;该项目的系统代理、Mihomo TUN/服务、订阅配置及更新地址都不能直接用于 opsx-vpn。其 GPL-3.0-only 源码、图标和界面资源不复制进当前仓库。
| 顺序 | 借鉴方向 | opsx-vpn 需要自行实现或验证的前置条件 |
|---|---|---|
| 1 | Rust IPC 边界、连接状态模型、线路选择、托盘显示 | 先有独立的终端用户身份/设备授权 API;线路和托盘只能根据实际授权、握手与系统隧道状态宣称“已连接” |
| 2 | 服务可用性检查、失败恢复、跨平台权限管理 | 使用目标系统支持的**标准 WireGuard** 隧道方案,分别验证安装/卸载、DNS/路由保存与恢复、崩溃及重启行为;不以 Mihomo TUN 或系统代理开关替代 WireGuard |
| 3 | 配置存储与更新流程 | 私钥在客户端安全存储,令牌按平台凭据库管理;普通偏好与机密分离。具备签名、可信发布地址和回滚策略后才添加自动更新 |
`corplink-rs` 仅在明确提出**兼容飞连官方服务**且确认私有协议、许可与长期维护成本后作为研究参考,不能作为本项目认证或隧道实现的基础。当前项目的认证对象是 opsx-vpn 自己的 LDAP/飞书用户,需由 opsx-vpn 签发客户端设备身份;飞连企业识别码、Cookie、私有 WireGuard 扩展字段不属于本项目契约。
## 4. 接口、数据与配置契约
- 第一阶段复用 `GET /api/health`,不新增服务端 API、不改变数据库或现有 Peer 数据。
- 桌面原生命令 `get_system_readiness` 返回操作系统和 PATH 中 `wg`、`wg-quick` 文件存在性;不执行这些工具、不申请特权,也不访问 VPN 凭据。托盘当前仅提供未连接静态状态、打开与退出,未来需绑定真实隧道状态机后才能展示连接状态。
- 桌面 `check_server(base_url)` 只返回地址、是否可达及错误,不持久化用户口令或令牌。默认不配置公网地址;用户可填写 HTTP 或 HTTPS 根地址,成功检测后在本机保存并在概览每 30 秒复测;后续认证流程仍推荐可信 HTTPS。
- 后续拟新增独立的客户端 `/api/client/*` 契约:登录/续期、可用线路、设备注册(公钥、设备标识)、设备吊销、连接配置领取及状态。响应不下发服务端私钥,客户端私钥不上传。接口形状、LDAP/飞书绑定与授权范围在实现前另行确定。
- 自动连接的服务端先决条件:建立普通用户身份和 WireGuard 专线之间的明确授权关系,不复用管理后台 `/api/login` 的管理员权限;设备由客户端生成私钥并上传公钥,服务端须原子分配独立 Peer/IP、限制设备数量与授权期限,支持撤销;运行中的 Peer 应增量更新,避免重启其他用户的隧道。随后才向已授权设备提供端点、路由、DNS 与服务端公钥,密钥不得进入 WebView 或日志。
- 首发 macOS 隧道执行层需有权限受限的原生组件,以受控方式创建/关闭隧道并恢复路由和 DNS;`wg`、`wg-quick` 出现在 PATH 不等于具备权限。Windows 和 Linux 各有单独验收,不以 shell 执行任意用户输入配置或要求客户端以管理员账户登录管理 API。
- 配置下载目前包含由服务端保存的客户端私钥,不得将管理员下载接口直接暴露给桌面端;现有管理员 `/api/login` 不等价于终端用户准入。
## 5. 风险与验收
| 风险或失败场景 | 处理方式 | 可观察信号 |
|---|---|---|
| 把管理 token 当拨号凭据 | 另建按用户/设备授权的控制面;第一阶段仅匿名健康检查 | 无管理 API 调用 |
| 设备私钥从管理配置泄漏 | 后续客户端生成密钥、公钥注册及撤销;不直接调用管理下载接口 | 设备 API 与密钥审计 |
| Peer 更新影响现有连接 | 后续用 `wg set` 等增量更新并测试掉线影响,再启用自动注册 | 连接连续性、审计日志 |
| Windows/macOS/Linux 权限差异 | 按平台单独设计特权 helper、安装与回滚;第一阶段不创建 TUN | 平台专项验收 |
| AC | 对应需求 | 前置与操作 | 预期结果 | 验证方式 | 状态 |
|---|---|---|---|---|---|
| AC-01 | FR-01, NFR-02 | 输入有效/无效及远端 HTTP 地址并点检测 | 真实健康结果、成功后保存、错误与拒绝行为明确 | Rust 单测、UI 构建、桌面试连 | Rust 测试与 UI 构建通过;浏览器手动截图显示远端健康检查可达,保存及重开验收待做 |
| AC-02 | FR-02, FR-02a, NFR-01 | 打开概览和线路 | 蓝白灰界面、真实健康检查;无授权接口时显示不可获取线路,不调用管理 API 或修改系统 | UI 构建、代码检查、桌面启动 | UI 构建通过;桌面手动视觉验收待做 |
| AC-02b | FR-02b, NFR-01 | 在桌面端打开托盘并访问设置页 | 托盘显示未连接,能打开/退出;本机工具存在性仅标注为 PATH 检查结果 | 原生构建、桌面手动检查 | UI 构建及 Rust 测试通过,Tauri 开发进程已重启;托盘点击与设置页人工验收待做 |
| AC-03 | FR-03 | 普通用户/MFA/过期令牌/撤销设备 | 仅已授权设备可领取配置 | 后续 API 集成测试 | Not Run |
| AC-04 | FR-04 | 同一用户多个设备注册/注销 | 各设备独立密钥、Peer、地址与审计 | 后续真实网关联调 | Not Run |
| AC-05 | FR-05 | 各 OS 正常连接与异常退出 | 路由/DNS 可恢复,不影响已有连接 | 后续目标 OS 实机验收 | Not Run |
| AC-06 | FR-06 | 新用户首次点击连接并完成登录;退出、撤销与重装再连 | 自动注册并获得专属配置,无需下载文件;只在真实握手后显示连接,撤销后不能重连 | 客户端/服务端集成测试、macOS 实机与网关握手 | Not Run |
## 6. 发布与恢复
- 第一阶段仅新增 `client/`,不修改服务器进程、不申请系统特权;构建检查与桌面手动健康检查后才能称为可运行。
- 2026-10-21 已经管理 API 在现有服务器上创建测试专线 `opsx-client-pilot`(ID 4),配置与早期核验见 [WireGuard API 创建记录](../wireguard-api-create.md)。后续只读复核确认 wg4 已在 UDP 51820 监听,公网测试包可达,但 Peer 握手仍为零;不代表桌面客户端可以认证或连接。
- 后续按“独立客户端身份与专线授权 API → 设备注册/增量 Peer 更新 → macOS 原生隧道实机握手 → Linux/Windows 适配”逐步交付;客户端自动领取配置,下载 `.conf` 仅作为管理员排障功能。2026-10-21 只读核对:试验专线 wg4 已运行,UDP 51820 可从公网抵达服务器,但尚无 Peer 握手,不能据此声称桌面客户端已连接。
- 若后续客户端版本故障,先禁用其设备访问凭据并停用客户端,保留现有管理后台与隧道;数据迁移需独立备份和回滚方案。
## 7. 待决问题
| 问题 | 是否阻塞第一阶段 | 建议 |
|---|---|---|
| 首发 OS 与安装包签名 | 不阻塞骨架 | 先选一台真实目标 OS 验收,再扩平台 |
| 企业账号映射到 VPN 权限、MFA/OIDC 与设备配额 | 阻塞自动拨号 | 稳定用户 ID + 单独设备授权策略,不按同名邮箱猜测 |
| 会话断开是否自动注销 Peer | 阻塞自动拨号 | 区分临时连接与已授权设备;明确租约、续期与撤销 |
| 是否需要代理模式/TCP/飞连兼容 | 不阻塞标准 WireGuard UDP | 先证明原生 WG 客户端与当前网关互通 |
## 8. 第二阶段:终端用户自动接入(2026-10-21)
**实现范围**:新增 `/api/client/login` 及令牌保护的线路、设备登记、配置、状态、撤销接口;管理后台展示设备和撤销入口。登录按 VPNUser 的实际密码与 TOTP 验证,令牌内的授权快照绑定拨号记录 ID 和凭据指纹,后续逐次核对记录仍存在、已启用、凭据未变化。管理员禁用/删除/改密或改变 MFA 时先撤销对应 WireGuard 设备,再注销相关令牌。客户端在 Rust 生成私钥,本机按服务器和用户名隔离设备记录,支持 Linux/macOS `wg-quick` 一键连接;未实现 Windows 系统隧道。
**账号入口与版本检测**:`/users` 是后台管理员,`/vpn-users` 才是客户端拨号身份。客户端先请求匿名 `/api/client/capabilities` 确认服务端支持,再发送密码;旧服务端将未知 API 返回 200 HTML 时明确提示升级。`oidc: false` 只表示 WireGuard 客户端浏览器免密码登录尚未实现,不影响 OpenVPN 独立插件的 OIDC 模式。后续可用浏览器 Authorization Code + PKCE、稳定飞书身份与 VPNUser 授权映射、一次性设备注册授权实现免密码;`openvpn-auth-oauth2` 的 OpenVPN 管理接口不能直接用于 WireGuard。是否有标准 OIDC issuer、回调域名及身份映射规则需接入前确定。
**持久化与兼容**:`AutoMigrate` 新增 `client_devices`、`client_sessions` 表;现有 VPNUser、Tunnel 及老 Peer 不重写。终端令牌仅存 SHA-256 哈希,旧客户端不存在有效令牌;设备记录绑定 VPNUser ID,同名但密码不同的线路不能借授权互访。用户密码/MFA 变更前的 Peer 永久撤销,需要客户端再次登录并重新登记设备。客户端允许 HTTP 控制面地址,但明确显示明文风险;生产建议 HTTPS。
**风险与恢复**:设备增删先协调运行态与磁盘配置;下发失败会尝试清理部分创建的 Peer,清理失败保留设备记录及审计,管理员应核对 `wg show` 和管理设备页。变更前备份 SQLite 数据、WireGuard 专线目录;如迁移或启动失败,回退二进制与数据库备份,不能只回滚二进制并假定新 Peer 自动失效。关闭新客户端 API 或撤销客户端令牌不等于断开已登记 Peer,须同时撤销设备/停止对应专线。Mac 提权调用与本机路由/DNS 回滚须在目标系统实机验收;Windows 暂不支持拨号。
| AC | 对应需求 | 操作与预期 | 当前证据 |
|---|---|---|---|
| AC-03 | FR-03 | 同名用户两线路使用不同密码,旧令牌不得访问另一线路;修改凭据后令牌失效 | 后端回归测试通过,真实 LDAP/TOTP 流程待验 |
| AC-04 | FR-04 | 公钥登记/撤销后持久配置与运行接口一致,旧用户被禁用时撤销 Peer | 后端 Peer 单测通过,运行态故障注入待验 |
| AC-05 | FR-05 | Linux/macOS 授权后连接及断开,路由和 DNS 恢复 | Rust 单测通过;目标系统实机未验 |
| AC-06 | FR-06 | 自动领取配置;握手超时显示重新连接中;更换线路不丢原设备密钥 | Rust 单测和前端构建通过;真实网关握手未验 |