初版功能完成
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
# 检查清单
|
||||
|
||||
> 对应规格书第 18–20 章。每项检查须在对应实现完成后逐条核验;任何一项不通过都视为偏离规格。
|
||||
>
|
||||
> `[x]` 表示代码/静态策略/自动化验证已经完成;Gate 与 T01–T18 仍只接受真实主机证据,保持 `[ ]` 不代表自动化测试失败。
|
||||
|
||||
## 关键技术 Gate
|
||||
|
||||
- [ ] Gate A:wireguard-go 能通过 MuxTun 使用唯一 Wintun,正常 Overlay 通信
|
||||
- [ ] Gate B:Remote Packet 被 MuxTun 截获后,普通 UDP Socket 的外层 Overlay Packet 能通过同一 Wintun 再进入 WireGuard,不死锁、不无限循环
|
||||
- [ ] Gate C:Site 收到 Session Raw IPv4 后能注入 gVisor netstack;TCP/UDP Forwarder 能建立 host socket 到现场目标并完成双向数据搬运
|
||||
- [ ] Gate D:gVisor netstack 能把返回数据重新构造成 Engineer 看到的原始目标 IP 流,并通过 Session UDP 反向封装回 Engineer;Ping Relay 能正确返回 ICMP Echo Reply
|
||||
|
||||
## 架构不可变约束(规格书第 0/20 章)
|
||||
|
||||
- [x] 每个 Engineer/Site 进程只创建一块名为 RemLink 的 Wintun;无第二块 Subnet Adapter、无 Transit CIDR
|
||||
- [x] Windows WireGuard 使用嵌入进程的 wireguard-go;无 WireGuardNT 独立接口
|
||||
- [x] wireguard-go 必须通过 MuxTun 包装唯一 Wintun
|
||||
- [x] v1 不调用 WinNAT、不启用 Windows IP Forwarding,也不修改第三方 NAT
|
||||
- [x] Server 使用 Linux Kernel WireGuard;Go Server 不逐包中转 Overlay
|
||||
- [x] Server Peer AllowedIPs 只放 Node Overlay /32;绝不把现场 Remote CIDR 加进 Server WireGuard AllowedIPs
|
||||
- [x] 所有 Engineer/Site 的 WireGuard 公网 Peer 都只有 Server;无 P2P 代码
|
||||
- [x] 无二层代码(ARP/以太网帧/TAP/Bridge/DCP/LLDP)
|
||||
- [x] PacketMux 只做 IPv4 CIDR 分类,不解析 TCP/UDP/ICMP 或应用层协议
|
||||
- [x] Remote Packet 外层传输使用普通 UDP Socket over Overlay;未手写外层 IP/UDP 协议栈
|
||||
- [x] Remote CIDR 由 Engineer Session 动态下发;Site 配置文件不保存现场网段
|
||||
- [x] 不同 Site 的 Remote CIDR 可完全相同(实现未引入人为限制)
|
||||
- [x] 一个 Engineer 同时只能有一个 Site Session
|
||||
- [x] Site Subnet Gateway 复用 gVisor netstack(固定 commit),无自研 TCP/IP 栈
|
||||
- [x] 无 S7Proxy/ModbusProxy/HTTPProxy/RDPProxy 等应用协议专用模块;PingRelay 是唯一 ICMP 模块
|
||||
- [x] 所有 Windows 网络修改集中在 platform/windows;GUI/业务层无散落 PowerShell
|
||||
- [x] 所有依赖固定版本(Go module/NPM/Wintun DLL/gVisor commit);生产构建无 latest 浮动依赖
|
||||
- [ ] 先完成 Phase 1–7 网络 Gate,再投入完整 GUI
|
||||
- [x] 敏感字段(WG PrivateKey/NodeToken)使用 DPAPI 保护落盘,无明文 YAML 私钥
|
||||
- [x] Engineer/Site YAML 可保存首次注册 Join Token;CLI/环境变量可覆盖 YAML
|
||||
- [x] Wintun DLL go:embed 打入 EXE,Engineer/Site 各自释放到本端 EXE 目录,不使用共享 ProgramData
|
||||
- [x] Engineer、Site、Server 使用三个独立目录和 ZIP,互不包含其他端可执行文件
|
||||
- [x] THIRD_PARTY_NOTICES 随发布包提供(WireGuard/Wintun/gVisor/参考代码许可证)
|
||||
- [x] 绝不自动关闭 Windows Firewall;防火墙规则仅创建带 RemLink 前缀、可识别且可回滚的规则
|
||||
- [x] v1 无 0.0.0.0/0 Exit Node 模式
|
||||
|
||||
## 验收测试(规格书第 19 章)
|
||||
|
||||
- [ ] T01:Engineer 与 Site 都在线,仅有一个 RemLink 虚拟网卡
|
||||
- [ ] T02:Engineer ping Site Overlay IP,通过 Server 中转成功
|
||||
- [ ] T03:Engineer 输入 192.168.13.0/24,本机无冲突,Session 可建立
|
||||
- [ ] T04:Engineer 本地本身是 192.168.13.0/24,建立前拒绝 CIDR_LOCAL_CONFLICT
|
||||
- [ ] T05:Site 对 192.168.13.0/24 无路由,拒绝 SITE_NO_ROUTE
|
||||
- [ ] T06:Site 同时运行 Hyper-V/Docker/已有 WinNAT,Remote Session 仍可正常建立;不读取/修改第三方 NAT
|
||||
- [ ] T07:Engineer ping 现场 PLC,ICMP Echo Relay 成功;延迟可比直接链路略高
|
||||
- [ ] T08:TCP 102 / 502 / HTTP / RDP 通过通用 TCP Relay 正常连接;无应用协议专用代码
|
||||
- [ ] T09:普通单播 UDP 流量通过 UDP flow relay 往返成功;idle flow 可回收
|
||||
- [ ] T10:Session 同时下发 192.168.13.0/24 与 192.168.21.0/24,两网段均可访问
|
||||
- [ ] T11:Engineer-A→Site-A、Engineer-B→Site-B;两 Site 都是 192.168.13.0/24,同时访问成功互不串流
|
||||
- [ ] T12:同 Site 两个 Engineer 访问不同/相同目标设备,SessionID + Overlay IP 隔离正确
|
||||
- [ ] T13:Engineer 尝试再连接第二个 Site,拒绝 ENGINEER_SESSION_EXISTS
|
||||
- [ ] T14:Engineer Wi-Fi 切手机热点,WireGuard/Control 自动恢复
|
||||
- [ ] T15:Server 重启,Node 自动重连;旧 Session CLOSED,用户可重新连接
|
||||
- [ ] T16:Engineer 崩溃后重启,清理残留 Remote Route
|
||||
- [ ] T17:Server 修改某 Node Overlay IP,Node 自动 Re-bootstrap 后使用新 IP
|
||||
- [ ] T18:Server 修改整个 Overlay CIDR,所有 Session 关闭、Nodes 重新分配/配置;Site 无需重建系统 NAT
|
||||
|
||||
## 功能完整性检查
|
||||
|
||||
- [x] Session 状态机七状态(CREATING/PREPARING_SITE/READY/ACTIVE/STOPPING/CLOSED/FAILED)按规格流转
|
||||
- [x] SessionHeader 20 字节头(RMLK/Version/Type/Flags/SessionID/PayloadLen/Reserved)双向对称封装
|
||||
- [x] Session 双向收包校验完整(SessionID/外层源/内层源/内层目 CIDR/IPv4 Total Length)
|
||||
- [x] Session UDP Listener 仅绑定 Overlay 地址,不监听公网或 0.0.0.0
|
||||
- [x] Bootstrap API 三接口 + Admin Web API 八接口按规格实现
|
||||
- [x] Control WebSocket 13 种消息按规格实现
|
||||
- [x] 14 个错误码全量落地(以权威规格实际枚举为准)
|
||||
- [x] SQLite 六张表 + migration 工具化(无散落 CREATE TABLE)
|
||||
- [x] Overlay CIDR 修改七步流程完整实现(含 REBOOTSTRAP_REQUIRED)
|
||||
- [x] Heartbeat 参数按默认值实现(5s 间隔;ONLINE ≤15s / UNSTABLE 15–30s / OFFLINE >30s;退避 1/2/5/10/30s)
|
||||
- [x] Flow limits 默认值(TCP 2048 / UDP 4096 / UDP idle 60s)且可配置
|
||||
- [x] Site PREPARE 路由检查覆盖 DIRECT/ROUTED/DEFAULT_ONLY/NO_ROUTE/OVERLAY_CONFLICT
|
||||
- [x] Engineer 本地冲突检查使用 net/netip prefix overlap(非字符串比较)
|
||||
- [x] Session 统计每 5 秒上报;Upload/Download 按 Engineer 视角定义
|
||||
- [x] 11 个日志模块分类正确;高频 packet 日志默认关闭、DEBUG 禁止逐包打印 Payload
|
||||
- [x] Web UI 五页面(Dashboard/Nodes/Sessions/Network/Logs)功能齐备
|
||||
- [x] Engineer GUI 功能齐备(Site 列表/多 CIDR 输入/冲突预检/Session 详情/断开)
|
||||
- [x] 端口占用符合基线:51820/udp(公网 WG)、8080/tcp(Web+Bootstrap)、7001/tcp(仅 Overlay Control)、6200/udp(仅 Overlay Session)
|
||||
- [x] Docker Compose 只授予 NET_ADMIN(非 privileged);启动 Preflight 检查内核 WireGuard
|
||||
- [x] MuxTun Close 幂等且使全部 goroutine 退出
|
||||
+414
@@ -0,0 +1,414 @@
|
||||
# RemLink v1.0(Userspace Netstack 版)实施规格
|
||||
|
||||
> 权威设计来源:仓库根目录《RemLink_v1.0_技术设计与AI开发规格书_Netstack版.docx》(版本日期 2026-08-19)。本文件将其转化为 spec-driven 开发格式;若实现中发现本文件与 docx 原文有冲突,以 docx 为准并回改本文件。禁止为了让代码跑通而绕开核心约束。
|
||||
|
||||
## 背景与目标
|
||||
|
||||
工业自动化远程调试中,工程师需要在异地直接访问现场设备 IP(如 192.168.13.10 的 PLC、HMI HTTP、RDP),而端口代理需逐个配置、协议适配无法泛化。RemLink 通过中心式 IPv4 L3 虚拟组网 + Engineer 动态下发远程 CIDR + Site 端 gVisor netstack userspace 子网网关,让 Windows 普通 IP 应用无需任何协议适配即可访问现场地址,且允许多个现场使用完全相同的网段。
|
||||
|
||||
## 变更内容
|
||||
|
||||
- 全新构建 RemLink v1.0 三端产品(绿地项目,全部为新增能力):
|
||||
- **Server**:Ubuntu / Docker / Linux Kernel WireGuard + Go(wgctrl 编排)+ SQLite + Vue 3 Web UI
|
||||
- **Engineer**:Windows Wails GUI + wireguard-go(嵌入进程)+ 单一 RemLink Wintun + PacketMux
|
||||
- **Site**:Windows Console + wireguard-go(嵌入进程)+ 单一 RemLink Wintun + gVisor netstack SubnetGateway
|
||||
- 架构基线:**单 Wintun + wireguard-go + PacketMux + gVisor netstack**;不使用 WinNAT、Windows IP Forwarding、Transit CIDR、WireGuardNT
|
||||
- 按 Phase 0–10 分阶段实施,Gate A–D 为关键验收关卡(见 tasks.md)
|
||||
- 仅 IPv4;Remote Subnet v1 支持 TCP / 单播 UDP / ICMP Echo,不实现二层
|
||||
|
||||
## 影响范围
|
||||
|
||||
- 受影响规格:无(首个 change)
|
||||
- 受影响代码:全新 Go monorepo `remlink/`(目录结构见 R15)
|
||||
- 固定版本外部依赖:Wintun DLL、wireguard-go(锁定 commit)、gVisor netstack(锁定 commit)、wgctrl、SQLite driver、Wails、Vue 3 + Vite、golang.org/x/net/icmp
|
||||
|
||||
## 新增需求
|
||||
|
||||
### 需求: R1 总体架构与不可变约束
|
||||
|
||||
系统 必须 采用中心式 Hub-and-Spoke 架构:所有 Engineer/Site 节点只与 Server 建立 WireGuard 连接,数据始终经 Server Hub 中转;节点只配置 Server 公网 IP/端口,不配置彼此公网地址。
|
||||
|
||||
系统 必须 遵守以下不可变决策:
|
||||
|
||||
| 决策项 | v1.0 最终选择 |
|
||||
|---|---|
|
||||
| 虚拟组网 | 中心式 Hub-and-Spoke,无 P2P |
|
||||
| Windows 虚拟网卡 | 每个 Engineer/Site 只有 1 块名为 RemLink 的 Wintun |
|
||||
| Windows WireGuard | wireguard-go 嵌入进程,不用 WireGuardNT |
|
||||
| Server WireGuard | Linux Kernel WireGuard |
|
||||
| 数据层 | IPv4 L3 捕获;Remote Subnet v1 = TCP/UDP/ICMP Echo |
|
||||
| 远程子网 | Engineer 建立 Session 时动态下发;Site 配置不保存现场 CIDR |
|
||||
| Site Subnet Gateway | gVisor netstack userspace relay,不依赖 WinNAT/IP Forwarding |
|
||||
| Engineer 并发 | 一个 Engineer 同时最多连接一个 Site |
|
||||
| Site 并发 | 底层允许多个 Engineer 同时连接同一个 Site |
|
||||
| 现场网段重复 | 允许不同 Site 使用完全相同的 192.168.x.0/24 |
|
||||
| 二层 | 明确不做 ARP/以太网帧/DCP/LLDP/TAP/Bridge |
|
||||
| 服务端网络地址 | Server Web UI 配置;Node IP 由 Server IPAM 自动分配 |
|
||||
|
||||
明确不在 v1 范围:二层协议、P2P/STUN/TURN/UDP 打洞、IPv6、IP 广播/组播、SCTP/GRE/ESP、用户注册/多租户/RBAC/OAuth、HA/Cluster/K8s、自研 TCP/IP 栈/VPN/网卡驱动/NAT、一个 Engineer 同时连多个 Site。
|
||||
|
||||
#### 场景: 两个 Site 使用相同现场网段
|
||||
|
||||
- **当** Site-A 与 Site-B 都使用 192.168.13.0/24 作为现场网段,且分别被 Engineer-A、Engineer-B 通过 Session 访问
|
||||
- **则** 两条 Session 同时工作互不串流;Server WireGuard 不出现路由冲突(Server 只按 Overlay /32 选路,现场 CIDR 位于 Session UDP Payload 内)
|
||||
|
||||
#### 场景: 尝试 P2P 或二层能力
|
||||
|
||||
- **当** 任何实现引入 P2P 直连、打洞、ARP/TAP/Bridge 代码
|
||||
- **则** 违反不可变约束,不被接受
|
||||
|
||||
### 需求: R2 Overlay 虚拟组网与 Server IPAM
|
||||
|
||||
系统 必须 提供 Overlay 虚拟组网与集中式 IPAM:
|
||||
|
||||
- 默认 Overlay CIDR 为 10.88.0.0/16(仅初始值,Server Web UI 必须允许修改);Server 地址默认取该网段第一个可用地址(如 10.88.0.1);Engineer 与 Site 使用统一 Node Pool。
|
||||
- 每个 Node 的地址在 Server WireGuard Peer 上以 /32 AllowedIPs 登记;客户端到 Server 的唯一 Peer 使用整个 Overlay CIDR 作为 AllowedIPs。
|
||||
- Server 是 Overlay 地址唯一权威来源,客户端不得自行填写虚拟 IP。
|
||||
- Node 首次注册时由 IPAM 分配地址,后续重启保持同一地址;地址不得为网络地址、广播地址、Server 地址或已占用地址。
|
||||
- Web UI 可手动修改 Node IP;修改后触发该 Node 重新 Bootstrap/重建 WireGuard。
|
||||
- 删除 Node 后撤销 WireGuard Peer、Node Token,并释放地址。
|
||||
|
||||
修改 Overlay CIDR 时系统 必须 按 7 步流程执行:Web UI 提交并校验新 CIDR → 所有 Active Session 进入 STOPPING/CLOSED → IPAM 为现有 Node 重新分配/迁移地址 → 重建 wg0 地址和 Peer AllowedIPs → 在线 Node 经旧 Control 通道收 REBOOTSTRAP_REQUIRED(通道断则自动走公网 Bootstrap API)→ Windows Node 更新 Adapter IP/路由并重启 wireguard-go → Site 重建 NetworkConfig 与 Session Gateway(不修改 Windows NAT/Forwarding)。
|
||||
|
||||
Node 必须 在应用新 Overlay CIDR 前检查其与本机现有直连网络是否重叠;发现冲突时不得强行启用,向 Server 报告 OVERLAY_LOCAL_CONFLICT。
|
||||
|
||||
#### 场景: Node 地址稳定
|
||||
|
||||
- **当** 已注册 Node 重启后再次 Bootstrap
|
||||
- **则** 获得与之前相同的 Overlay IP
|
||||
|
||||
#### 场景: Node 本地网络与 Overlay 冲突
|
||||
|
||||
- **当** Node 本机直连网络与 Overlay CIDR 重叠
|
||||
- **则** 拒绝启用新配置并上报 OVERLAY_LOCAL_CONFLICT
|
||||
|
||||
### 需求: R3 Windows 单虚拟网卡数据面(MuxTun / PacketMux)
|
||||
|
||||
Engineer 与 Site 必须 都只创建一块名称固定为 RemLink 的 Wintun L3 Adapter(默认 MTU 1280),同时承载 Overlay 节点通信和 Engineer 的 Remote Subnet 路由。不存在第二块 Subnet Adapter,也不存在 Transit CIDR。
|
||||
|
||||
系统 必须 将真实 Wintun 包装为 MuxTun(实现 wireguard-go 的 tun.Device 接口,Read/Write 代理给真实 Wintun)后交给 wireguard-go Device,使 RemLink 能在"Windows 网络栈 ↔ WireGuard 引擎"边界做 L3 分流。MuxTun 必须尽量薄。
|
||||
|
||||
Engineer 出方向 PacketMux 必须 仅根据目标 IPv4 地址分类:
|
||||
|
||||
- 目标属于 Overlay CIDR:原包不修改,直接交给 wireguard-go。
|
||||
- 目标属于当前 Active Session 的任一 Remote CIDR:不交给 wireguard-go,将原始 IPv4 bytes 放入 SubnetSender 的有界队列。
|
||||
- 其他目标:丢弃并做限速日志。
|
||||
|
||||
SubnetSender 必须 使用普通 Go UDP Socket(绑定 Engineer Overlay IP,目标为 Site OverlayIP:6200)发送 `RemLinkHeader + 原始 IPv4 Packet`,由 Windows TCP/IP 生成外层 IP/UDP Header;该外层 UDP Packet 因目标属于 Overlay CIDR 再次进入同一 Wintun,被 PacketMux 判定为 Overlay 流量交给 wireguard-go(防循环:外层目标为 Site Overlay IP,不属于 Remote CIDR,只进入一次 WireGuard)。
|
||||
|
||||
MuxTun 并发要求:
|
||||
|
||||
- PacketMux Read 不得在发送 Remote Packet 时直接阻塞等待 UDP 网络 I/O;使用有界 channel + 独立 Sender goroutine。
|
||||
- 当一次 Wintun batch 全部为 Remote Packet 时,Read 应继续读取,直到能向 wireguard-go 返回至少一个 Overlay Packet 或收到关闭/错误事件,避免返回 0,nil 造成 busy loop。
|
||||
- Wintun Write 由 wireguard-go 入方向和 Site Session 注入共用时,通过统一 Writer/锁序列化。
|
||||
- packet buffer 尽量复用池;先保证正确性,再做 batch 优化。
|
||||
|
||||
#### 场景: Remote 包外层再入 Wintun
|
||||
|
||||
- **当** Engineer 应用访问 192.168.13.10,PacketMux 拦截后由 SubnetSender 以 UDP 发往 Site Overlay IP:6200
|
||||
- **则** 外层 UDP 再次进入同一 Wintun 后被识别为 Overlay 流量交给 wireguard-go,无死锁、无无限循环
|
||||
|
||||
### 需求: R4 Remote Subnet Session
|
||||
|
||||
Session 业务规则:
|
||||
|
||||
- Remote CIDR 由 Engineer 用户在连接 Site 时输入并下发;Site 本地配置不预存现场网段。
|
||||
- 一个 Engineer 同时最多一个处于 CREATING/PREPARING_SITE/ACTIVE 的 Site Session。
|
||||
- 一个 Session 可携带多个 IPv4 CIDR,数据结构从第一天就使用数组。
|
||||
- Site 底层允许同时服务多个 Engineer;通过 SessionID + Engineer Overlay IP 区分。
|
||||
- 两个不同 Site 的 Remote CIDR 可以完全相同。
|
||||
- 目标 CIDR 不得与 RemLink Overlay CIDR 重叠,不得与 Engineer 本地非 RemLink 网络发生任何前缀重叠。
|
||||
- v1 禁止 0.0.0.0/0 Exit Node 模式。
|
||||
|
||||
Session 状态机 必须 为:CREATING → PREPARING_SITE → READY → ACTIVE → STOPPING → CLOSED,以及 FAILED(任一关键检查失败)。
|
||||
|
||||
Session 建立顺序 必须 为:Engineer GUI 选择 Site 并输入 CIDR → Engineer 本地冲突检查(失败不请求 Server)→ CREATE_SESSION → Server 校验(已有 Session/Site 在线/CIDR 合法性)→ Server 生成随机 64-bit SessionID、状态 PREPARING_SITE、向 Site 下发 PREPARE_SESSION → Site 对每个 CIDR 做 Windows Route Lookup 并检查 SubnetGateway 能力与 flow capacity → Site 返回 PREPARE_RESULT(含 subnet_gateway=netstack)→ Server 向 Engineer 返回 SESSION_CONFIG(SessionID、Site Overlay IP、Remote CIDRs、MTU、Session UDP Port)→ Engineer 添加 Windows Remote Routes 并启动 SessionTransport → Engineer 发送 ROUTES_READY → Server 置 ACTIVE 并通知 Site。
|
||||
|
||||
Session 数据包格式(两个方向统一)SHALL 为:
|
||||
|
||||
| 字段 | 长度 | 说明 |
|
||||
|---|---|---|
|
||||
| Magic | 4 bytes | ASCII: RMLK |
|
||||
| Version | 1 byte | v1 = 1 |
|
||||
| Type | 1 byte | v1 固定 0x01 = IPv4 |
|
||||
| Flags | 2 bytes | v1 置 0 |
|
||||
| SessionID | 8 bytes | Server 生成的 uint64 |
|
||||
| PayloadLen | 2 bytes | 原始 IPv4 Packet 长度 |
|
||||
| Reserved | 2 bytes | 置 0 |
|
||||
| Payload | N bytes | 完整原始 IPv4 Packet |
|
||||
|
||||
不加入 TCP 风格序号/ACK/重传/拥塞控制;不做 TCP-over-TCP。WireGuard 已提供外层完整性和加密,不再增加自研加密或可靠 UDP。
|
||||
|
||||
Session 双向收包校验 必须 满足:
|
||||
|
||||
- Engineer 与 Site 都只在自己的 OverlayIP:6200 上监听 Session UDP,不监听公网地址或 0.0.0.0。
|
||||
- 根据 SessionID 查找 Active Session。
|
||||
- UDP 外层源 Overlay IP 必须等于该 Session 对端绑定的 Overlay IP(双向都校验)。
|
||||
- Engineer→Site 方向内层 IPv4 Source 默认必须等于 Engineer Overlay IP(应用显式绑定其他物理源地址的流量不保证可用)。
|
||||
- Engineer→Site:内层 Destination 必须属于 Session Remote CIDR;Site→Engineer:内层 Source 必须属于 Session Remote CIDR,Destination 必须等于 Engineer Overlay IP。
|
||||
- IPv4 Total Length、版本和实际 Payload 长度必须一致。
|
||||
- 校验失败直接丢弃并做限速安全日志。
|
||||
|
||||
#### 场景: Engineer 已有 Active Session 再连第二个 Site
|
||||
|
||||
- **当** Engineer 在 ACTIVE Session 期间请求连接另一个 Site
|
||||
- **则** 拒绝并返回 ENGINEER_SESSION_EXISTS
|
||||
|
||||
#### 场景: 目标 CIDR 与本地网络冲突
|
||||
|
||||
- **当** Engineer 本机存在 192.168.0.0/16,用户输入 192.168.13.0/24
|
||||
- **则** 本地冲突检查失败(前缀重叠),不向 Server 发起请求
|
||||
|
||||
### 需求: R5 Site Userspace Subnet Gateway(gVisor netstack)
|
||||
|
||||
Site 收到 Session UDP 后 必须 取出原始 IPv4 Packet,直接交给 gVisor netstack SubnetGateway(通过 channel.Endpoint.InjectInbound),不注入 Windows Wintun 做内核转发。gVisor netstack 负责 TCP/UDP 连接状态和报文重建;RemLink 不实现 TCP 状态机。现场侧实际出站使用 Windows 普通 host socket(net.Dial / UDPConn),目标设备看到的源地址是 Site 的现场可达地址。
|
||||
|
||||
- v1 不创建 Transit CIDR,不创建 RemLinkNAT,不要求 PLC/网关认识 Overlay 网段。
|
||||
- 能力边界:v1 明确支持 TCP、单播 UDP 和 ICMP Echo(Ping);IP 广播/组播、SCTP、GRE、ESP 及其他非 TCP/UDP IP 协议不在保证范围。
|
||||
- SubnetGateway 接口:
|
||||
|
||||
```go
|
||||
type SubnetGateway interface {
|
||||
Prepare(ctx context.Context, cfg SessionConfig) error
|
||||
InjectIPv4(ctx context.Context, sessionID uint64, packet []byte) error
|
||||
CloseSession(ctx context.Context, sessionID uint64) error
|
||||
}
|
||||
// v1 default / only backend: GVisorNetstackBackend
|
||||
```
|
||||
|
||||
- 优先直接依赖 upstream gVisor netstack 并固定 commit;可参考 Tailscale wgengine/netstack(BSD-3-Clause)实现模式,但不得引入整个 Tailscale 控制面。未来 Kernel/NAT Backend 必须保持接口不变。
|
||||
- TCP Relay:gVisor netstack 接收 Engineer 的 SYN,创建 Engineer-facing TCP endpoint;RemLink 使用 host net.Dial("tcp", target) 建立 Site→PLC 连接,以 io.CopyBuffer 双向搬运字节;不做应用协议识别。
|
||||
- UDP Relay:按 SessionID + Engineer 源/目标五元组维护轻量 flow mapping;每个 flow 使用 host UDPConn 与现场目标通信,收到回复后写回 gVisor UDP endpoint;flow 使用可配置 idle timeout 做 GC。
|
||||
- 返回包必须使用 Session 封装(对称封装,v1 强制设计):gVisor netstack 生成面向 Engineer 的 IPv4 响应包(SRC=现场 IP → DST=Engineer Overlay IP)后,Site 必须将其再封装成 Session UDP(Outer SRC=Site Overlay IP,Outer DST=Engineer Overlay IP,Payload=SessionHeader+RawIPv4);不得把该 Raw Packet 直接交给 WireGuard(Server 对 Site Peer 的 AllowedIPs 只有 /32,源地址会被 cryptokey routing 拒绝)。Engineer SessionListener 校验后将 Raw IPv4 写入本机 Wintun 入站方向;Windows 最终看到 SRC=现场 IP、DST=Engineer Overlay IP。
|
||||
- ICMP Echo Relay:v1 只支持 Echo Request/Reply;收到 Echo Request 后由 PingRelay 使用成熟 ICMP 库或受控系统 ping 从 Site 探测目标;目标响应后构造与原请求 ID/Sequence 对应的 Echo Reply Raw IPv4 经 SessionTransport 返回 Engineer;其他 ICMP 类型不支持。
|
||||
- Site PREPARE 时 必须 对每个目标 CIDR 做 Windows Route Lookup:DIRECT(直连)允许;ROUTED(明确静态/动态路由)允许;DEFAULT_ONLY(仅默认路由)默认拒绝;NO_ROUTE 拒绝(SITE_NO_ROUTE);OVERLAY_CONFLICT 拒绝。
|
||||
- 建议初始 flow 上限(均可配置):TCP 2048 flows、UDP 4096 flows、UDP idle timeout 60s。
|
||||
|
||||
#### 场景: 现场设备返回路径
|
||||
|
||||
- **当** PLC 192.168.13.10 响应 Site host socket 的连接
|
||||
- **则** Site 经 gVisor netstack 生成面向 Engineer 的 Raw IPv4,再以 SessionHeader 封装经 UDP 发往 Engineer Overlay IP;PLC 无需任何 Overlay 返回路由
|
||||
|
||||
### 需求: R6 控制面与节点协议
|
||||
|
||||
系统 必须 分离 Bootstrap 与正常 Control:
|
||||
|
||||
- Bootstrap(公网 HTTP):`http://SERVER_IP:8080/api/v1/bootstrap/...`
|
||||
- WireGuard:`SERVER_IP:51820/udp`
|
||||
- Control(仅 Overlay):`ws://10.88.0.1:7001/control`
|
||||
|
||||
节点第一次启动无 WireGuard 时使用公网 Bootstrap API 获取 Overlay IP、Server WireGuard PublicKey 和 Endpoint;建立 WireGuard 后所有控制消息走 Overlay 内 Control WebSocket。
|
||||
|
||||
Node 身份 必须 包含:NodeID(UUID,首次运行生成并持久化)、NodeType(engineer/site)、NodeName、WireGuard PrivateKey(本地生成,仅本机保存)、WireGuard PublicKey(注册时提交 Server)、NodeToken(Server 注册成功后生成,用于后续身份验证;不是用户系统)。
|
||||
|
||||
Windows 本地敏感字段(WG PrivateKey、NodeToken)SHALL 优先使用 Windows DPAPI 保护后落盘,不以明文 YAML 保存私钥;为简化自用部署,首次注册 Join Token 可以明文保存在 Engineer/Site YAML 中。Server PrivateKey 保存到数据目录并设置严格文件权限。
|
||||
|
||||
Server 必须 维护可轮换 Join Token:新 Node 注册必须提供 Join Token;注册成功后改用 NodeToken。v1 不实现 HTTPS,公网 Bootstrap/管理 HTTP 的机密性不由 TLS 提供,作为部署边界在文档中说明。
|
||||
|
||||
Heartbeat 参数(默认值):interval 5s;ONLINE=最近心跳 ≤15s;UNSTABLE=15–30s;OFFLINE=>30s;Reconnect backoff=1s,2s,5s,10s,30s 上限。
|
||||
|
||||
### 需求: R7 Server 设计
|
||||
|
||||
Server 必须 承担:Bootstrap API 和 Web UI、IPAM/Node Registry/Node Token、Linux Kernel WireGuard interface 和 Peer 编排(wgctrl)、Control WebSocket Hub、Session Manager、节点/Session 统计聚合、SQLite 持久化和日志查询;不逐包处理 WireGuard Overlay 数据。
|
||||
|
||||
Server wg0 必须 配置 Address 10.88.0.1/16(随 Overlay CIDR)、ListenPort 51820;每个 Node Peer 的 AllowedIPs 为其 Overlay /32。Linux 必须启用 IPv4 forwarding 并允许 wg0→wg0 转发;Server 不对 Overlay 做 SNAT。v1 不同时实现 kernel WireGuard 与 wireguard-go 两套 Server Backend。
|
||||
|
||||
Docker Compose 基线 必须 只授予 NET_ADMIN(不用 privileged: true),映射 /dev/net/tun、51820/udp、8080/tcp,挂载 ./data:/app/data,sysctls net.ipv4.ip_forward=1。镜像启动 Preflight 必须检查内核 WireGuard 能否创建接口;失败时明确报错并退出,不静默切换。
|
||||
|
||||
Web UI 必须 提供五个页面:
|
||||
|
||||
| 页面 | 核心内容 |
|
||||
|---|---|
|
||||
| Dashboard | Uptime、Overlay CIDR、在线 Engineer/Site、Active Session、流量 |
|
||||
| Nodes | 名称、类型、Overlay IP、WG PublicKey 摘要、Handshake、App 状态、版本、LastSeen |
|
||||
| Sessions | Engineer、Site、Remote CIDRs、状态、上下行流量、持续时间、强制断开 |
|
||||
| Network | Overlay CIDR、Server IP、WG Port、Join Token 轮换 |
|
||||
| Logs | 按时间/级别/模块/Node/Session 过滤事件 |
|
||||
|
||||
### 需求: R8 Engineer 设计
|
||||
|
||||
Engineer 进程(RemLinkEngineer.exe)SHALL 包含:Wails GUI、Bootstrap/Control Client、Node Identity Store、Wintun Adapter Manager、MuxTun/PacketMux、wireguard-go Device、Session Manager、SessionTransport(UDP Sender + Listener)、RouteManager、Stats、Logging。
|
||||
|
||||
GUI 必须 提供:Server 公网地址/Overlay 状态/本机虚拟 IP/Control 状态/版本显示;全部 Site 列表(Name、Overlay IP、Online、RemoteSubnetCapability、LastSeen);选择 Site 后输入一个或多个 CIDR;连接前执行本地 CIDR 冲突检查;连接后显示 SessionID、目标 Site、CIDR、上传/下载、包数、时延和日志;Active Session 时禁止再选择第二个 Site。
|
||||
|
||||
Session READY 后 Engineer 必须 为每个 Remote CIDR 增加指向 RemLink Adapter 的 Windows 路由,由 RouteManager 统一创建并带 RemLink ownership metadata/本地状态记录以便异常恢复。不得通过降低 Metric 强抢冲突路由;发现 Remote CIDR 与本机现有非 RemLink 直连/静态/VPN 前缀重叠时直接拒绝建立 Session。
|
||||
|
||||
应用显式绑定网卡的情况:普通 Socket 由 Windows 根据 Remote Route 选择 RemLink Adapter;显式绑定物理网卡或固定源 IP 的工业软件不完全透明,应在其网络接口选择中选择 RemLink Adapter;v1 不增加源地址改写。
|
||||
|
||||
### 需求: R9 Site 设计
|
||||
|
||||
Site 进程(RemLinkSite.exe,Console)SHALL 包含:Bootstrap/Control Client、Node Identity Store、Wintun Adapter Manager、MuxTun/PacketMux、wireguard-go Device、UDP Session Listener :6200(Overlay only)、gVisor Netstack SubnetGateway、TCP/UDP/Ping Relay、Route Inspector、Flow Manager/Stats、Logging。
|
||||
|
||||
Site 启动能力检测 必须 按 7 步执行:加载 Node Identity 并获取 Server NetworkConfig → 检查 Overlay CIDR 与本地直连网络冲突 → 创建/复用 RemLink Wintun 并配置 Overlay IP/Prefix/MTU → 启动 wireguard-go 并等待 Server Overlay 可达 → 初始化 gVisor netstack SubnetGateway(TCP/UDP forwarder 与 flow limits;无需检查或修改 WinNAT)→ 启动仅绑定 OverlayIP:6200 的 Session UDP Listener(同一端口承载双向)→ 连接 Control WebSocket 并报告能力、OS、版本、netstack 状态与 flow capacity。
|
||||
|
||||
Console 输出 必须 展示 Server、Node、Overlay IP、WireGuard/Control/Remote Subnet/SubnetGateway 状态及 SESSION/ROUTE 事件。
|
||||
|
||||
### 需求: R10 数据库与数据模型
|
||||
|
||||
Server 必须 使用单 SQLite 数据库文件(如 /app/data/remlink.db),database/sql + 稳定 SQLite Driver,迁移采用成熟 migration 工具或嵌入 SQL migration 文件,不在代码中散落 CREATE TABLE。
|
||||
|
||||
核心表 必须 包含:
|
||||
|
||||
| 表 | 关键字段 |
|
||||
|---|---|
|
||||
| settings | key, value, updated_at |
|
||||
| nodes | node_id, type, name, overlay_ip, wg_public_key, node_token_hash, status, version, os_version, last_seen |
|
||||
| sessions | session_id, engineer_node_id, site_node_id, status, created_at, active_at, closed_at, error_code |
|
||||
| session_cidrs | session_id, cidr |
|
||||
| session_stats | session_id, tx_bytes, rx_bytes, tx_packets, rx_packets, updated_at |
|
||||
| event_logs | time, level, module, node_id, session_id, message, fields_json |
|
||||
|
||||
Windows Node 不使用 Server SQLite;Engineer 与 Site 必须作为完全独立的便携式包发布,持久状态放在各自 EXE 所在目录,且不得共用运行文件。敏感字段用 DPAPI,至少保存 NodeID、NodeToken、WireGuard PrivateKey、Server URL、NodeName、最后一次 NetworkConfig 版本,以及 RemLink 创建过的路由状态用于 Reconcile。
|
||||
|
||||
### 需求: R11 API 与消息定义
|
||||
|
||||
Public Bootstrap API 必须 提供:GET /api/v1/server/info(Server ID、版本、WG Endpoint、Bootstrap 信息)、POST /api/v1/bootstrap/register(首次 Node 注册)、POST /api/v1/bootstrap/config(已注册 Node 用 NodeID+NodeToken 拉取最新 NetworkConfig)。
|
||||
|
||||
Admin Web API 必须 提供:GET /api/v1/admin/nodes;PATCH /api/v1/admin/nodes/{id}(改名/改 Overlay IP);DELETE /api/v1/admin/nodes/{id}(撤销 Node);GET /api/v1/admin/sessions;POST /api/v1/admin/sessions/{id}/disconnect(强制断开);GET /api/v1/admin/network;PUT /api/v1/admin/network;GET /api/v1/admin/logs。v1 不做用户系统;如需保护仅实现可选的单一 Admin Token,不设计账户/角色体系。
|
||||
|
||||
Control WebSocket 消息 必须 包含:
|
||||
|
||||
| Type | 方向 | 关键字段 |
|
||||
|---|---|---|
|
||||
| HELLO | Node→Server | node_id, node_token, config_version, capabilities |
|
||||
| WELCOME | Server→Node | server_time, network_config_version |
|
||||
| NODE_LIST | Server→Engineer | sites[] |
|
||||
| CREATE_SESSION | Engineer→Server | site_node_id, target_cidrs[] |
|
||||
| PREPARE_SESSION | Server→Site | session_id, engineer_overlay_ip, target_cidrs[] |
|
||||
| PREPARE_RESULT | Site→Server | ok, route_results[], subnet_gateway_status, tcp_capacity, udp_capacity, error |
|
||||
| SESSION_CONFIG | Server→Engineer | session_id, peer_overlay_ip, cidrs[], mtu, udp_port |
|
||||
| ROUTES_READY | Engineer→Server | session_id |
|
||||
| SESSION_ACTIVE | Server→Engineer/Site | session_id |
|
||||
| STOP_SESSION | 任意→Server / Server→Node | session_id, reason |
|
||||
| SESSION_STATS | Engineer/Site→Server | cumulative counters |
|
||||
| HEARTBEAT | 双向 | timestamp/status |
|
||||
| REBOOTSTRAP_REQUIRED | Server→Node | config_version, reason |
|
||||
|
||||
错误码基线 必须 包含:SERVER_UNREACHABLE、JOIN_TOKEN_INVALID、NODE_AUTH_FAILED、OVERLAY_LOCAL_CONFLICT、ENGINEER_SESSION_EXISTS、SITE_OFFLINE、CIDR_INVALID、CIDR_LOCAL_CONFLICT、CIDR_OVERLAY_CONFLICT、SITE_NO_ROUTE、NETSTACK_UNAVAILABLE、FLOW_LIMIT_REACHED、SESSION_TIMEOUT、SESSION_INJECT_FAILED。
|
||||
|
||||
### 需求: R12 路由冲突、异常与恢复
|
||||
|
||||
Engineer 本地 CIDR 冲突算法 必须 使用 net/netip 做真正的 Prefix overlap(不使用字符串比较):收集 Windows 有效路由和所有非 RemLink 直连前缀;默认路由 0.0.0.0/0 不作为冲突依据;任何更具体的现有本地/VPN 路由只要与 Remote CIDR 有重叠即拒绝。
|
||||
|
||||
Reconcile 规则:
|
||||
|
||||
- Engineer 启动时检查 RemLink Adapter 上由自身记录创建但没有对应 Active Session 的 Remote Routes,删除残留。
|
||||
- Site 启动时初始化/重建 userspace SubnetGateway;不检查、不创建、不删除任何 Windows NAT。
|
||||
- Site 清理上次异常退出遗留的 userspace flow/session 状态;Windows 系统路由、NAT、Forwarding 不需要恢复。
|
||||
- Wintun Adapter 可复用,不要求每次退出删除;卸载/显式清理时再删除。
|
||||
|
||||
Server 重启后所有 Remote Session 必须 直接标记 CLOSED,不实现透明 Session Resume;Node 的 wireguard-go/Control 自动重连,Engineer 用户重新点击连接,Site userspace flow 随 Session 关闭并清理。
|
||||
|
||||
网络切换(Wi-Fi→网线/热点)时由 WireGuard 正常 roaming/重新握手处理;客户端只配置 Server 固定 Endpoint,Server Peer Endpoint 由 WireGuard 根据握手学习;Control WebSocket 断线使用指数退避重连。
|
||||
|
||||
### 需求: R13 日志、统计与可观测性
|
||||
|
||||
日志模块 必须 分为:CORE、BOOTSTRAP、WG、IPAM、CONTROL、SESSION、ROUTE、NETSTACK、TUN、SUBNET、SYSTEM。高频 packet 日志默认关闭;DEBUG 也禁止逐包打印 Payload,只允许限速采样(防止 PLC 下载/RDP 时写满磁盘)。
|
||||
|
||||
Session 流量统计 必须 以 Engineer 视角定义:Upload=Engineer→Site LAN,Download=Site LAN→Engineer;Engineer PacketMux 在拦截 Remote 出包时累计上传,Engineer SessionListener 在校验并注入 Remote Reply 前累计下载;每 5 秒向 Server 上报累计值。Server 不为了统计进入 WireGuard packet path;节点层 WireGuard 总流量可由 wgctrl 读取作为辅助指标。
|
||||
|
||||
### 需求: R14 安全边界
|
||||
|
||||
- WireGuard 负责公网数据通道加密、认证和完整性;RemLink 不重复实现。
|
||||
- 每个 Node 独立 WireGuard KeyPair;PrivateKey 不上传 Server。
|
||||
- Server WireGuard Peer 只允许该 Node 的 /32 Overlay IP,防止节点伪造其他 Overlay Source。
|
||||
- Engineer 与 Site 的 Session UDP Listener 都只绑定 Overlay 地址,并按方向校验 SessionID、Outer Source、Inner Source/Target CIDR、Inner Destination。
|
||||
- Join Token 仅用于首次加入;NodeToken 用于应用层身份。
|
||||
- v1 按需求不实现 HTTPS;公网暴露时可由外部反向代理/防火墙补充,但不是 v1 内核功能。
|
||||
- 绝不自动关闭 Windows Firewall;如需要规则,只创建带 RemLink 前缀、可识别且可回滚的规则。
|
||||
|
||||
### 需求: R15 项目结构、关键接口与依赖管理
|
||||
|
||||
Monorepo 结构 必须 为:
|
||||
|
||||
```
|
||||
remlink/
|
||||
├─ cmd/
|
||||
│ ├─ server/
|
||||
│ ├─ engineer/
|
||||
│ └─ site/
|
||||
├─ internal/
|
||||
│ ├─ model/
|
||||
│ ├─ protocol/
|
||||
│ ├─ config/
|
||||
│ ├─ identity/
|
||||
│ ├─ ipam/
|
||||
│ ├─ control/
|
||||
│ ├─ session/
|
||||
│ ├─ subnet/
|
||||
│ │ ├─ header.go
|
||||
│ │ ├─ sender.go
|
||||
│ │ ├─ listener.go
|
||||
│ │ └─ validator.go
|
||||
│ ├─ overlay/
|
||||
│ │ ├─ clientwg/
|
||||
│ │ │ ├─ device.go
|
||||
│ │ │ ├─ muxtun.go
|
||||
│ │ │ └─ adapter.go
|
||||
│ │ └─ serverwg/
|
||||
│ ├─ platform/
|
||||
│ │ ├─ windows/
|
||||
│ │ │ ├─ route/
|
||||
│ │ │ ├─ netinfo/
|
||||
│ │ │ ├─ socket/
|
||||
│ │ │ └─ dpapi/
|
||||
│ │ └─ linux/
|
||||
│ │ └─ netlink/
|
||||
│ ├─ subnetgateway/
|
||||
│ │ ├─ netstack/
|
||||
│ │ ├─ tcprelay/
|
||||
│ │ ├─ udprelay/
|
||||
│ │ └─ pingrelay/
|
||||
│ ├─ database/
|
||||
│ ├─ logging/
|
||||
│ └─ stats/
|
||||
├─ frontend/
|
||||
│ ├─ server/
|
||||
│ └─ engineer/
|
||||
├─ deploy/docker/
|
||||
└─ docs/
|
||||
```
|
||||
|
||||
关键接口 必须 为:
|
||||
|
||||
```go
|
||||
type RouteManager interface {
|
||||
AddRemote(prefix netip.Prefix, ifIndex uint32) error
|
||||
RemoveRemote(prefix netip.Prefix) error
|
||||
Conflicts(prefix netip.Prefix) ([]RouteConflict, error)
|
||||
Lookup(dst netip.Addr) (RouteInfo, error)
|
||||
}
|
||||
|
||||
type SubnetGateway interface {
|
||||
Prepare(ctx context.Context, cfg SessionConfig) error
|
||||
InjectIPv4(ctx context.Context, sessionID uint64, packet []byte) error
|
||||
CloseSession(ctx context.Context, sessionID uint64) error
|
||||
}
|
||||
|
||||
type SessionTransport interface {
|
||||
SendIPv4(sessionID uint64, peer netip.Addr, packet []byte) error
|
||||
}
|
||||
```
|
||||
|
||||
MuxTun 约束:必须实现所固定 wireguard-go 版本的 tun.Device 接口,go.mod 锁定 wireguard-go commit/version,升级前先跑 MuxTun Windows POC;base Device 的 Name/MTU/Events/BatchSize 按原语义代理;Read 方向负责 OS→WireGuard 分类;Write 方向负责 WireGuard→Windows 原样写入(Engineer SessionReceiver 的 Remote Reply 也通过统一 Wintun inbound writer 注入;Site netstack 数据不注入 Windows Wintun);Close 必须可幂等,并使所有 goroutine 退出。
|
||||
|
||||
依赖管理:所有 Go module、NPM package、Wintun DLL、gVisor commit 均固定版本/commit,禁止生产构建使用 latest 浮动依赖(gVisor netstack API 不保证稳定,升级必须经过 POC);Wintun DLL 使用 go:embed 打入 EXE,首次运行释放到当前角色 EXE 所在目录,不得通过 `%ProgramData%` 在 Engineer 与 Site 之间共享;发布包提供 THIRD_PARTY_NOTICES(WireGuard/Wintun/gVisor 及参考复用代码许可证,如直接采用 Tailscale 代码片段遵循其 BSD-3-Clause);Windows Engineer 与 Site 都要求管理员权限(创建 Wintun、配置接口/路由;Site v1 不修改 WinNAT 或 IP Forwarding)。
|
||||
|
||||
### 需求: R16 阶段化开发与验收
|
||||
|
||||
开发 必须 按 Phase 0–10 顺序执行(详见 tasks.md),每阶段通过明确验收后才进入下一阶段;先完成 Phase 1–7 网络 Gate,再投入完整 GUI。Gate A–D 为关键关卡:
|
||||
|
||||
- Gate A:wireguard-go 能通过 MuxTun 使用唯一 Wintun,正常 Overlay 通信。
|
||||
- Gate B:Remote Packet 被 MuxTun 截获后,普通 UDP Socket 的外层 Overlay Packet 能通过同一 Wintun 再进入 WireGuard,不死锁、不无限循环。
|
||||
- Gate C:Site 收到 Session Raw IPv4 后能注入 gVisor netstack;TCP/UDP Forwarder 能建立 host socket 到现场目标并完成双向数据搬运。
|
||||
- Gate D:gVisor netstack 能把返回数据重新构造成 Engineer 看到的原始目标 IP 流,并通过 Session UDP 反向封装回 Engineer;Ping Relay 能正确返回 ICMP Echo Reply。
|
||||
|
||||
最终验收 必须 通过规格书第 19 章 T01–T18 全部场景(详见 checklist.md)。测试 TCP/102、TCP/502、HTTP、RDP、ICMP Echo、UDP 的目的是证明通用 TCP/UDP/Ping userspace gateway 成立;生产代码中禁止出现 S7Proxy、ModbusProxy、HTTPProxy 等应用协议专用模块;PingRelay 是唯一明确的 ICMP Echo 诊断模块。
|
||||
|
||||
## 修改需求
|
||||
|
||||
无(绿地项目)。
|
||||
|
||||
## 删除需求
|
||||
|
||||
无(绿地项目)。
|
||||
@@ -0,0 +1,90 @@
|
||||
# 开发任务
|
||||
|
||||
> 对应规格书第 18 章分阶段开发计划。执行方式:不要一次性写完整个项目;网络数据面 POC 先于完整 GUI;每一阶段通过明确验收后再进入下一阶段。任何"为了能跑"而改变核心路由模型的实现必须先修改 spec,不允许隐藏偏离。
|
||||
|
||||
- [x] Phase 0: 仓库与基础模型
|
||||
- [x] 建立 Go monorepo 目录结构(cmd/server、cmd/engineer、cmd/site、internal/*、frontend/、deploy/docker/、docs/)
|
||||
- [x] internal/logging:slog + 成熟滚动文件库,11 个模块分类(CORE/BOOTSTRAP/WG/IPAM/CONTROL/SESSION/ROUTE/NETSTACK/TUN/SUBNET/SYSTEM),高频 packet 日志默认关闭
|
||||
- [x] internal/config:YAML 配置加载(Engineer/Site 支持首次注册 Join Token 便捷配置,参考规格书附录 B)
|
||||
- [x] internal/protocol:SessionHeader(RMLK,20 字节头)编解码、Control WebSocket 消息类型定义、14 个错误码常量(以权威规格实际枚举为准)
|
||||
- [x] CI 基础:go build / go test / go vet;不写 GUI
|
||||
- [x] 验收:monorepo 可构建,协议模型单元测试通过
|
||||
|
||||
- [ ] Phase 1: 单 Wintun + wireguard-go POC(Gate A)
|
||||
- [x] internal/platform/windows:Wintun Adapter Manager(创建/复用名为 RemLink 的 Wintun,配置 Overlay IP/Prefix/MTU 1280)
|
||||
- [x] internal/overlay/clientwg/muxtun.go:MuxTun 实现 tun.Device(本阶段为直通模式,Read/Write 代理真实 Wintun,Name/MTU/Events/BatchSize 按原语义代理,Close 幂等)
|
||||
- [x] internal/overlay/clientwg/device.go + adapter.go:wireguard-go Device 集成(go.mod 锁定 wireguard-go commit)
|
||||
- [x] Wintun DLL go:embed 并首次运行释放到 Engineer/Site 各自 EXE 所在目录
|
||||
- [x] 搭建手动配置的测试 Server wg0(POC 配置与操作手册已提供;真实主机执行计入 Gate A)
|
||||
- [ ] 验收(Gate A):两个 Windows 节点通过 Server ping 对方 Overlay IP;系统中只有一块 RemLink 虚拟网卡
|
||||
|
||||
- [x] Phase 2: Server IPAM + Bootstrap
|
||||
- [x] internal/database:SQLite + migration 工具化(settings/nodes/sessions/session_cidrs/session_stats/event_logs 六张表)
|
||||
- [x] Bootstrap API:GET /api/v1/server/info、POST /api/v1/bootstrap/register、POST /api/v1/bootstrap/config
|
||||
- [x] Join Token 管理(可轮换,存 settings)
|
||||
- [x] internal/ipam:地址分配/持久化/释放/手动修改(Server 地址唯一权威)
|
||||
- [x] internal/overlay/serverwg:wgctrl 创建 wg0(10.88.0.1/16,ListenPort 51820)、Peer 动态编排(/32 AllowedIPs)、IPv4 forwarding + wg0→wg0 放行、不做 SNAT
|
||||
- [x] Windows 端 internal/identity:Node Identity Store(NodeID/NodeName/WG KeyPair/NodeToken),敏感字段 DPAPI 落盘
|
||||
- [x] 验收:10 个模拟 Node 注册,地址唯一且重启不变;Peer 动态创建/删除测试通过
|
||||
|
||||
- [x] Phase 3: Control Plane
|
||||
- [x] internal/control:Overlay Control WebSocket Hub(ws://10.88.0.1:7001/control,仅 Overlay 监听)
|
||||
- [x] HELLO/WELCOME 握手、HEARTBEAT(5s 间隔;ONLINE ≤15s / UNSTABLE 15–30s / OFFLINE >30s)
|
||||
- [x] NODE_LIST 下发 Engineer(Site 在线列表:Name、Overlay IP、Online、RemoteSubnetCapability、LastSeen)
|
||||
- [x] 能力上报(capabilities、OS、版本、netstack 状态、flow capacity)
|
||||
- [x] 断线指数退避重连(1s, 2s, 5s, 10s, 30s 上限)
|
||||
- [x] 验收:Control Hub 集成测试验证节点上下线状态传播与 Engineer Site 列表
|
||||
|
||||
- [ ] Phase 4: Engineer PacketMux POC
|
||||
- [x] MuxTun Read 出方向分类:Overlay CIDR → wireguard-go;Remote CIDR → SubnetSender 有界队列;其他 → 丢弃 + 限速日志
|
||||
- [x] internal/platform/windows/route:RouteManager(AddRemote/RemoveRemote/Conflicts/Lookup,带 ownership metadata)
|
||||
- [x] Engineer 本地 CIDR 冲突检查(net/netip prefix overlap,收集非 RemLink 直连/静态/VPN 前缀;0.0.0.0/0 不作为冲突依据)
|
||||
- [ ] 验收:给 Wintun 添加测试 Remote CIDR 路由后能截获完整 IPv4 Packet,Overlay 普通流量不受影响
|
||||
|
||||
- [ ] Phase 5: 同网卡 UDP 再入路径(Gate B)
|
||||
- [x] internal/subnet/sender.go:SubnetSender(普通 UDP Socket 绑定 Engineer Overlay IP → Site OverlayIP:6200,不手写外层 IP/UDP Header)
|
||||
- [x] internal/subnet/header.go + validator.go:SessionHeader 编解码与双向收包校验(Magic/Version/SessionID/PayloadLen/外层源/内层源目 CIDR)
|
||||
- [x] internal/subnet/listener.go:Engineer 端 Session UDP Listener(仅绑定 OverlayIP:6200)
|
||||
- [x] MuxTun 并发模型:有界 channel + 独立 Sender goroutine、Read 不阻塞、全 Remote batch 时继续读取防 busy loop、统一 Wintun writer 序列化、buffer pool
|
||||
- [ ] 验收(Gate B):外层 UDP 再入同一 Wintun 无死锁/无限循环,经 wireguard-go 发往 Server
|
||||
|
||||
- [ ] Phase 6: Site gVisor netstack POC(Gate C)
|
||||
- [x] internal/subnetgateway/netstack:gVisor netstack 集成(pinned commit,channel.Endpoint,InjectInbound 入 / Read 出)
|
||||
- [x] internal/subnetgateway/tcprelay:TCP Forwarder(接收 SYN → host net.Dial("tcp", target) → io.CopyBuffer 双向搬运)
|
||||
- [x] internal/subnetgateway/udprelay:UDP flow mapping(SessionID + 五元组,host UDPConn,idle timeout GC,默认 60s 可配置)
|
||||
- [x] Site 端 Session UDP Listener(仅 OverlayIP:6200)+ SubnetGateway.InjectIPv4 接入
|
||||
- [ ] 验收(Gate C):TCP Forwarder 通过 host net.Dial 连接测试目标;UDP Forwarder 双向收发
|
||||
|
||||
- [ ] Phase 7: 双向 Session 回传 + Ping Gate(Gate D,关键 Gate)
|
||||
- [x] netstack egress Raw IPv4 → SessionTransport 对称 SessionHeader 封装(Outer SRC=Site Overlay IP → DST=Engineer Overlay IP)回传 Engineer
|
||||
- [x] Engineer SessionListener 校验(SessionID/Outer Source/Inner Source CIDR/Inner Destination)后经统一 Wintun inbound writer 注入
|
||||
- [x] internal/subnetgateway/pingrelay:ICMP Echo Relay(x/net/icmp 或受控系统 ping;保留原 ID/Sequence 构造 Echo Reply)
|
||||
- [ ] 验收(Gate D):TCP/UDP 往返闭环;Engineer ping 现场 IP 得到正确 Echo Reply(T07/T08/T09 场景)
|
||||
|
||||
- [ ] Phase 8: 完整 Session 状态机
|
||||
- [x] Server Session Manager:CREATING/PREPARING_SITE/READY/ACTIVE/STOPPING/CLOSED/FAILED 全状态流转
|
||||
- [x] 全链路消息:CREATE_SESSION → PREPARE_SESSION → PREPARE_RESULT → SESSION_CONFIG → ROUTES_READY → SESSION_ACTIVE;STOP_SESSION 双向
|
||||
- [x] Site PREPARE:每 CIDR Windows Route Lookup(DIRECT/ROUTED/DEFAULT_ONLY/NO_ROUTE/OVERLAY_CONFLICT)+ netstack capability + flow capacity(TCP 2048/UDP 4096 可配置)检查
|
||||
- [x] 错误码全量落地(含 SESSION_TIMEOUT/FLOW_LIMIT_REACHED/NETSTACK_UNAVAILABLE/SESSION_INJECT_FAILED)
|
||||
- [x] SESSION_STATS:Engineer 视角 Upload/Download 累计,每 5 秒上报 Server 并持久化
|
||||
- [x] Reconcile:Engineer 启动清理残留 Remote Route;Site 启动重建 SubnetGateway 并清理遗留 flow;Server 重启 Session 全部 CLOSED
|
||||
- [ ] 验收:T03/T04/T05/T13/T16 场景通过
|
||||
|
||||
- [ ] Phase 9: Engineer GUI + Server Web UI
|
||||
- [x] Engineer Wails GUI(frontend/engineer + Vue 3):Server/Overlay/Control 状态、Site 列表、多 CIDR 输入、本地冲突预检、Session 详情(SessionID/流量/包数/时延/日志)、断开按钮;Active 时禁止选择第二个 Site
|
||||
- [x] Server Web UI(frontend/server + Vue 3 + Vite + Go embed):Dashboard/Nodes/Sessions/Network/Logs 五页面
|
||||
- [x] Admin Web API 全量实现(nodes/sessions/network/logs 的 CRUD 与强制断开;可选单一 Admin Token)
|
||||
- [ ] 验收:UI 操作可驱动完整业务流程;UI 中不复制网络业务逻辑
|
||||
|
||||
- [ ] Phase 10: 多节点与重复现场网段
|
||||
- [x] 多 Engineer 多 Site 并发在线与会话(自动化状态机测试完成;真实四节点证据仍归 T11/T12)
|
||||
- [x] 重复现场网段隔离验证:SessionID + Engineer Overlay IP 区分(实现级隔离测试完成;T11/T12 仍待真实环境)
|
||||
- [ ] 端到端验收测试 T01–T18 全量执行(见 checklist.md)
|
||||
- [x] 打包与部署:deploy/docker Compose 基线(仅 NET_ADMIN、/dev/net/tun、Preflight 检查)、THIRD_PARTY_NOTICES
|
||||
- [ ] 验收:Engineer-A→Site-A 与 Engineer-B→Site-B 同时访问相同 192.168.13.0/24 互不串流
|
||||
|
||||
# 任务依赖
|
||||
|
||||
- Phase 0 → Phase 1 → Phase 2 → Phase 3 → Phase 4 → Phase 5 → Phase 6 → Phase 7 → Phase 8 → Phase 9 → Phase 10(规格书要求每阶段通过验收后进入下一阶段)
|
||||
- 例外:Phase 1 的 Windows 侧 POC 与 Phase 2 的 Server 侧可部分并行(Phase 1 验收用手动配置的 kernel WireGuard Server)
|
||||
- Gate A=Phase 1 验收、Gate B=Phase 5 验收、Gate C=Phase 6 验收、Gate D=Phase 7 验收;Gate D 是全项目最重要关卡,未通过前不投入 Phase 9 GUI
|
||||
Reference in New Issue
Block a user