feat: support permanent credentials and overlapping token rotation

This commit is contained in:
2026-09-27 11:45:15 +08:00
parent 87aa710013
commit 385a618154
29 changed files with 718 additions and 161 deletions

View File

@@ -2,7 +2,7 @@
状态:P1 已实现。业务接口每次在数据库事务内校验凭据摘要、撤销/到期、应用与命名空间启停,以及动作和队列范围。授权与写入使用同一事务;管理变更与已经获得授权锁的请求按事务先后次序生效。
已提供单管理员[应用接入页面](console-applications.md):新建应用、查看队列与凭据、重新生成 Token、复制 SDK 配置。下方 CLI 继续用于首次安装及运维。
已提供单管理员[应用接入页面](console-applications.md):新建应用、查看队列与凭据、长期凭据、调整有效期、平滑换证、复制 SDK 配置。下方 CLI 继续用于首次安装及运维。
## 初始化一个新的接入应用
@@ -21,7 +21,7 @@ go run ./cmd/orbit-admin bootstrap \
ORBIT_MYSQL_DSN 通过环境注入,命令不读取 .env。所有名称必填,以字母/数字开头,后续允许字母、数字、`_ . : -`;environment 最长 32,其余最长 128。输出文件必须不存在,以 0600 创建;原始凭据不会打印到 stdout/stderr。`.local/` 和 `*.credentials.json` 已被 Git 忽略。
输出 JSON 包含 application_id / namespace_id / queue_id / credential_id / token / expires_at。初始凭据有效期为 30 天,权限包括当前命名空间内的五个 P1 动作和全部队列。初始队列 max_concurrency=4、rate_per_second=10、max_unfinished_tasks=100000;并发、开始限速与积压背压已生效,语义及管理方式见 [队列接口](queues.md)。这些默认值是开发基线,不是容量承诺。
输出 JSON 包含 application_id / namespace_id / queue_id / credential_id / token,可选 expires_at。初始凭据默认长期有效(expires_at 省略),可用 `--valid-for 720h` 显式选择 30 天;0 表示长期,其他值为 1 小时至 365 天。权限包括当前命名空间内的五个 P1 动作和全部队列。初始队列 max_concurrency=4、rate_per_second=10、max_unfinished_tasks=100000;并发、开始限速与积压背压已生效,语义及管理方式见 [队列接口](queues.md)。这些默认值是开发基线,不是容量承诺。
如果希望在第一次初始化时同时创建控制台管理员,可在同一条 bootstrap 事务中提供账号,并从标准输入交付密码:
@@ -46,7 +46,7 @@ secret-manager read orbit/console-admin-password | \
## 轮换与撤销
管理 CLI 在受保护的数据库管理环境执行;控制台也可由部署指定的单管理员轮换凭据,见[应用接入](console-applications.md)。业务 SDK 不开放凭据管理,撤销仍由 CLI 执行。轮换保留 `credential_id`,因此订阅和 Worker 实例的授权绑定仍指向同一记录;新的 Worker 进程必须按现有进程身份规则重新连接,旧令牌不能再通过任何 API 或 WSS 认证。平台不会批量改写在途任务、交付记录或历史会话证据。
管理 CLI 在受保护的数据库管理环境执行;控制台也可由部署指定的单管理员轮换凭据,见[应用接入](console-applications.md)。业务 SDK 不开放凭据管理,撤销仍由 CLI 执行。轮换保留 `credential_id`,因此订阅和 Worker 实例的授权绑定仍指向同一记录;新进程仍遵循原 Worker 身份规则;过渡期内旧 Token 的 API 请求、原 WS 控制命令及跨节点重连都继续有效。新 Token 立即可用,结束过渡或超过截止后旧 Token 才被拒绝。平台不会批量改写在途任务、交付记录或历史会话证据。
先读取脱敏状态和当前版本:
@@ -63,12 +63,13 @@ go run ./cmd/orbit-admin credential rotate \
--namespace-id <namespace_id> \
--credential-id <credential_id> \
--expected-revision 1 \
--valid-for 720h \
--valid-for 0 \
--grace 168h \
--reason "scheduled credential rotation" \
--output .local/rotated.credentials.json
```
命令先读取并核对版本,生成一个固定 `operation_id`、令牌和绝对到期时间,写入新的 `0600` 私有操作文件,然后提交数据库事务。文件不得已存在;提交结果不明、网络断开或数据库返回未知错误时必须保留文件,并使用同一个文件执行 `credential apply --file`,不能重新执行 `rotate` 生成第二个秘密。`--valid-for` 限定在 1 小时到 1 年。数据库只保存秘密摘要、请求摘要和脱敏回执。
命令先读取并核对版本,生成一个固定 `operation_id`、令牌、可选绝对到期时间和固定过渡截止,写入新的 `0600` 私有操作文件,然后提交数据库事务。文件不得已存在;提交结果不明、网络断开或数据库返回未知错误时必须保留文件,并使用同一个文件执行 `credential apply --file`,不能重新执行 `rotate` 生成第二个秘密。`--valid-for` 默认 0(长期),也可配置 1 小时到 365 天;`--grace` 默认 168h(7 天),范围 0–720h,0 表示立即停用旧 Token。旧 Token 的实际截止取过渡截止与原到期时间的较早者,不复活已过期秘密。数据库只保存秘密摘要、请求摘要和脱敏回执。
```sh
go run ./cmd/orbit-admin credential apply \
@@ -77,6 +78,36 @@ go run ./cmd/orbit-admin credential apply \
`apply` 会严格拒绝软链接、非 0600 文件、未知 JSON 字段、重复 JSON 对象和修改过的操作内容。重复应用已提交的文件只返回原回执,回执中的 `deduplicated` 为 `true`,不会重新轮换或新增审计。
### 原 Token 调整有效期
无需换 Token:读取当前 revision 后执行以下命令,业务侧继续使用原配置。缩短有效期同样会生效,应按预期截止配置。
```sh
go run ./cmd/orbit-admin credential expiry \
--namespace-id <namespace_id> --credential-id <credential_id> \
--expected-revision 1 --valid-for 0 \
--reason "service credential remains valid until revoked" \
--output .local/expiry.operation.json
```
### 平滑切换的完整顺序
1. 生成新 Token,保留默认 7 天过渡。凭据 ID、权限、任务、订阅和交付身份不变。
2. 把新 Token 发布到调用方配置,逐台更新 API 服务、Worker 和结果消费者。SDK v0.1.1 的 `Options.Token` 在创建客户端时读取,修改 `.env` 不会自动刷新正在运行的客户端;新建客户端或滚动重启服务,按 [HTTP 宿主示例](../examples/http-service.md) 正常排空在途工作。
3. 在截止前确认所有旧进程已完成任务回报和 ACK,并已经退出或用新 Token 重新连接。HTTP 调用无须整体停机;持续执行能力依赖业务侧保留足够副本。单实例重启本身会产生短暂停顿。
4. 全部切换后可提前结束过渡;否则旧 Token 到时自动失效,认证使用数据库时间,不依赖定时清理。
```sh
go run ./cmd/orbit-admin credential finalize \
--namespace-id <namespace_id> --credential-id <credential_id> \
--expected-revision 2 --reason "all service instances switched and drained" \
--output .local/finalize.operation.json
```
结束过渡立即停用旧 Token,当前 Token 不变。长期 Token 仍受主动撤销、应用/命名空间停用约束。旧代仍在有效过渡期时再次轮换返回 409,避免误伤未切换服务;先完成本次切换。修改当前有效期不延长旧代截止。若旧 Token 本身即将到期而需要更长切换窗口,应先调整当前有效期,再发起换证。
新固定操作文件使用 version=2;历史 version=1 仍按原来的固定到期和立即轮换语义重放,不自动延长旧秘密。
发生泄露或 Worker 必须立即停用时执行撤销:
```sh
@@ -88,7 +119,7 @@ go run ./cmd/orbit-admin credential revoke \
--reason "incident response"
```
撤销同样先写入固定操作文件,再提交事务;提交不明时用 `credential apply --file .local/revoke.operation.json` 重试。撤销只标记凭据失效,不删除订阅、任务、交付或历史记录。即使应用或命名空间已停用,管理撤销仍可执行。重复应用同一撤销文件只返回原回执,不追加重复审计。审计主体取自 MySQL 管理连接的 `CURRENT_USER()`,审计摘要不包含秘密原文或秘密摘要。
撤销同样先写入固定操作文件,再提交事务;提交不明时用 `credential apply --file .local/revoke.operation.json` 重试。撤销使当前和过渡中的上一代同时失效,不删除订阅、任务、交付或历史记录。即使应用或命名空间已停用,管理撤销仍可执行。重复应用同一撤销文件只返回原回执,不追加重复审计。审计主体取自 MySQL 管理连接的 `CURRENT_USER()`,审计摘要不包含秘密原文或秘密摘要。
## 凭据格式与作用域
@@ -130,3 +161,7 @@ P3 结果订阅核心新增 `events:read`、`events:manage` 和 `events:consume`
P4定义发布新增`workflows:definitions:read`与`workflows:definitions:publish`,仅显式`--workflow-access`授予。创建/读取元数据作用于命名空间,发布及读取完整版本额外校验全部引用队列;默认提交/Worker凭据不获此权限。发布不创建任务,不要求tasks:submit,详见[定义API](workflow-definitions.md)。
P4/P5实例公共接入增加`workflows:start/read/control/signal/recover`及`workflows:audit:read`。五个独立CLI开关`--workflow-run-access`、`--workflow-control-access`、`--workflow-signal-access`、`--workflow-recovery-access`、`--workflow-audit-access`分别明确授予。前四个同时包含实例读取;audit只授予流程操作审计读取。原`--workflow-access`仍仅授予定义读/发布,任务`--recovery-access`不授予流程恢复。全部固定队列仍按当前凭据校验,详见[实例权限表](workflows.md#1-路由权限与-sdk)。初始化权限集合在交付凭据前校验。
## 部署升级
迁移 `000032` 为凭据表增加上一代摘要与 UTC 截止,并以 CHECK 约束成对为空;不新增外键、不改变已有秘密或有效期。新安装的基线同步到 32。按部署流程应用迁移并更新全部 Orbit 节点后再使用平滑换证;旧版本节点只认识当前摘要,不能在混合版本期间承诺旧 Token 可用。既有凭据不会被迁移自动改为长期,须通过有审计的 expiry 操作显式调整。