easyai-ai-gateway/docs/standard-identity-runtime-configuration.md
chengcheng 5b8178b703 docs(identity): 记录统一认证恢复与重配边界
补充管理员从 Active 禁用到新接入码重配的标准流程,说明 credentials_saved 恢复、SSF 凭据交接、持久清理、Break-glass 与动态 Origin 边界,并明确历史 Revision 仅作审计、当前不支持直接回滚。
2026-07-17 18:32:04 +08:00

108 lines
10 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.

# 统一认证运行时配置与标准应用接入
## 状态
Accepted2026-07-17。
## 决策
Gateway 是标准应用接入协议的首个消费方。认证中心保持消费方无关“统一认证”页面、Gateway 本地租户映射、Legacy JWT 兼容和 Session 策略均属于 Gateway 自己的管理边界。
OIDC、JIT、Introspection、BFF 和 SSF 的业务配置以 Gateway 管理 API/数据库为唯一来源。数据库、SecretStore、Bootstrap 管理凭据、TLS 与 RBAC 仍由部署环境提供Machine Credential 和 Session Encryption Key 由服务端生成或领取,只存入 SecretStoreRevision 仅保存引用。开发阶段不读取或迁移旧 `OIDC_*`、`VITE_OIDC_*` 业务变量,升级后由管理员重新配对。
## Revision 状态机
```text
draft -> validated -> active
draft | validated -> failed
active -> active (重新验证)
active -> superseded (禁用)
superseded (只读历史,不可重新验证或激活)
```
同一时刻最多一个 Active Revision。Revision 保存非敏感配置与 Secret 引用,不保存机器 Secret 或 Session 加密根。Auth Center 当前会复用并修改远端 OAuth/SSF 资源,因此旧 Revision 无法证明 Redirect URI、Scope、Audience、Client Secret 和 Stream 仍与历史快照一致。直接回滚以及 `superseded -> validated -> active` 均被服务端拒绝;恢复旧配置也必须先禁用,再使用新接入码完成一次新的远端交接。
## 管理员标准流程
1. 先确认至少一个本地 `source=gateway`、已启用且具有 Manager/Admin 权限的 Break-glass 密码凭据可登录。
2. 若已有 Active先点击“禁用统一认证”。启用 SSF 时Gateway 会先删除远端 Stream只有连接进入 `retiring` 后才封存 Active 和删除 BFF Session。
3. 在 Auth Center 通用 Application 的“应用接入”向导选择能力、确认预览并生成一张新的接入码。
4. 在 Gateway 填写 Auth Center、接入码、Gateway API/Web 地址和本地租户映射。
5. 等待 Pairing `completed`,保存本地策略,执行“验证配置”,再执行“激活配置”。
当前流程是受控停机切换,不宣称跨配置零停机。要支持 Active 直接换绑或历史 Revision 一键回滚,必须先在 Auth Center 实现远端 OAuth/SSF 资源版本化与可证明的双版本交接。
## 激活顺序
1. 从 Draft 和 SecretStore 构建不可变候选 Runtime。
2. 验证 Discovery、Issuer、JWKS、Audience、Tenant、Client、本地租户、Introspection 和可选 SSF Discovery。
3. 确认存在可用的本地 Break-glass Manager。
4. 在数据库事务中确认没有其他 Active 或配对启动预留,再把 Revision 原子设为 Active。
5. 原子替换进程内 Runtime 引用;在途请求继续使用旧实例。
6. 数据库提交结果不确定时,运行时先 fail closed再由后台协调器按数据库中的唯一 Active 自动重建;不得部分启用。
验证失败不会修改当前 Active Runtime首次配置失败时统一认证保持关闭本地管理登录继续可用。
## 配对与 Secret 边界
管理员只填写认证中心地址、一次性接入码、Gateway API/Web 公网地址、本地租户映射和 Legacy JWT 开关。Gateway 后端领取接入码、提交精确 URI、轮询资源状态、获取 Manifest 与一次性机器凭据,并先写入 SecretStore 再确认 Exchange 完成。
接入码、Exchange Token、机器 Secret 不进入 URL、数据库、日志、审计、指标或浏览器持久存储。SecretStore 写入失败时请求认证中心轮换,旧 Secret 立即失效。
配对主状态为 `metadata_pending -> preparing -> ready -> credentials_saved -> completed`。`failed`、`expired` 和 `cancelled` 是终态;可重试步骤保留当前主状态,并只记录白名单内的脱敏错误分类。这样页面既能说明当前停在哪一步,也不会把 Discovery URL、Token、Secret 或底层响应写入数据库和浏览器。
`cancelled` 只表示管理员放弃 Gateway 本地的未激活配对,不伪装成已撤销认证中心中已经领取的 Exchange 或历史凭据。Gateway 使用独立的 `cleanup_status``none`、`pending`、`completed`)跟踪本地补偿:
1. `POST /api/admin/system/identity/pairings/{pairingID}/cancel` 要求 Manager、`Idempotency-Key` 和 Pairing `If-Match`
2. 数据库事务先把未激活 Revision 封存为 `failed/pairing_cancelled`,再把 Pairing 标记为 `cancelled/pending`
3. 后台任务停止原配对 Worker只销毁该 Revision 的 Exchange Token、机器凭据、Session Key 和以精确 owner key 创建的 SSF 连接;不得删除共享或 Active 连接。
4. 绑定了 Stream 的 SSF 连接遵循既有安全退役窗口;服务重启后自动恢复 `pending` 清理。
5. 只有清理进入 `completed` 后才允许提交下一张接入码。下一次凭据交付会轮换机器 Secret因此不依赖回放旧 Secret。
管理员页面在 `credentials_saved` 等处理中状态展示稳定错误分类并继续自动重试,同时始终提供“放弃并清理本次配对”。`failed`、`expired` 或验证失败的未激活草稿也必须提供相同恢复入口;清理完成后重新显示标准配对表单。
`credentials_saved` 只表示 Manifest 与机器凭据已经安全保存,不代表 SSF Stream 已建立或 Revision 已可激活:
- Discovery、远端暂时不可用和 `security_event_retirement_pending` 由后台重试。
- `security_event_connection_conflict` 只能通过当前 Pairing 作用域的“安全退役现有连接”处理。
- `security_event_credential_handoff_unsafe` 表示旧连接与新 Issuer/Client 不匹配;系统不会尝试旧凭据,管理员应在原配置下断开,或放弃本次 Pairing 并重新生成接入码。
- 放弃后必须等 `cleanup_status=completed`,再提交新接入码。
`credentials_saved` 因已有 SSF owner 冲突而等待时,页面只能调用 Pairing 作用域的退役操作。服务端同时校验 Pairing ID、版本、错误分类和 Revision owner只退役不属于当前 Revision 的旧连接;一旦当前 Revision 已拥有连接,陈旧操作自动成为 no-op。退役期间持久化 `security_event_retirement_pending`,页面隐藏重复退役按钮并显示自动继续。服务重启时除恢复处理中 Pairing 和 Cleanup 外,还会为“已完成、待验证/激活”的 Revision 从 SecretStore 重建 prepared Receiver。
旧 SSF 连接需要换用新机器凭据时Gateway 先用新凭据向新 OIDC Issuer 取得管理 Token再对旧 Transmitter 执行经过认证的 Stream 读取;存在 Stream 时必须精确核验 Stream ID、Issuer、Receiver 和 Audience。只有该端到端证明成功后才替换本地凭据引用并进入旧连接退役证明失败不会发送旧 Secret、不会覆盖引用也不会自动删除连接。
后台协调器每 5 秒恢复持久状态:优先继续 `cleanup_status=pending`,再恢复唯一的处理中 Pairing然后恢复 `completed` 但尚未激活的 prepared Receiver并校准提交结果不确定的 Active Runtime。SSF `retiring` 的最终清理由服务生命周期 Worker 托管,不随旧 Runtime 的 30 秒关闭而取消;到达安全窗口后,数据库事务先把全部 Secret 引用移交给持久清理队列,再删除精确的连接 generation。
SSF 凭据写入采用持久化 Secret 清理队列新引用先以延迟清理记录暂存数据库事务在引用生效时撤销暂存记录并把旧引用原子加入到期清理队列。SecretStore 删除完成后才删除队列记录;进程在写入、切换或删除任一步退出,重启任务都能继续收敛。队列只保存非敏感引用和时间,不保存 Secret 值。
## 威胁模型摘要
- 伪造/越权:所有管理写操作要求 Manager、`Idempotency-Key`、`If-Match` 和写前审计;审计存储不可用时拒绝变更。管理 API 不接受 API KeyOIDC/JWT 不接受 `?key=` URL 传递。
- 篡改Manifest 使用严格 schema_version 和字段白名单Issuer 与 Discovery issuer 必须精确相等。
- 信息泄露:公开运行时接口只返回启用状态、登录/退出入口和脱敏健康状态。
- SSRF认证中心地址必须通过协议与地址策略校验禁止 userinfo、重定向和非开发 HTTPDiscovery/Token/Introspection 请求使用超时与响应大小上限。
- 锁死:激活和禁用前确认本地 Break-glass Manager 可用Break-glass Token 使用独立 `local_break_glass_manager` purpose。即使 Active Runtime 构造失败,持久化 Active 的精确 Web Origin 仍可用于恢复管理Cookie 身份校验继续 fail closed。
- 降级OIDC 校验 fail closedIntrospection/SSF 不可用时遵循已有降级窗口,不绕过身份校验。
## 前端运行时配置
Web 前端启动后读取公开只读统一认证状态,不使用 `VITE_OIDC_*` 构建变量。安全事件作为统一认证能力状态显示,原独立页面只保留运行运维动作。
Active Revision 的 `WebBaseURL` 会以精确 scheme + host 动态加入 CORS禁止 `*`Cookie CSRF 只信任当前健康 Active 的 Web Origin。第一次接入或确认禁用后没有 Active 时,跨 Origin 管理 UI 仍需部署级精确 `CORS_ALLOWED_ORIGIN`,或由反向代理以同源 `/gateway-api` 提供 API。该 bootstrap Origin 是管理平面的网络边界,不是 OIDC 业务配置。
## 部署级配置
允许继续由部署环境提供的参数仅限 SecretStore、管理面 bootstrap Origin 与基础设施健康窗口:
```dotenv
IDENTITY_SECRET_STORE=file
IDENTITY_SECRET_DIR=.local-secrets/identity
IDENTITY_SECURITY_EVENTS_HEARTBEAT_INTERVAL_SECONDS=60
IDENTITY_SECURITY_EVENTS_STALE_AFTER_SECONDS=180
IDENTITY_SECURITY_EVENTS_CLOCK_SKEW_SECONDS=60
CORS_ALLOWED_ORIGIN=https://gateway-admin.example.com
```
Kubernetes 部署改用 `IDENTITY_KUBERNETES_NAMESPACE`、`IDENTITY_KUBERNETES_SECRET_NAME`、`IDENTITY_KUBERNETES_API_SERVER`、`IDENTITY_KUBERNETES_TOKEN_FILE` 和 `IDENTITY_KUBERNETES_CA_FILE`。这些参数不包含 Issuer、Audience、Scope、Client ID、Machine Secret 或业务开关。