跳转到内容

NSGW · WireGuard Peer Relay 协议层

本页是 architecture.md §2.2 的协议层附录,描述 NSGW 已实现的不解密 WireGuard 中继:如何在不持有任何密钥的前提下把访问端与发布端的端到端 WireGuard 会话桥接起来,以及它与原生 WireGuard 客户端的兼容矩阵。设计参照 Tailscale Peer Relay,代码位于 nsgw-wg/src/relay.rs。

适合的读者:NSGW 实现者、要用原生 WG 客户端接入 NSIO relay 的运维。

WireGuard 只有四种报文,布局固定,前几个字节为明文:

Type名称长度NSGW 用到的明文字段
1HandshakeInit148 Bsender_index(4..8)+ mac1(116..132)
2HandshakeResponse92 Bsender_index(4..8)+ receiver_index(8..12)
3CookieReply64 Breceiver_index(4..8)
4TransportData变长receiver_index(4..8)+ ciphertext

NSGW 只读 index 与 mac1,报文按字节原样转发,不修改、不重组、不解密。

2. 生命周期:materialize → register → demux → forward

Section titled “2. 生命周期:materialize → register → demux → forward”

NSD 按 pair 投影 gateway_wg_relay(键为目标端公钥),NSGW replace_config 装入配对表并为每个目标预计算

mac1_key[target] = HASH("mac1----" ‖ target.pubkey) # HASH = BLAKE2s

同一 generation 的重复投影与已 admitted 身份比较后跳过重建(PLAN-131)。每个 pair 绑定 realm、两端机器身份、peer key、generation、选定网关与当前 NSD 授权;capability 描述端点行为但不替代授权。

发布端 NS 从其真实 WG 数据 socket 发送签名的 wg_relay.register_source 信封;NSGW 校验 Ed25519 签名(machine_key_pub pin 到投影身份)后绑定 target.pubkey → 观察到的 NAT 源 (ip:port),TTL 90 s,由发布端周期刷新。没有学到源时回退到投影里的静态 nsn_endpoint,两者都没有则丢弃。

2.3 用 mac1 分流 HandshakeInit(demux)

Section titled “2.3 用 mac1 分流 HandshakeInit(demux)”
on init_packet(p, src):
for (target, key) in mac1_keys:
if MAC(key, p[0..116]) == p[116..132]:
dst = learned_source(target) or target.static_endpoint or drop
admit(cookie, token_bucket[target]) or drop
sessions.install(client_index = u32(p[4..8]), src → dst) # 双向
forward(p, dst); return
drop("init_no_mac1_match")

复杂度 O(N) BLAKE2s-MAC per Init(N = 配对表目标数);握手每 2 分钟一次,可负担。

  • HandshakeResponse 的 receiver_index 等于访问端的 sender_index;NSGW 据此把响应配回访问端,并把发布端分配的 sender_index 安装为反向路由键。同一目标有多个 pending init 时,选择按响应刷新前 last_seen 最新的那一个,确保重试的 init 与重绑的 NAT 源仍解析到同一个客户端;同一端点后的不同目标保持 fail-closed(BUG-031)。
  • CookieReply 与 TransportData 按 (receiver_index, src) 单跳查表转发。
  • 转发受因果租约约束:会话与签名的转发 epoch 绑定,撤销、替换或切到 direct 后立即失效;quarantined lease 可恢复(PLAN-108 / 110 / 111)。

整体即一个加了 mac1 索引、带租约与准入的 UDP NAT。

