外观
fleet 供给收权:节点密钥与端口的单一口径
节点 WireGuard 密钥与 WireGuard 端口段位都是 control-server 的一等数据,所有写入渠道走同一个服务。本文说明这两条单一口径的形状、对等命名规范,以及存量收编用的运维端点。
节点密钥
单一事实源是 node_wireguard_keys 表(node_id → 私钥加派生公钥),由 control-server 保管。
- 节点无钥时收养既有接口在用的字面钥,全新节点才生成一对。绝不凭空生成一把顶替正在跑的身份——那正是密钥分叉的形状。
- 不用任何引用魔法。
node://、secret://之类的 URI 形态一律废弃,管理面校验直接422。spec 与 DesiredState 里存的都是密钥字面串。 - 单源纪律由 materializer 对每个 WG 接口无条件注入节点密钥实现——这与 link-local 注入同构,都是「把副本变派生」。spec 里存什么都会被节点密钥覆盖,agent 侧协议零改动。
Node.wireguard_public_key是本表公钥的只读投影,写路径只有services/node_keys。托管上报的一致性闸对照的就是这个投影:公钥不符即409并回滚事务。
轮换:set_node_key 加全接口重物化。内部对端的公钥由 materializer 现取现填、自动跟随;外部对端需要人工通知。
agent 侧只做一致性上报,不生成也不保管密钥:从 DesiredState 取密钥、推导公钥上报,控制面比对不符即 409 并中止该轮 reconcile。状态目录下已无密钥文件。
⚠️ 私钥以 base64 明文列存在
node_wireguard_keys里。 控制面数据库泄露等于全 fleet 隧道密钥泄露——这是密钥收权换来的代价,库备份、公网发布的 PostgreSQL 端口与读权限凭据都要按此定级。
可选的离线托管([server] recovery_public_key)在此之上再存一份加密副本,生产当前未配置。见 密钥托管与恢复。
端口
段位入库 port_pools:
| scope | 段位 | 用途 |
|---|---|---|
external | 51800–51830 | 对外对等,含自动对等 |
internal | 52800–52820 | 节点间内部隧道 |
表空时自播种,保证任何建库路径都能落到定案值。
统一分配 services/port_allocator:全部渠道——自动对等落成、管理面手工、迁移工具——共用同一套 allocate 与 validate_port。
占用集是节点的全部 WG 接口,含禁用的。禁用是暂停不是释放:一条禁用会话的端口若被重新分配出去,恢复它就会撞端口。调用方持 Node 行锁,与 materialize 串行化。
发布段派生:节点 runtime 的 wireguard_port_range(external)与 wireguard_internal_port_range(internal)由 materializer 从 port_pools 注入。schema 的 normalize 钩子对两段做并集校验,并各注入一条 UDP 发布。
base_template 里的显式段位是过渡门控——存量端口迁完前优先生效;迁移清除后端口池成为唯一口径。
端口段位收窄有一个直接的运维收益:docker-proxy 为每个发布端口起一个进程,段位从上百个收到几十个之后,这些进程的常驻内存随之显著下降。
对等命名规范
单一事实源是 services/peer_naming。
| 资源 | 规则 | 例 |
|---|---|---|
| Peering | <slug>_<asn>_<node> | exploro_4242421771_hkg1 |
| WG 接口 | as<asn>(≤15 字符) | as4242421771 |
| BGP 会话 | <slug>_<asn>_<node>_<af>,af ∈ v6_v4(MP-BGP)/ v4 / v6 | exploro_4242421771_hkg1_v6_v4 |
slug 是对端短名,取值按优先级:
- 既有 BGP 会话名的首段——保留运维与对端沟通时惯用的名字,规范化不改口;
- 既有 Peering 名;
- registry
as-name归一(NEXUS-AS→nexus); - 兜底
as<asn>。
全程只用 [a-z0-9_],对 BIRD 标识符与 Linux 接口名都安全。
观测侧不按名字反推。 live、metrics、routes 一律经 peering_id 关联查该对等的实际会话名——名字里含 slug 与节点,无法从 ASN 猜出来。
可用连接数与开关
- 节点可用连接数等于外部端口池剩余量——端口才是真资源。
autopeering_node_policies.max_sessions是可选的额外上限,NULL表示只受端口池约束。用户面同时给出free_ports与取二者较小值的remaining。 - 是否接受自动对等是节点级、存库的开关(
autopeering_node_policies.enabled)。前端用PATCH /admin/autopeering/nodes/{node_id}传{"enabled": false}即可切换——部分更新,不必回填endpoint_host与展示元数据;PUT仍是全量替换语义。
关闭后该节点从用户面清单消失、新提交 404;已落成的对等不受影响——停的是「接纳新对等」。拆除既有对等走标准 Peering 管理 API。
运维视角见 自动对等运维。
存量收编端点
三个管理面端点,幂等、支持 dry_run。它们是为把历史形态收编到上述口径而存在的,在已收编的 fleet 上跑是 no-op,也可用于核对当前状态。
执行顺序:
POST /admin/nodes/{id}/peerings/backfill——先修 orphan 关联。孤儿接口与会话没有 Peering 行,后续两步按聚合根遍历就会漏掉它们。POST /admin/fleet/migrate-keys——每节点取一把典范钥(外部对等在用的那把,统一到它意味着外部对端零配合)入库;全部接口 spec 归一为该字面串,历史写入的引用形态也在此归一。per-link 分叉的接口(早期「每条链路独立 keypair」的产物)随之换成节点密钥,其对端的 peer 公钥同事务跟随改写。内部链路两端都受管,换钥只有一次重握手的瞬断。
POST /admin/fleet/normalize-peer-names——存量对等命名规范化,顺带清理空壳 Peering。改名会重建对应的 BIRD protocol 与 WG 接口,一次瞬断。route collector 的喂送会话跳过。POST /admin/fleet/migrate-internal-ports——内部链路的listen_port迁入 internal 池,引用旧端口的对端 endpoint 同事务改写。节点全部端口都能被池解释之后,清除base_template里的过渡段位。每条链路的两端在同一事务与同一广播轮内保持一致。
先跑 dry_run 审阅 details,再实跑。
部署顺序(硬约束)
agent 先行——必须是认识
wireguard_internal_port_range的版本。旧 agent 的StrictModel会拒收携带新 runtime 字段的 DesiredState。控制面代码本身可以先上:新字段的注入被
base_template的过渡段位门控,迁移前不会产出。控制面部署——新表由迁移链建出。
依序跑收编端点——迁移会触发全 fleet 容器重建(发布段变化进
config_hash),内部 mesh 有一次滚动重握手,收敛与自愈回路兜底。
顺序的普遍规则见 锁步契约。