From 5b8178b7033708c3e5696dcba15176a912d21de4 Mon Sep 17 00:00:00 2001 From: chengcheng Date: Fri, 17 Jul 2026 18:32:04 +0800 Subject: [PATCH] =?UTF-8?q?docs(identity):=20=E8=AE=B0=E5=BD=95=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E8=AE=A4=E8=AF=81=E6=81=A2=E5=A4=8D=E4=B8=8E=E9=87=8D?= =?UTF-8?q?=E9=85=8D=E8=BE=B9=E7=95=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 补充管理员从 Active 禁用到新接入码重配的标准流程,说明 credentials_saved 恢复、SSF 凭据交接、持久清理、Break-glass 与动态 Origin 边界,并明确历史 Revision 仅作审计、当前不支持直接回滚。 --- docs/oidc-jit-provisioning.md | 9 +-- docs/security/ssf-session-revocation.md | 17 +++++- ...standard-identity-runtime-configuration.md | 59 ++++++++++++++++--- 3 files changed, 70 insertions(+), 15 deletions(-) diff --git a/docs/oidc-jit-provisioning.md b/docs/oidc-jit-provisioning.md index 770d1c2..12d1aae 100644 --- a/docs/oidc-jit-provisioning.md +++ b/docs/oidc-jit-provisioning.md @@ -21,10 +21,11 @@ OIDC、JIT 和浏览器会话不读取业务环境变量。管理员在 Auth Cen - 浏览器只保存 32 字节随机、`HttpOnly + SameSite=Strict` 的 Session Cookie。新标签页通过该 Cookie 调用 `/api/v1/me` 恢复登录态;Gateway 不签发第二枚 JWT。 - Access Token 继续保持 5 分钟。认证请求发现剩余时间不超过 60 秒时使用 Refresh Token 自动刷新;多实例通过 PostgreSQL 刷新租约保证同一版本只刷新一次,并原子保存旋转后的 Refresh Token。 - Session 闲置 30 分钟、绝对最长 8 小时。闲置 5~30 分钟后的首个请求可自动刷新;超过闲置或绝对期限不会刷新,必须重新登录。浏览器不运行定时刷新。 -- 所有 Web API 请求使用 `credentials: include`。Cookie 鉴权的 POST、PUT、PATCH、DELETE 必须携带 `CORS_ALLOWED_ORIGIN` 白名单中的 Origin,否则返回结构化 403。 +- 所有 Web API 请求使用 `credentials: include`。Cookie 鉴权请求的 Origin 必须与当前健康 Active Revision 的 Gateway Web 地址精确匹配,否则返回结构化 403;不接受 `*`。 - Cookie 的 `Secure` 属性由 Revision 中的 Gateway API 公网地址推导:生产地址只允许 HTTPS;本地开发允许 localhost HTTP。可信 Origin 由 Gateway Web 地址精确推导,不接受 `*`。 - `POST /api/v1/auth/oidc/logout` 校验可信 Origin、删除本地 Session、使用公共 Client ID 撤销 Refresh Token,并跳转 Auth Center 退出地址;`DELETE /api/v1/auth/oidc/session` 保留为幂等的本地删除接口。 -- Session Encryption Key 由 Gateway 服务端生成,只以 Secret 引用进入 Revision,不得复用 JWT Secret,也不会进入数据库、响应、日志或浏览器。禁用统一认证会清理现有 BFF Session;回滚后用户需要重新登录。 +- 第一次接入、确认禁用或 Active Runtime 故障恢复时,跨 Origin 管理页面使用部署级精确 bootstrap CORS;生产部署建议由反向代理通过同源 `/gateway-api` 暴露 API。该配置不包含 Issuer、Client 或 Scope。 +- Session Encryption Key 由 Gateway 服务端生成,只以 Secret 引用进入 Revision,不得复用 JWT Secret,也不会进入数据库、响应、日志或浏览器。禁用统一认证会清理现有 BFF Session;重新配对后用户需要重新登录。 ## 数据和事务语义 @@ -53,8 +54,8 @@ OIDC、JIT 和浏览器会话不读取业务环境变量。管理员在 Auth Cen `ErrLocalUserRequired` 仅作为防御性内部错误保留,对外统一转换为结构化 403。 -## 验收与回滚 +## 验收与重新接入 自动化门禁通过后,依次验证本地真实 OIDC 和 `auth.51easyai.com` Staging 专用测试租户。证据应包含脱敏 Claims、网络截图、本地 Gateway 用户记录、Trace ID、Gateway/Auth Center 审计 ID及 200/401/403/503 负向证据;不得保存 Token、授权码、密码或 Secret。 -JIT 策略变更通过创建并验证新 Revision 后激活;身份配置回滚使用“系统设置 → 统一认证”的回滚操作。激活、回滚和禁用均要求本地 Break-glass Manager 可用,并清理受影响的 BFF Session。已经创建的 `source=oidc` 测试投影保留为惰性数据,不自动删除、迁移或合并。 +Active Revision 可原位重新验证。需要改变远端身份资源时,先确认本地 Break-glass Manager,禁用当前配置,再使用 Auth Center 新接入码创建、验证和激活新 Revision;历史 `superseded` Revision 只用于审计,不能直接回滚或重新激活。已经创建的 `source=oidc` 测试投影保留为惰性数据,不自动删除、迁移或合并。 diff --git a/docs/security/ssf-session-revocation.md b/docs/security/ssf-session-revocation.md index f832f8d..a199c54 100644 --- a/docs/security/ssf-session-revocation.md +++ b/docs/security/ssf-session-revocation.md @@ -48,6 +48,19 @@ Verification 成功后仅在首次连接和主动轮换时自动启用 Stream。 “断开连接”先删除远端 Stream,再进入至少 360 秒 `retiring`,期间继续应用撤销水位和 RFC 7662,避免旧 Token 因关闭功能重新有效。远端不可用时保持 `disconnect_pending` 并指数退避重试;不会提前删除 Push Secret。收据、水位和脱敏审计数据不会清表。 +禁用统一认证也遵循相同顺序:若 Active Revision 开启了 SSF,只有远端 Stream 已删除且本地连接进入 `retiring` 后,Active 才会变为 `superseded`,BFF Session 才会清理。`disconnect_pending` 时禁用返回明确的 retirement pending,数据库 Active 保持不变;即使完整 OIDC Runtime 已 fail closed,Gateway 也会只重建 SSF 管理面完成断开,不要求 Discovery、JIT 或 BFF 先恢复。`retiring` 最终清理由服务生命周期 Worker 托管,Gateway 重启后仍会继续。 + +## 旧连接交接与配对恢复 + +Pairing 停在 `credentials_saved` 时,Manifest 和机器凭据已经保存,但 Stream 可能仍在创建、验证或退役: + +- 暂时性 Discovery/Transmitter 错误和 `security_event_retirement_pending` 会自动重试。 +- `security_event_connection_conflict` 只允许通过当前 Pairing 的“安全退役现有连接”处理。 +- `security_event_credential_handoff_unsafe` 表示旧连接与新 Issuer/Client 不匹配;系统不会把旧凭据发往新端点。管理员应在原配置下断开,或放弃当前 Pairing。 +- 放弃后等待 `cleanup_status=completed`,再从 Auth Center 生成新接入码重新配对。 + +若旧连接与新配置具备可证明的同一管理边界,Gateway 仍不会直接覆盖 S1:先用新机器凭据 S2 从新 OIDC Issuer 取得管理 Token,再经过认证读取旧 Transmitter;存在 Stream 时精确校验 Stream ID、Issuer、Receiver 和 Audience。证明成功后才原子切换凭据引用并把 S1 交给持久清理队列;失败时 S1/S2 引用和远端连接都保持不变。 + 所有写管理接口要求 Gateway `Manager` 权限、`Idempotency-Key` 和 `If-Match`: ```text @@ -58,10 +71,10 @@ POST /api/admin/system/identity/security-events/connection/rotate-credential DELETE /api/admin/system/identity/security-events/connection ``` -## 监控、回滚与验收 +## 监控、退役与验收 `GET /metrics` 输出接收结果、删除 Session 数、水位拒绝数、Verification 年龄、健康模式、内省结果、JWKS 失败和处理延迟。至少告警 Verification age 超过 120 秒、fallback 超过 5 分钟、内省失败、SET 拒绝增长以及撤销 P99 超过 3 秒。日志只记录脱敏 Trace/Audit ID 和错误类别。 -回滚时先在认证中心暂停 Stream,再通过 Gateway 安全断开;保留迁移表、水位和审计。Legacy HS256 Token 和 API Key 不在本协议撤销范围内。 +当前禁止直接激活历史 `superseded` Revision,因为 Auth Center 会复用并修改远端 OAuth/SSF 资源,旧快照无法证明仍然匹配。恢复配置时先通过 Gateway 安全禁用并退役 Stream,再使用 Auth Center 新接入码重新配对;保留迁移表、水位和审计。Legacy HS256 Token 和 API Key 不在本协议撤销范围内。 真实链路只使用明确标记为测试用途的 Application,并且必须在自动化测试后执行。报告包含脱敏 Trace、Claims、截图、Audit ID、投递延迟、fallback 和恢复证据;需要人工或第三方操作时记录 `SKIPPED_HUMAN_OR_THIRD_PARTY_REQUIRED`。 diff --git a/docs/standard-identity-runtime-configuration.md b/docs/standard-identity-runtime-configuration.md index 0638059..365c4ab 100644 --- a/docs/standard-identity-runtime-configuration.md +++ b/docs/standard-identity-runtime-configuration.md @@ -13,22 +13,33 @@ OIDC、JIT、Introspection、BFF 和 SSF 的业务配置以 Gateway 管理 API/ ## Revision 状态机 ```text -draft -> validated -> active -> superseded +draft -> validated -> active draft | validated -> failed -superseded -> validated -> active -active -> superseded (回滚、替换或禁用) +active -> active (重新验证) +active -> superseded (禁用) +superseded (只读历史,不可重新验证或激活) ``` -同一时刻最多一个 Active Revision。Revision 保存非敏感配置与 Secret 引用,不保存机器 Secret 或 Session 加密根。关键身份字段变化时清理旧 BFF Session;回滚后用户重新登录。 +同一时刻最多一个 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. 在数据库事务中把 Revision 原子设为 Active,并把旧版本设为 Superseded。 +4. 在数据库事务中确认没有其他 Active 或配对启动预留,再把 Revision 原子设为 Active。 5. 原子替换进程内 Runtime 引用;在途请求继续使用旧实例。 -6. 若内存替换失败,恢复数据库指针并保持旧 Runtime;不得部分启用。 +6. 数据库提交结果不确定时,运行时先 fail closed,再由后台协调器按数据库中的唯一 Active 自动重建;不得部分启用。 验证失败不会修改当前 Active Runtime;首次配置失败时统一认证保持关闭,本地管理登录继续可用。 @@ -38,22 +49,51 @@ active -> superseded (回滚、替换或禁用) 接入码、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` 和审计;Exchange Token 仅授权一个 Exchange。 +- 伪造/越权:所有管理写操作要求 Manager、`Idempotency-Key`、`If-Match` 和写前审计;审计存储不可用时拒绝变更。管理 API 不接受 API Key,OIDC/JWT 不接受 `?key=` URL 传递。 - 篡改:Manifest 使用严格 schema_version 和字段白名单;Issuer 与 Discovery issuer 必须精确相等。 - 信息泄露:公开运行时接口只返回启用状态、登录/退出入口和脱敏健康状态。 - SSRF:认证中心地址必须通过协议与地址策略校验,禁止 userinfo、重定向和非开发 HTTP;Discovery/Token/Introspection 请求使用超时与响应大小上限。 -- 锁死:激活、禁用与回滚前确认本地 Break-glass Manager 可用。 +- 锁死:激活和禁用前确认本地 Break-glass Manager 可用;Break-glass Token 使用独立 `local_break_glass_manager` purpose。即使 Active Runtime 构造失败,持久化 Active 的精确 Web Origin 仍可用于恢复管理,Cookie 身份校验继续 fail closed。 - 降级:OIDC 校验 fail closed;Introspection/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 与基础设施健康窗口: +允许继续由部署环境提供的参数仅限 SecretStore、管理面 bootstrap Origin 与基础设施健康窗口: ```dotenv IDENTITY_SECRET_STORE=file @@ -61,6 +101,7 @@ 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 或业务开关。