访问端 NS NSGW :51821 发布端 NS
│ Init {sndr=A, mac1=X} │ │
├─────────────────────────►│ mac1 命中 target.pubkey │
│ │ dst = register_source 学到的源 │
│ ├─────────────────────────────►│
│ │ Response {sndr=B, rcv=A} │
│ │◄─────────────────────────────┤
│ Response │ A→访问端;安装 B→发布端 │
│◄─────────────────────────┤ │
│ Transport {rcv=B} │ │
├─────────────────────────►│ (B, src) 单跳查表 │
│ ├─────────────────────────────►│
│ │ Transport {rcv=A} │
│◄─────────────────────────┼──────────────────────────────┤
参数默认含义
wg_relay_max_sessions4096会话总数上限
wg_relay_max_pending_inits1024未配对 init 上限
wg_relay_handshake_rate / burst64/s / 128per-target 令牌桶
wg_relay_cookie_threshold16触发 cookie 卸载的负载阈值

超限时丢弃并计数;决策见 docs/decisions/2026-06-10-wg-relay-dos-bounds.md。

  1. Endpoint 与 PublicKey 解耦:peer 的 Endpoint = NSGW:51821 只决定初始往哪发,PublicKey 仍是对端真实静态公钥;NSGW 不持任何相关密钥,物理上无法解密。
  2. Roaming Endpoint:mac1 校验通过且解密成功的握手会让两端把对端 endpoint 锁定到 NSGW,后续出包自然走 NSGW。

NS 侧的 wg_relay 指令就是把该 peer 的 endpoint 指向网关 relay 地址,同时保留本地合成的 wss_relay_fallback 以便阶梯降级。

原生客户端只需把 Endpoint 指向 NSGW relay 端口即可被中继;所有遵循标准报头的实现(Linux 内核 WG、wireguard-go、BoringTun、官方 App、OpenWrt、商用客户端)均兼容。但授权仍来自 NSD:pair 不在配对表、发布端未 register_source、租约失效时一律丢弃。

特性NSGW 的处理客户端要求
mac1解析以路由 Init;Response / Transport 透传所有现行实现都支持
mac2 / Cookie透传;NSGW 自身在高负载下也可要求 cookie按协议正常工作
PSK透传两端共享即可
PersistentKeepalive建议 ≤ 25 sNAT 映射 30~60 s 过期
IPv4 / IPv6 endpoint双栈任意
MTU透传不重组保持默认

终结设备(:51820)与 relay socket(:51821)是两个独立端口,不再依赖 mac1 在同端口区分自身与他人。终结、公网边缘、Egress、Exit 才有网关侧报文级 ACL、MSS clamp、按目标计费与应用层观测;relay 模式按设计放弃这些能力,换取端到端加密与存量客户端兼容。

情形NSGW 行为客户端表现
目标公钥不在配对表mac1 不匹配 → 丢包 + 计数握手超时
发布端未注册源且无静态端点丢弃握手超时
长时间空闲,NAT 失效会话与学习源自然过期WG 重新握手自动恢复
访问端漫游旧键失配 → 新一轮 Init自动 rekey
sender_index 碰撞(index, src) 复合键 + last_seen 消歧各自正常
授权撤销 / pair 切到 direct租约立即失效该 pair 停止转发
超过 DoS 边界丢弃 + cookie 卸载重试
NSGW 重启会话丢失下次 retransmit 重建
UDP 被阻断不在 NSGW 侧处理NS 阶梯降级到同网关 WSS
  • 模式在登记时声明(--transport 能力集),由 NSD 为每条 pair 选定并投影 WgConfig 的 per-peer 指令与 gateway_wg_relay;没有”直连失败 → 申请中继”的运行期 API。
  • 配对投影的规模与增量:NSD PLAN-125(有界 / 分片 / 增量)与 NSGW PLAN-130(增量应用,草案:delta 序列绑定签名转发 epoch 与完整快照 digest)。
  • 同网关 WSS override 由 NSD 的 pair transport 权威投影,NSGW 无需改动。
  • NSGW 自己作为 WG 端点参与握手(终结模式)。
  • WSS 不透明缝合(另一条并行的 relay 通道)。
  • STUN / ICE / 打洞本身(relay socket 只提供反射探测)。
  • 应用层策略 / 计费 / 审计(终结、公网边缘、Egress、Exit 承担)。