feat(ssf): 实现安全事件一键连接与凭据托管

新增数据库驱动的 SecurityEventConnectionManager,复用 RFC 7662 机器 Client,自动完成 Discovery、Push Bearer 生成、Stream 创建、Verification、首次启用、零停机轮换和安全退役。

增加文件与 Kubernetes SecretStore、最小权限 RBAC、动态 Receiver/撤销水位/内省降级,以及系统设置管理页面。已通过全量 Go 测试、go vet、关键包竞态测试、27 个 Web 测试、类型检查、生产构建、Compose 和 Kubernetes dry-run。
This commit is contained in:
2026-07-15 17:25:00 +08:00
parent f30aaeb2d4
commit 9efeb16fd1
31 changed files with 2738 additions and 227 deletions
+48 -37
View File
@@ -1,56 +1,67 @@
# SSF/CAEP 实时会话撤销运行手册
Gateway 可选接收 Auth Center 通过 RFC 8935 Push 投递的 RFC 8417 Security Event Token,并处理 OpenID CAEP `session-revoked`默认关闭;关闭时不会注册接收路由或启动状态机,也不会改变 OIDC、BFF、API Key Legacy JWT 行为。
Gateway 可选接收 Auth Center 通过 RFC 8935 Push 投递的 RFC 8417 Security Event Token,并处理 OpenID CAEP `session-revoked`功能由数据库中的连接资源控制:没有连接时保持原有 OIDC、BFF、API Key Legacy JWT 行为;不再使用 `OIDC_SECURITY_EVENTS_ENABLED`
## 启用前提
## 用户接入流程
- Auth Center 已创建属于当前 Application/租户的 paused StreamAudience 为 `urn:easyai:ssf:receiver:{application-public-id}`
- Gateway OIDC `iss``tid` 与 Stream 绑定完全一致;Stream ID 为公开 UUID,不使用认证内核 ID。
- 接收 Bearer、Management Client Secret、RFC 7662 Client Secret 仅从 Git 忽略的 `.env`、Kubernetes Secret 或 Secret Manager 注入。
- `OIDC_SECURITY_EVENTS_PUBLIC_ENDPOINT` 必须是外部可访问的精确 HTTPS 地址;本地测试环境才允许 localhost HTTP。
- Management Client 至少有 `ssf.stream.read ssf.stream.verify`,不能使用浏览器 PKCE Client。
用户只执行两项操作:
完整配置键见仓库根目录 `.env.example`。启用时 `OIDC_SECURITY_EVENTS_ENABLED=true`,且 RFC 7662 Client ID/Secret 必须同时存在,否则启动失败
1. 在认证中心 Application 的“安全事件流”页面选择现有 RFC 7662 机器 Client,点击“准备 Gateway 接入”。认证中心自动合并 SSF 管理 Scope;不会创建第二个 Client
2. 在 Gateway“系统设置 → 认证中心安全事件”中填写认证中心公开 SSF Issuer,点击“连接认证中心”。
## 启用顺序
Gateway 后端随后自动读取 Discovery、生成并托管 256 bit Push Bearer、创建 paused Stream、完成 Verification、首次启用 Stream,并进入 360 秒 RFC 7662 bootstrap。浏览器看不到 Push Bearer、OAuth Secret 或 Secret 引用,管理员不编辑 Secret 文件,也不需要重启 Gateway。
1. 运行数据库迁移,部署仍保持功能关闭。
2. 在 Secret Store 创建接收 Bearer、Management Client Secret 和内省 Client Secret;不要把值放进日志或工单。
3. 启用 Gateway。启动模式为 `bootstrap`,所有 OIDC Token 都先内省。
4. 在 Auth Center 对 paused Stream 发起 Verification,确认 Gateway 返回空 Body 的 `202`
5. 启用 Stream。首次 Verification 成功后仍内省 360 秒,覆盖启用前已签发的最长 300 秒 Token。
6. 观察 `easyai_gateway_ssf_mode{mode="push_healthy"}` 变为 1,再执行测试用户撤销。
前置配置只有既有 OIDC/RFC 7662 配置和 Gateway 公共地址:
健康时 Gateway 每 60 秒请求一次 Verification,不按业务 QPS 请求 Auth Center。180 秒没有匹配的有效 Verification 会进入 `introspection_fallback`;只有最近收到的 Stream 状态为 `enabled` 时,连续两次 Verification 成功才会恢复,`paused`/`disabled` 状态下心跳不会错误恢复推送信任。降级期间内省不可用会对 OIDC 返回可审计的 503,API Key 继续工作。
- `OIDC_ISSUER``OIDC_TENANT_ID`
- `OIDC_INTROSPECTION_ENABLED=true`
- `OIDC_INTROSPECTION_CLIENT_ID/SECRET`,与认证中心准备 Receiver 时选择的机器 Client 相同;
- `AI_GATEWAY_PUBLIC_BASE_URL`,生产必须是认证中心可访问的 HTTPS 地址。
## 零停机 Bearer 轮换
第一版一个 Gateway 部署只允许一个认证中心连接。下游业务服务无需接入 SSF。
1. 在 Gateway 写入 `OIDC_SECURITY_EVENTS_BEARER_SECRET_NEXT`,同时接受 current/next。
2. 更新 Auth Center 的 Stream `credential_ref` 指向 next。
3. 发起 Verification,并确认投递和状态机健康。
4. 等待至少 180 秒后,将 next 提升为 current 并清空旧值。
## SecretStore
Gateway 使用常量时间比较两个值。响应、日志、审计和指标都不得包含 Authorization Header 或原始 SET
本地和 Docker 使用 `file` 驱动。服务自动创建目录并强制目录 `0700`、文件 `0600`Docker Compose 将目录放在持久化 `api_data` 卷中。用户不写入任何 `ssf-delivery-*` 文件
## 监控与告警
Kubernetes 使用 `kubernetes` 驱动和一个部署时预创建的空 Secret:
`GET /metrics` 输出以下低基数指标;生产入口应仅允许监控网络访问该路径:
```text
OIDC_SECURITY_EVENTS_SECRET_STORE=kubernetes
OIDC_SECURITY_EVENTS_KUBERNETES_NAMESPACE=easyai
OIDC_SECURITY_EVENTS_KUBERNETES_SECRET_NAME=easyai-gateway-security-events
```
- `easyai_gateway_ssf_receipts_total{outcome}`accepted/rejected/duplicate
- `easyai_gateway_ssf_sessions_deleted_total`:被撤销删除的 BFF Session。
- `easyai_gateway_ssf_watermark_rejections_total`:本地水位拒绝的 Token。
- `easyai_gateway_ssf_processing_duration_seconds`:Receiver 数据库事务处理直方图,用于 P99 告警。
- `easyai_gateway_ssf_verification_age_seconds``easyai_gateway_ssf_mode{mode}`
- `easyai_gateway_ssf_heartbeat_requests_total{outcome}`
- `easyai_gateway_oidc_introspection_total{outcome}`
- `easyai_gateway_jwks_refresh_failures_total{source}`SSF/OIDC JWKS 刷新失败。
参考清单见 `deploy/kubernetes/security-events-secret-rbac.yaml`。ServiceAccount 只能对这个固定 Secret 执行 `get/update/patch`,不能创建、删除或读取其他 Secret。数据库、API、浏览器、日志和审计只保存或展示非敏感连接元数据
至少配置以下告警:Verification age 超过 120 秒、fallback 超过 5 分钟、内省 failed 增长、SET rejected 增长、撤销端到端 P99 超过 3 秒。日志只能携带脱敏 Trace ID、Audit ID 和错误类别。
## 动态运行与状态
## 回滚
Receiver 路由始终注册:没有连接时返回无敏感信息的 `404`;存在连接但凭据丢失时返回 `503` 并进入 `introspection_fallback`。OIDC Evaluator 始终挂载但在无连接时无副作用。
先在 Auth Center 暂停 Stream,再保持 Gateway 启用但持续使用 RFC 7662,确认没有遗漏撤销后才关闭接收功能。保留迁移表和 Secret,不做破坏性数据库回滚。Legacy HS256 Token 和 API Key 不在本协议撤销范围内
连接生命周期包括 `connecting``verifying``bootstrap``enabled``degraded``rotating``disconnect_pending``retiring`。健康时每 60 秒一次 Verification,不随业务请求 QPS 增长;180 秒无匹配 Verification 时切换 RFC 7662。内省也不可用时 OIDC Fail ClosedAPI Key 继续工作
## 验收证据
Verification 成功后仅在首次连接和主动轮换时自动启用 Stream。认证中心管理员之后手工暂停或禁用 Stream,Gateway 的“重新验证”只检查链路,不会擅自恢复远端状态。
真实链路验收只能使用明确标记为测试用途的 Application,且必须在自动化测试通过后执行。报告需包含脱敏 Trace、Claims 结构、截图、Audit ID、投递延迟、fallback 与恢复证据。涉及人工、真实设备或第三方操作时记录 `SKIPPED_HUMAN_OR_THIRD_PARTY_REQUIRED`Mock、契约和安全测试不得跳过。
## 轮换与断开
“轮换凭据”由 Gateway 自动完成:生成 next、Receiver 同时接受 current/next、暂停并更新远端 Stream、Verification、重新启用、保留旧值至少 180 秒,最后提升 next 并删除旧值。中途失败保留两个凭据和 `rotating` 状态,可安全重试。
“断开连接”先删除远端 Stream,再进入至少 360 秒 `retiring`,期间继续应用撤销水位和 RFC 7662,避免旧 Token 因关闭功能重新有效。远端不可用时保持 `disconnect_pending` 并指数退避重试;不会提前删除 Push Secret。收据、水位和脱敏审计数据不会清表。
所有写管理接口要求 Gateway `Manager` 权限、`Idempotency-Key``If-Match`
```text
GET /api/admin/system/identity/security-events/connection
PUT /api/admin/system/identity/security-events/connection
POST /api/admin/system/identity/security-events/connection/verify
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 不在本协议撤销范围内。
真实链路只使用明确标记为测试用途的 Application,并且必须在自动化测试后执行。报告包含脱敏 Trace、Claims、截图、Audit ID、投递延迟、fallback 和恢复证据;需要人工或第三方操作时记录 `SKIPPED_HUMAN_OR_THIRD_PARTY_REQUIRED`