73 lines
3.4 KiB
Markdown
73 lines
3.4 KiB
Markdown
# Reality 密钥主控托管(Key Hosting)
|
||
|
||
Reality 密钥对由**控制面**生成、保存和下发,节点只是落地执行。这样节点重装
|
||
系统后重新部署的仍是同一把密钥,客户端订阅里的 `pbk`(publicKey)永不变化,
|
||
客户无需重新复制配置。
|
||
|
||
## 数据模型
|
||
|
||
- 控制面数据库 `nexusgate.json` 新增顶层对象 `keyStore`:
|
||
`{ "reality-<nodeId>": { "privateKey": "...", "publicKey": "...", "updatedAt": "..." } }`
|
||
- 密钥格式与此前一致:Node JWK 导出的 x25519 密钥对,`privateKey`/`publicKey`
|
||
为 base64url 无填充(43 字符),与 `sing-box generate reality-keypair` 输出一致。
|
||
- 旧数据库没有 `keyStore` 时自动补为空对象,不影响启动。
|
||
|
||
## 协议变更
|
||
|
||
### 下发(控制面 → Agent)
|
||
|
||
`apply_resource` 任务的 payload 新增可选字段:
|
||
|
||
```json
|
||
{
|
||
"resource": { "...": "inbound 里仍是 ${REALITY_PRIVATE:reality-<nodeId>} 占位符" },
|
||
"kind": "node",
|
||
"keyMaterial": { "reality-<nodeId>": { "privateKey": "...", "publicKey": "..." } }
|
||
}
|
||
```
|
||
|
||
- 控制面在构建部署任务时(`rebuildNode` / 对账 `reconcileServer`):节点是
|
||
Reality 协议且 `keyStore` 无此 keyId → 面板现场生成并入库;已有则复用。
|
||
- 出口(node-exit)任务不带 `keyMaterial`(出口资源没有 Reality 占位符)。
|
||
- `/api/jobs` 管理接口不返回 payload,私钥不会经管理 API 泄漏。
|
||
|
||
### Agent 行为(Go agent)
|
||
|
||
1. 收到 `keyMaterial` 时,先写入本地 `/etc/nexusgate/keys.json`(0600),
|
||
再做占位符 materialize —— 部署的必然是主控托管的那把钥匙。
|
||
2. 若某个被使用的 keyId **不是**本次 payload 提供的(本地已有或本地新生成),
|
||
在任务结果里回传:`result.artifacts.keyMaterial = { "<keyId>": { privateKey, publicKey } }`。
|
||
3. payload 无 `keyMaterial`(旧控制面)时行为与托管前完全一致。
|
||
|
||
Node 版 agent 不读 `keyMaterial` 字段,照旧本地生成/使用密钥,协议兼容;
|
||
其任务结果不带 `keyMaterial` 回传,控制面也不会被它改写(见下)。
|
||
|
||
### 回填(Agent → 控制面)
|
||
|
||
任务成功完成(`completeJob`)时,控制面合并 `artifacts.keyMaterial`:
|
||
|
||
- `keyStore` 无此 keyId → 入库(存量节点密钥由此自动迁入主控)。
|
||
- 已有且相同 → 无操作。
|
||
- 已有且**不同** → **保留主控的**,写一条 `reality_key_conflict` 审计日志;
|
||
下一次下发会把主控密钥重新发给 agent,agent 本地副本随之被纠正。
|
||
- 合并后节点的 `artifacts.realityPublicKey` 强制以 `keyStore` 为准,
|
||
保证订阅输出稳定。`keyMaterial` 本身不会存进 `node.artifacts`。
|
||
|
||
## 优先级
|
||
|
||
**主控 keyStore > 本次下发 payload > 节点本地 keys.json**
|
||
|
||
payload 的内容就是 keyStore 的副本,所以前两者永远一致;本地仅在主控
|
||
从未托管过该密钥的迁移期短暂生效,回填完成后即以主控为准。
|
||
|
||
## 删除清理
|
||
|
||
节点记录被真正删除时(草稿直接删除、删除任务确认后、服务器强制遗忘),
|
||
对应的 `keyStore["reality-<nodeId>"]` 一并移除。
|
||
|
||
## 恢复语义
|
||
|
||
节点重装系统 → 重新注册 agent → 主控重新下发节点资源,payload 携带原
|
||
keyMaterial → agent 落地同一私钥 → sing-box 配置与重装前一致,客户端
|
||
订阅(pbk + shortId)逐字不变,客户无感。
|