本页给出 NSIO 四组件的"角色 + 互联"总体视图:每个组件在哪个平面、对外暴露什么、谁向谁发起连接、典型时序如何展开、各组件失败时影响范围。组件内部模块见对应组件目录的
architecture.md。
NSIO 把传统 VPN "认证 + 控制 + 数据转发"混合的网关拆为四个独立组件,归属三个平面:
| 平面 | 职责 | 谁在这个平面 | 是否承载业务流量 |
|---|---|---|---|
| 管理面 | 管理员编辑、IdP 对接、审计 | NSD(Web UI / Admin API) | 否 |
| 控制面 | 身份签发、策略编排、配置分发 | NSD(唯一来源);NSN / NSGW / NSC(订阅者) | 否 |
| 数据面 | 业务流量转发与 ACL 终决 | NSGW(中继);NSN(终结 + 终决);NSC(出站封装) | 是 |
NSD 是唯一有状态强一致组件,但永不承载业务流量;其余三方都是控制面订阅者,同时是数据面参与者。
横向是发起方,纵向是接受方;空格表示无直接连接。
| ↓ 接受 \ 发起 → | NSD | NSGW | NSN | NSC | 浏览器 / 标准工具 |
|---|---|---|---|---|---|
| NSD | — | REST 注册 / 端点上报 + SSE | REST 注册 / 心跳 / 服务上报 + SSE / Noise / QUIC | REST 注册 / 心跳 + SSE | — |
| NSGW | — | — | — | WireGuard UDP / WSS(出站) | HTTPS 443 |
| NSN | — | WireGuard UDP / WSS(NSC↔NSN 段 2) | — | — | — |
| NSC | — | — | — | — | 127.127.x.x:port / *.n.ns / HTTP 代理 |
三条不变量:
NSC ↔ NSGW 与 NSGW ↔ NSN 是两段独立握手;终结模式下 NSGW 持私钥并解密,中继模式下 NSGW 只见密文。WgConfig)协议级地不含 private_key 字段。MultiControlPlane),NSD 之间互不感知,不联邦同步。四个组件的启动顺序、事件订阅子集与触发源:
按订阅者类型选择事件子集,事件签名独立于外层传输(SSE / Noise IK / QUIC 共用同一事件解析器):
| 事件类 | NSN | NSGW | NSC |
|---|---|---|---|
wg_config(WireGuard 拓扑) | ✓ | ✓ | 接收点已就位、未消费 |
proxy_config / acl_config | ✓ | — | 接收点已就位、未消费 |
acl_projection(预拒副本) | — | ✓ | — |
gateway_config(网关拓扑) | ✓ | — | ✓ |
routing_config | ✓ | ✓ | ✓ |
dns_config | ✓ | — | ✓ |
services_ack | ✓ | — | — |
token_refresh / ping / pong | ✓ | ✓ | ✓ |
NSD 推送的触发源:订阅建立、services/report 与 gateway/report 上报触发、管理员变更、JWT 临近过期。
时序图(§5.1 / §5.2)回答"消息按时间如何往返",本节的两张总图回答"业务数据在路径上的形态变化"与"控制面配置如何驱动数据面行为"——前者是业务数据流,后者是配置数据流。
下图把五种连接形态(§5.4 的 direct / relay / terminate / wss 四模式 + 公网浏览器 https)合并到一张图,用颜色标注加密边界,用节点内文字标注处理逻辑。读图顺序:左侧入口 → 中间封装与处理 → 右侧业务服务出口。
| 模式 | NSGW 角色 | 加密段数 | NSGW 是否见明文 | 适用场景 |
|---|---|---|---|---|
direct | 不参与 | 1(端到端) | — | NSC ↔ NSN P2P 可达 |
relay | 不解密 UDP 中继 | 1(端到端) | ✗ | NAT 阻断直连但 UDP 仍通;managed / 云端 NSGW 的默认(结构性不可观测客户明文,见 §6.4.1) |
terminate | 持私钥并终结 | 2(段 1 + 段 2 独立) | ✓ | self-hosted NSGW 的默认;NSGW 也是 ACL 预拒点(managed 形态须客户显式 opt-in,见 §6.4.1) |
wss | 帧缝合(不解 WG) | 2 | 部分(WSS 内可见) | UDP 完全被阻断 |
https | TLS 终结 + Host 路由(可选外部鉴权) | 1(TLS) + 1(段 2 WG) | ✓ | 不依赖 NSC 的独立公网流量承载:浏览器、API 客户端、移动端、第三方系统等任意公网 HTTPS 客户端,可选挂接外部鉴权(OIDC / mTLS / API key / 签名 URL / Basic Auth) |
加密边界三条不变量:
direct / relay 下 NSGW 物理上无法读取应用层;terminate 下 NSGW 与 NSN 各自用本地私钥握手,私钥都不来自 NSD。terminate 模式下 NSGW 短暂持有明文。NSD 的 11 类 SSE 事件按订阅者类型分包推送,落在每个数据面节点内部不同的"消费模块"上。下图展示事件 → 模块的驱动关系,以及上报触发推送的反向链路:
要点:
wg_config / proxy_config / acl_config 三类事件 NSC 端已定义接收点(虚线),但当前实现忽略;预留给未来 TUN 模式、出站 ACL 预检等能力。services/report 与 NSGW 的 gateway/report 是触发 NSD 策略合成的关键反向链路;管理员只编辑高层意图(ACL / 角色),低层配置(路由、DNS、WG 拓扑)由 NSD 据上报自动展开。gateway_wg_relay 是 relay 模式的载体:NSD 在为某条 (NSC, NSN) 边选定 relay 模式时,下发的不是探测指令,而是直接 materialize 出"中继配对表"给对应 NSGW,配合双方的 wg_config 一次性激活。用户进程在本机连 127.127.x.x:port 或 *.n.ns,经由 NSC 的 VIP 监听器 / HTTP 代理出站,经 NSGW 中继送达目标 NSN:
关键点:
/client 入口预拒;不解密中继模式下 NSGW 不参与握手,NSC ↔ NSN 端到端加密。不依赖 NSC 的公网 HTTPS 客户端(浏览器、API 客户端、移动端、第三方系统)通过 NSGW 的 443 入口直达 NSN 上的服务。这是 NSGW 在"无 NSC 部署"形态下的主入口,与 §6.1 的 NSC 双跳并列:
关键点:
routing_config)。nsgw/architecture.md §2.4.1),主体以 NSGW 签名头部透传给 NSN。services.toml 兜底)。NSGW 还可以作为不解密 WireGuard 中继(peer-relay 模式),NSC ↔ NSN 端到端加密、NSGW 只见密文,详见 nsgw/peer-relay-protocol.md。当前 mock 未实现,已设计 gateway_wg_relay SSE 事件预留。
NSC ↔ NSN 走哪条路径,是 NSN / NSC 在 /machine/register 时声明的支持集合 + NSD 策略层选定的激活模式,不是运行期探测失败后再申请的产物。这把决策从数据面挪到了控制面,可预测且无反馈环。
| 模式 | 数据面路径 | NSN 看到的 NSC peer Endpoint | NSGW 角色 | NSGW 是否见明文 | 兼容存量 WG 客户端 |
|---|---|---|---|---|---|
direct | NSC ↔ NSN(P2P,可叠加 hole-punch) | NSC 真实 endpoint | 不参与 | — | ✓ |
relay | NSC ↔ NSGW ↔ NSN(端到端) | NSGW endpoint,peer pubkey 仍是 NSC 真实 pubkey | 不解密中继 | ✗ | ✓ |
terminate | NSC↔NSGW + NSGW↔NSN(双段独立 WG) | NSGW endpoint,peer pubkey 是 NSGW | 终结 | ✓ | ✓ |
wss | NSC↔(TCP/WS)↔NSGW↔(WG)↔NSN | (NSC 端非 WG) | WSS 帧中继 + 出口走 WG | 部分 | NSC 端非 WG |
注册流程的字段(规划,加入 POST /api/v1/machine/register body):
supported_link_modes: ["direct", "relay", "terminate", "wss"] —— 该节点物理上支持的子集preferred_mode(可选) —— 同等可行时的优先项NSD 在策略授权时为每条 (NSC, NSN) 边取 NSC.supported ∩ NSN.supported 的非空交集,选最优先项,材料化 为对应的 wg_config(双方)+ 必要时的 gateway_wg_relay(NSGW),一次性下发完成。
NSN / NSC 端不需要实现"直连超时 → 申请中继"的探测器与控制信道;模式切换属于策略变更(admin 调权限或节点重新登记 supported 集合),仍由 NSD 推送。auto-fallback 可作为基础模型上的可选附加层(NSC / NSN 端在 supported_link_modes 内自动降级),不属于默认行为。
supported_link_modes ∩ 给出 NSC 与 NSN 物理上能选的集合,默认从中挑哪一个取决于 NSGW 自身的信任分层 —— 由 NSGW 在 gateway/report 时声明 operator 字段,NSD 据此为每条 (NSC, NSN) 边设默认优先级:
operator | 含义 | 默认模式 | 理由 |
|---|---|---|---|
self-hosted | 客户在自己 PoP 部署的 NSGW | terminate | NSGW 在客户信任边界内,见明文不增加暴露面;同时拿到 /client 入口预拒、流级计费、MSS clamp、协议异构等"网关见明文"才有的能力 |
managed | NSIO 或第三方 SaaS 运营的云端 NSGW | relay | NSGW 在第三方信任域,结构性不可观测客户明文;客户显式 opt-in 才升级到 terminate,与"结构上不可读 ≠ 承诺不读"的产品立场一致 |
partner | ISP / 合作伙伴托管 | relay | 信任假设介于两者之间,保守取 relay |
与"未声明能力 NSD 不能凭空生成"原则同构:默认信任边界跟着 NSGW 运营方走;客户要扩大边界(让 NSGW 见明文)必须通过 preferred_mode = "terminate" 显式声明,等价于在合规上接受"运营方有能力观测明文"。这个 opt-in 在 NSD 端审计可见。
relay 默认带来的副作用(云端形态需要相应补齐):
| 失去的能力 | 补救路径 |
|---|---|
/client 入口 ACL 早拒(~99% 越权拦在网关) | 移到 NSC 侧出站 ACL 预检(acl_config SSE 事件 NSC 端已就位、未消费;§4 表);NSN 仍兜底终决。两级信任从"NSGW 偏宽 + NSN 偏严"变成"NSC 自检 + NSN 终决",信任根去中心化 |
| per-org 精确流级计费 | NSN 报 per-service 字节;NSGW 仍能算密文 4-tuple byte counters,但失去 L4+ 切分粒度 |
| MSS clamp / tc 整形 | NSC 端 userspace WG 自己处理 |
| WAF / mTLS / OIDC 中间件 | 这些是 §6.2 公网入口(https)路径上的能力,与 relay/terminate 选择正交 —— 启用公网入口本身就意味着接受 NSGW 在那条路径上见明文 |
| NSN 侧 peer 表 O(NSGW) 扩展性 | 退化为 O(NSC),是硬代价——大型租户在 managed 形态下需要 NSN 用 wireguard-go userspace + sharding 应对,无法从其他地方补 |
自部署 NSGW 不变:客户在自家 PoP 跑 NSGW 时,operator = self-hosted,所有"网关见明文"特性继续是免费默认,与现有部署形态零差异。
当前实现里"服务"与"NSN"是 1:1 绑定:每个服务必须在某个 NSN 的
services.toml里声明、由该 NSN 进程承接终端流量并做 ACL 终决。这是默认且最常用的形态,但不是"服务"概念的唯一可能形态。本节统一描述两条相关扩展——backend 类型的多元化、以及服务声明权的对称化。
随着 NSIO 覆盖面扩大,以下场景越来越普遍:访问 SaaS API、按特定 egress region 适配 IP allowlist、把跨云 PaaS 端口暴露给开发者、让 NSC 把"全部互联网流量"经特定 PoP egress(类似 Tailscale exit node)。它们都符合"我只想要一个被分配 VIP / 路由的入口,backend 是哪种形态用户不关心"。
| Backend 类型 | 数据路径 | 终决 ACL 在哪 | 适用场景 |
|---|---|---|---|
NsnBacked { site, port }(现有) | NSC → NSGW → NSN → 本机/LAN 服务 | NSN(权威终决 + services.toml 兜底) | 需要进入站点内网、需要 L3 模型、需要 smoltcp 解析、需要权威 ACL 终结的传统场景 |
GatewayEgress { gateway_id, target_host, target_port, transport }(规划) | NSC → NSGW → 直接 TCP/UDP 到指定目标 | NSGW(此时无 NSN,NSGW 升级为权威决策点) | SaaS / 公网 API egress、IP allowlist、轻量"虚拟服务" |
GatewayExit { gateway_id, scope }(规划,exit node 模式) | NSC → NSGW → 公网(任意目标) | NSGW(per-user / per-group exit policy,fail-CLOSED) | "把整段默认路由经此 PoP 出去"——开发者跨地 egress、合规出口、移动用户绕墙 |
scope 取值:default-route(等价 0.0.0.0/0,完整 exit node)、cidr: "10.0.0.0/8"(部分 exit,只这段经过)、或 acl-driven(由 ACL 决定哪些流量启用 exit)。
与 NSC 数据面模式的耦合:
NsnBacked / GatewayEgress 在所有数据面模式下工作(VIP 段 + WSS 流);GatewayExit 实质上需要 TUN 数据面——userspace 模式的 VIP 段无法捕获任意公网 IP,HTTP 代理只能覆盖 HTTP/CONNECT;exit node 启用后 NSC 需在系统路由表写 0.0.0.0/0 dev nsio0(或目标 CIDR)。详见 nsc/roadmap.md F.1 TUN 数据面真实化。TUN 模式的寻址模型选择(VIP vs passthrough):VIP 段(127.127/16 userspace、100.64/16 TUN)是 userspace 约束的延续,不是 TUN 模式的硬要求。TUN 已获 root 并管控 TUN 设备,可以选择直接路由到站点 CIDR(passthrough,等效 WireGuard subnet router / Tailscale subnet 路由)。NsnBacked 服务的 addressing 字段(NSN 端 services.toml 声明)在 "vip" 与 "passthrough" 间二选一,二者可共存:虚拟服务(GatewayEgress)走 VIP,L3 整段网络服务走 passthrough。详见 nsc/architecture.md §2.8.1。
声明权当前不对称:只有 NSN 能声明自己的服务(services.toml 本地白名单 + services_report 上报),NSGW 是纯被动消费者——只接收 NSD 推送的 wg_config / routing_config / acl_projection,自己不声明能服务什么。
这条不对称在引入 §6.5.1 的新 backend 类型后无法继续维持:GatewayEgress 与 GatewayExit 描述的是网关的网络位置 / 出口能力,只有网关自己知道"我能到达 SaaS 后端的网段"、"我所在 PoP 提供哪个国家的 egress IP"、"我有没有专线打到合作伙伴"。把这些事实交给 NSD 维护,等于让"远离数据面的中央数据库"声明"靠近数据面的网关能干什么",违反 NSIO 的"上报驱动合成"原则。
由此引入对称的服务声明权:
| 声明者 | 通道 | 可声明的 backend 类型 | 说明 |
|---|---|---|---|
| NSN(现有) | POST /api/v1/services/report,源自本地 services.toml | NsnBacked | 节点本地白名单是 ACL 兜底,不可绕过 |
| NSGW(规划) | POST /api/v1/gateway/services_report(或扩展现有 gateway_report body),源自本地 gateway_services.toml | GatewayEgress、GatewayExit | 网关本地清单是该网关 ACL 终决的兜底——admin 在 NSD 端不能让 NSGW 暴露它本地清单中没有声明的目标 |
| NSD admin(管理面) | Admin UI / API | 任意类型(裁剪后下发) | 管理员选择"启用哪些声明给哪个 realm / role";不能凭空生成 NSN/NSGW 没声明的能力 |
声明对称带来的副作用:NSGW 与 NSN 一样持有"自身能力的本地权威清单",这同时是 ACL 终决的最后兜底——gateway_services.toml 里没有的目标,即便 NSD 推下来,NSGW 也拒绝中转。这与 NSN 的 services.toml floor 是同一种安全模型。
NSC 与现状一致——routing_config 给它的就是"site 名 + service 名 + VIP",NSC 不感知 backend 是 NSN 还是 NSGW(egress / exit);open_stream / VIP listener 的语义不变,唯一例外是 GatewayExit 需要 NSC 切到 TUN 数据面以承接默认路由。改动集中在 NSGW(L4 egress + exit NAT + 完整 ACL 引擎 + 服务声明上报)与 NSD(扩展声明合成,接受来自网关的服务上报)。
信任模型的关键变化:NsnBacked 下 NSN 是 ACL 终决点+本地白名单兜底;GatewayEgress / GatewayExit 下没有 NSN,NSGW 必须承担权威终决——这违反了 §7.1 中"NSGW 仅做预拒,不做权威决策"的当前不变量。因此这两类 backend 的实施需要在 NSGW 引入完整 ACL 引擎(而不仅是预拒投影),与现有"两级信任"形成正交的"纯 NSGW 终决"分支。该 carve-out 仅作用于 GatewayEgress / GatewayExit 路径,不影响 NsnBacked 的现有信任边界。
详细演进项见 nsgw/roadmap.md 与 nsgw/architecture.md §2.8.1;NSC 侧 NsnBacked / GatewayEgress 零改造,GatewayExit 需要 TUN 数据面真实化(nsc/roadmap.md F.1)。
| 边界 | 谁在线内 | 谁在线外 |
|---|---|---|
| 私钥 | 仅本节点(NSN / NSGW / NSC) | NSD、所有传输通道、所有日志、所有备份 |
| 业务流量明文 | NSN(ACL 终决点);终结模式下的 NSGW;浏览器 | 中继模式下的 NSGW;NSD |
| 权威 ACL 决策 | NSN | NSGW(仅做预拒,可偏宽不可偏严) |
| 服务白名单 | NSN(本地权威) | NSD(不能反向告诉 NSN 能代理什么) |
| 故障 | 业务连接 | 控制变更 | 新节点接入 |
|---|---|---|---|
| NSD 整体不可达 | 既有连接继续 | 不能下发新策略 | 不能注册新节点 |
| 某个 NSGW 不可达 | NSC / NSN 切到备网关;旧连接断(lazy binding 让新连接立即用新网关) | 不影响 | 不影响 |
| 某个 NSN 不可达 | 该 site 服务不可达 | 不影响 | 不影响 |
| NSC 不可达 | 本机用户进程访问失败 | 不影响 | 不影响 |
| SSE 与 NSD 短暂断链 | NSGW ACL 投影 fail-open(偏宽);NSN 维持上一次配置 | 暂停热更新 | 暂停 |
NSD 故障域被压缩到"无法变更策略 / 无法新建注册"——这是 NSIO 把控制面与数据面解耦的最重要收益。
index.mdarchitecture.mdnsgw/peer-relay-protocol.mdroadmap.md