最佳实践场景
本章给出几种典型部署拓扑:每个场景说明什么时候用它、怎么搭、关键配置、要注意什么。可以直接照抄,也可以 组合使用。
场景一:单节点纯直连(最简单)
适用:只想要一个带用户认证 + 访问日志的应用出口,不需要分流。
client ──► rove (认证 + 直连) ──► 目标
- 每个用户绑定一个无 route、无
default_action的 routing policy。 - 决策全部落到「直连」。
{
"schema_version": 1,
"version": 1,
"users": { "alice": { "password": "s3cret", "policy": "open" } },
"routing_policies": { "open": { "routes": [] } },
"egresses": {}
}
注意:生产务必开 TLS 入口(https / socks5tls),别让用户名密码在明文 HTTP 上裸奔。
场景二:选择性分流到二级 hop
适用:大部分流量本地直连,只有部分域名/网段需要从另一个出口(hop)出去。
┌─ 命中 route ─► named egress ─► 目标
client ──► rove ┤
└─ 其余 ─────────► 直连 ─► 目标
-
在受控网络里跑一个 独立 hop 节点:
rove-hop --socks5 0.0.0.0:1080 --username hop-user --password hop-pass -
快照使用当前 routing policy schema:route 选中 named egress,未命中则直连:
{ "schema_version": 1, "version": 2, "users": { "alice": { "password": "s3cret", "policy": "walled" } }, "routing_policies": { "walled": { "routes": [ { "selectors": ["api.openai.com", "openai.com", "203.0.113.0/24"], "action": { "type": "egress", "egress": "hop-a" } } ] } }, "egresses": { "hop-a": { "type": "upstream", "backend": { "kind": "socks5", "addr": "10.0.0.9:1080", "username": "hop-user", "password": "hop-pass" } } } }
命中 route 的走 hop,其余直连。想「全量走上游」就把 policy 的 default_action 配成
{"type":"egress","egress":"<id>"},不必再写 catch-all route;想「只放行清单内目标」就配成
{"type":"block"},未列出的目标一律拒绝。
场景三:hop 在 NAT / 防火墙后(反向 hop)
适用:出口机器在 NAT、家宽、或私有网络里,edge 无法主动拨号它。
client ──► edge (公网, QUIC 监听) ◄══ QUIC 注册 ══ hop (NAT 后)
│ │
└──── 反向开隧道 ───────────────────┘──► 目标
- edge 侧开
[reverse_hop](QUIC 的 UDP 端口,记得放行 UDP)。 - hop 侧
rove-hop --reverse-quic edge.example.com:9443 --reverse-hop-id rove-hop-jp,令牌走Rove_HOP_REVERSE_TOKEN环境变量。 - 快照 named egress 写成
{ "type": "upstream", "backend": { "kind": "reverse", "addr": "rove-hop-jp" } }(命名规范见 hop-id-naming)。 - fail-closed:edge 若没有该
hop_id的已认证会话,直接报错,绝不回落直连。
完整线协议、多 edge、观测见 反向 hop 数据面。
场景四:网络隔离环境(MQTT 运维)
适用:控制面不能直接访问节点(节点在隔离网段),但需要查询策略、触发同步、做拨测追踪。
control plane ──► MQTT broker ◄── rove (主动连接, 订阅主题)
- 节点开
[mqtt],主动连 broker,订阅用户查询 / 同步指令 / 拨测追踪主题。 - 用户策略查询返回脱敏结果(不含密码);同步指令触发一次控制面拉取并回报状态;拨测追踪只对匹配连接 回传一次阶段结果。
消息契约见 MQTT 运维通道。
场景五:多节点 fleet + 统一控制面 + 监控
适用:多地边缘节点,统一下发策略,集中观测。
┌─ edge-tokyo ─┐
control ──────┼─ edge-sg ─┼──► 各自 syslog / SNMP ──► 集中监控
(静态快照) └─ edge-fra ─┘
- 同一个
snapshot_url+token服务所有节点:接口不带node_id,所有节点命中同一 URL、收到完全相同的 响应体。控制面可以用纯静态文件 / 对象存储提供这个接口,无需任何按节点路由的后端逻辑。 - 个别节点要不同出口 realization(如各地本地 hop)→ 用快照的
node_overrides.<node_id>.egresses整项替换同名 egress,节点本地按自己的node_id合并;policy 保持全节点统一。 - 观测:每节点开 SNMP 给 Cacti/LibreNMS 轮询流量,或把 访问日志转发 syslog
集中检索。
node_id是跨节点归集的关键维度,务必唯一稳定。
场景六:给 TLS 入口选证书
- 公网域名:用受信任 CA(Let’s Encrypt 等)签发的证书,客户端零配置即可信任。
- 纯内网 / 自签名:客户端需导入你的 CA。当上游 hop 是自签名时,节点侧用
Rove_EXTRA_CA_CERTS追加信任,或在该上游单独设skip_cert_verify=true(仅限受控网络)。 - 证书更新后重启进程加载新证书。
场景七:Subnetra —— 打进隔离网段(spoke egress)
适用:目标在一个不对外开放的内网段里,你想让 edge 上的已认证用户「点名」访问里面的服务。
不用 TUN、不用 NET_ADMIN、不用额外守护进程——config.toml 加一段 [subnetra] 即可组网,
数据面是加密 UDP(每链路独立 PSK 的 ChaCha20-Poly1305)。
按「哪一边能开一个 UDP 口」选摆法:
摆法 A:网段可以放行一个入站 UDP 端口(最通用)
网段内跑一个 rove hub,只对 edge 放行一个 UDP 端口;edge 作 spoke 主动拨进去。 hub 在 overlay 上开的代理入口就是「二跳」:经它可达网段内任意机器。
flowchart LR
C["客户端"] -->|"① CONNECT 10.9.0.1:8080"| E["rove edge · spoke<br/>overlay 10.9.0.2"]
E ==>|"加密 UDP overlay<br/>网段只放行这一个 UDP 口"| H
subgraph SEG["隔离网段 192.168.1.0/24"]
H["rove hub · overlay 10.9.0.1<br/>overlay 上只监听代理端口 8080"]
H -->|"② 二跳 CONNECT<br/>走 hub 自己的认证 + 分流"| T["192.168.1.50:443<br/>网段内任意服务"]
end
-
网段内(hub)——即便没有任何
[[listeners]]也能启动,代理入口在 overlay 上:node_id = "seg-hub-01" [subnetra] enable = true mode = "hub" local_id = 1 listen = "0.0.0.0:18020" # 数据面 UDP,网段边界只放行这一个口 overlay_cidr = "10.9.0.1/24" proxy_protocol = "http" # overlay 上的代理入口(二跳用),走完整认证/策略 proxy_port = 8080 [[subnetra.peers]] id = 2 psk = "<64 hex,每条链路唯一>" allowed_src = "10.9.0.2/32" # 精确到 edge 的 overlay IP name = "edge-spoke" # endpoint 留空,从已认证流量学习 -
edge(spoke):
[subnetra] enable = true mode = "spoke" local_id = 2 listen = "0.0.0.0:0" # spoke 只出站,临时端口即可 overlay_cidr = "10.9.0.2/24" keepalive_secs = 25 # 维持 NAT / 防火墙映射 [[subnetra.peers]] id = 1 psk = "<同一条链路的 64 hex>" allowed_src = "10.9.0.0/24" # 整个 overlay 子网路由到 hub(spoke 的默认路由) endpoint = "seg-gw.example.com:18020" # spoke 必填 name = "seg-hub" -
edge 的快照——给分组一个
subnetra出口,命中 overlay 网段的目标才走隧道:"isolated": { "upstream": { "kind": "subnetra" }, "proxy": ["10.9.0.0/24"] } -
客户端:目标必须是 overlay IPv4 字面量。第一跳
CONNECT 10.9.0.1:8080到 hub 的 overlay 代理,第二跳在这条隧道里再CONNECT 192.168.1.50:443(hub 侧同样要过快照认证)。 两层 CONNECT 需要客户端支持代理链(如 proxychains-ng),或由业务侧封装。
摆法 B:网段完全零入站(连 UDP 也不给开)
角色对调:edge 作 hub(公网 UDP 监听),网段内放一个原版 Zig spoke(有 TUN)主动拨出。
edge 的 subnetra 出口拨 spoke 的 overlay IP,就是那台机器 TUN 上的 OS 服务。
flowchart LR
C["客户端"] -->|"CONNECT 10.9.0.3:22"| E["rove edge · hub<br/>overlay 10.9.0.1"]
subgraph SEG["隔离网段 · 零入站"]
Z["Zig spoke · TUN 10.9.0.3<br/>本机 ssh · DB · API 可直达"]
end
Z ==>|"UDP 出站拨 edge · keepalive 保活"| E
kind = "subnetra"出口拨的是裸 TCP(不讲代理协议),所以「目标机器上有监听」是唯一要求: Zig 节点的 TUN IP 上任何服务都能直达。- Rove spoke 在 overlay 上没有任何监听(用户态栈只在 hub 模式开代理端口),所以摆法 B 的
网段侧要用带 TUN 的 Zig 节点;要够 spoke 机器之外的其他机器,在它的 TUN IP 上再放一个小代理
(如
rove-hop --socks5 10.9.0.3:1080)做二跳。 - 与 Zig 版完全线兼容(CI 逐字节 KAT 校验),老节点不改任何东西。
两种摆法共同的注意事项:
- fail-closed:subnetra 未启用、目标不是 IPv4 字面量、或 overlay 路由不可达时直接报错,
绝不回落直连;
subnetra出口不接受username/password/tls字段。 - overlay 出口仅 TCP(UDP 出口目前只有反向 hop 支持)。
obfuscate必须全网一致(无握手协商,一端开一端关会互不通);PSK 每条链路唯一。- 时钟:节点用 boot epoch 排序,跨重启时钟回拨会被对端拒收——保证 NTP 正常即可。
- 内层 MTU 默认 1452 已自动处理(smoltcp 按此通告 MSS)。若跑在已压缩、路径固定的外层隧道
里(如载体 1360),在
[subnetra]设mtu(范围 576–1452)适配即可,见 Subnetra。
字段语义、路由与相容性细节见 内嵌 Subnetra 组网底座。
场景八:Subnetra hub —— 隔离网段里的机器借 edge 的出口(hub inbound)
适用:方向反过来——网段里的机器想用到 edge 的代理能力(认证、分流、限速、反向 hop 出口), 但网段不允许任何入站。edge 作 hub,网段内 Zig spoke 主动拨出。
flowchart LR
subgraph SEG["隔离网段 · 零入站"]
A["应用<br/>代理设为 10.9.0.1:8080"] --> Z["Zig spoke · TUN<br/>overlay 10.9.0.3"]
end
Z ==>|"UDP 出站拨 edge"| E["rove edge · hub<br/>overlay 代理入口 10.9.0.1:8080<br/>完整认证 + 分流 + 限速"]
E -->|"直连"| T1["公网目标"]
E -->|"或反向 hop / 上游"| T2["其他出口"]
- edge 的
[subnetra]配mode = "hub"+proxy_protocol/proxy_port(同场景七摆法 A 的 hub 片段),overlay 代理入口跑的是 Rove 完整引擎:同一份快照认证、同样的分流 / 限速 / 访问日志, 甚至可以从这里再走反向 hop 出口。 - spoke 机器上的应用把代理地址配成 hub 的 overlay IP:proxy_port 即可,流量经 TUN 进 mesh;
网段内其他机器想共用,把去
10.9.0.0/24的路由指向 spoke 主机(OS 路由,超出 Rove 范围)。 - hub 侧对该 spoke 的
allowed_src配精确/32,端点留空由认证流量学习;NAT 后的 spoke 靠keepalive维持映射。
场景九:多节点统一发布 rove-addrbook
适用:大量云厂商 IP 段、企业应用域名或自有分类需要被多个 edge 的 route selectors 复用。
可信源 ─► rove-abctl fetch/build/verify/diff ─► 制品仓库 ─► 各节点本地 book.rab
│
控制面快照(selectors: book:category)────┘
- 地址簿与快照是两条发布链:
.rab提供分类成员,快照决定每个 policy/route 如何使用分类。 - 首次启用先升级节点并部署有效
.rab,最后才发布带book:selector 的快照。 - 每次候选书先
verify、inspect/query和diff --max-shrink,再按节点/机房 canary。 - 发布系统应核对各节点日志里的 addrbook checksum;只对比快照
version不足以证明决策一致。 - Docker 挂载地址簿目录,不挂单个文件;在同一目录原子 rename,坏书会保留旧书与旧快照。
- 回滚用旧数据构建一个更高 epoch 的新工件,不直接把历史
.rab当作新版本复制回去。
完整 manifest、六种数据源、CLI 退出码、资源上限和恢复语义见 rove-addrbook 指南。
安全加固清单
- 所有对外入口都启用 TLS(
https/socks5tls),不在公网暴露明文http/socks5。 - hop 一律设置非默认凭据(不要留
rove/rove)。 - 令牌 / 密码 / 证书私钥通过部署系统注入,不提交进仓库;示例只用占位符。
- 反向 hop 令牌走环境变量,不进命令行 argv。
- SNMP 配
community强随机值 + 收紧allow_cidrs白名单;对外优先用 SNMPv3(authPriv)。 - 用 systemd 加固(非 root、
NoNewPrivileges、ProtectSystem=strict),低端口用CAP_NET_BIND_SERVICE。 - 访问日志保持开启,用于事后审计;确认日志/syslog 目标本身是可信通道。
- 定期核对:策略失败(认证/过期/block/上游失败)时节点是拒绝而非放行。
- 地址簿工件走受认证的发布通道;
.rab内置 SHA-256 只校验完整性,不证明发布者身份。
性能与可靠性
- 限速:
up_rate/down_rate为 0 时走零开销快路;只对需要限的用户设非零值。 - 离线韧性:确保
cache_path落在持久卷上,控制面故障时节点仍能靠缓存服务。 - 退避:控制面连续失败会指数退避到最高 5 分钟,避免故障时所有节点同时打满重试。
- 本地压测:仓库自带
examples/proxy-benchmark-local.rs和docker-compose.local.yml, 可在本地拉起 edge + 多个 hop,对 HTTP/HTTPS-TLS/SOCKS5/SOCKS5-TLS 四个入口做端到端延迟、 带宽、并发梯度与限速精度测试;支持--json-out保存机器可读结果 (cargo run --release --example proxy-benchmark-local -- all)。 - Subnetra 压测:
cargo run --release --example subnetra-benchmark-local会在同进程内拉起 内嵌 Subnetra hub/spoke,测spoke-egress与hub-inbound两条 Rove 业务路径。它测的是 Subnetra wire + smoltcp + Rove HTTP 代理处理的组合成本;裸 L3 数据面基准仍应使用上游 Subnetra 项目的 netns / live-overlay benchmark。