Files
NexusGate/KEYHOSTING.md

73 lines
3.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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)逐字不变,客户无感。