# 安全模型 | 项目 | 内容 | | --- | --- | | 状态 | Approved | | 最后更新 | 2026-09-10 | ## 保护目标 - 只有通过可信 verifier 验证并匹配 trust binding 的 workload 才能取得 JWT。 - 签发的 JWT 只能用于明确的 audience,且权限不能超过 SA binding 的 issuance rule。 - 客户端不能控制输出 subject、任意 claims、签名算法或最大 TTL。 - subject token、输出 bearer token 和签名私钥不得进入数据库、日志、metric 或 trace。 - 一个 issuer、principal 或策略配置错误不能静默扩大为所有 audience 的访问权。 - OpenBao 与 Kubernetes 保留各自的最终授权权威。 ## 信任边界 平台管理员可以配置 verifier、trust binding、audience、token profile 和 policy,因此是 本系统的高信任主体。上游 issuer 管理员能够为其信任域签发身份;其权限必须通过 trust binding 限制,不能因 issuer 被信任就获得所有 audience。 OpenBao Transit 保管 JWT 签名私钥。workload-sts 使用自己的 Kubernetes ServiceAccount JWT 登录 OpenBao,并取得只能请求指定 Transit key 签名和读取其公钥所需信息的短期 运行凭据。该凭据与客户端通过 JWT auth 获得的 Bao token 是不同安全域。资源服务器 通过 HTTPS 获取 metadata/JWKS 或使用固定信任配置验证 JWT。 数据库管理员可以改变签发配置,等价于 IAM 管理员;数据库备份和迁移必须按高敏感配置 处理,即使其中没有 bearer token 或私钥。 ## 主要威胁 ### Subject token 重放 攻击者取得尚未过期的 Kubernetes JWT 或 JWT-SVID 后可能重复交换。缓解措施包括: - 上游 token 必须使用短 TTL; - 严格校验其 audience 为 workload-sts; - 输出 JWT 使用短 TTL 和独立 audience; - 高风险 profile 可以要求在线状态验证或一次性机制; - 日志以安全指纹关联重复请求,不保存完整 token。 是否对所有 subject token 实现全局 `jti` 单次消费仍待决定;默认不能假定标准 ServiceAccount JWT 只能交换一次。 ### Audience confusion 验证必须检查上游 token 的 audience,而不只检查签名和 issuer。输出 token 的 `aud` 必须来自已登记 audience,禁止接受自由字符串后原样签入 JWT。 不同 Kubernetes cluster、OpenBao 实例和普通 API 应使用不同 audience。资源服务器必须 拒绝没有自己 audience 的 token。 ### Issuer confusion 不能只按 JWT `sub` 映射 principal。trust binding 至少绑定: ```text credential type + issuer + source subject ``` 同名 ServiceAccount 在不同 cluster 中是不同来源。issuer discovery、JWKS URL 和允许的 签名算法必须由管理员配置,不能由未验证 token 中的 URL 动态决定。 ### Claim injection 与权限扩大 请求中的 `scope`、`audience` 和 `client_id` 都是不可信输入。`scope` 和 `audience` 只 表示请求上限;v1 的必填 `client_id` 只用于审计且没有默认值。输出 claims 由 verifier 事实、trust binding、issuance rule 和服务端 token profile 共同生成。 CEL 只能读取经过类型化的上下文。未经验证的 Gitea repository、workflow、branch、 job ID、HTTP header 或自报 SPIFFE ID 不能用于扩大权限。 ### Algorithm 与密钥混淆 - 每个 issuer 固定允许的算法集合; - 拒绝 `none` 和未配置算法; - 不根据 JWT header 中的任意 URL 获取密钥; - `kid` 必须解析到对应 issuer 的已知密钥; - 签名与验证密钥用途分离; - 轮换时同时发布当前及仍覆盖有效 token 的旧公钥。 ### SSRF 与 discovery 上游 issuer、discovery URL 和 JWKS URL 是管理员配置,不从 token exchange 请求动态 创建。HTTP client 必须设置超时、响应大小上限和可接受的 TLS 信任;默认拒绝重定向到 未批准 origin。 ### 策略拒绝服务 CEL 环境只暴露固定变量和函数,限制表达式大小、编译时间与运行成本。策略在发布前编译 验证;无效策略不能替换最后一个有效版本。求值错误按拒绝签发处理。 ## Kubernetes ServiceAccount JWT - 只接受 projected、带过期时间和明确 audience 的 token; - 使用 TokenReview 校验身份和 audience,并检查返回的兼容 audience; - cluster identity 属于 verifier 配置,不能只由 token 的通用 issuer 名推导; - TokenReview 不可用或失败时不得自动降级为离线 JWKS; - token 中的 namespace、ServiceAccount、Pod 和 Node 信息只有经过 TokenReview 返回的 已认证 user/extra 信息确认后才能进入 policy context。 ## 未来的 SPIFFE verifier SPIFFE 不属于第一阶段。未来接入时必须验证真正的 SVID 和 trust bundle,不能仅把内部 principal 写成 `spiffe://` URI: - JWT-SVID 必须校验 trust bundle、audience 和时效; - X.509-SVID 只能通过完成验证的 mTLS 连接接受; - 不能接受普通 header 中自报的 SPIFFE ID; - SPIFFE ID 到内部 principal 的 trust binding 必须显式配置。 ## Token 签发 - 输出 JWT 必须包含 `iss`、`sub`、`aud`、`iat`、`nbf`、`exp` 和唯一 `jti`; - 默认不签发 refresh token; - token profile 由匹配身份的 trust binding/issuance rule 唯一确定,并定义 TTL 和允许 claims; - 客户端可以请求更少 scope,不能请求 TTL 或扩大 scope; - JWT 中不放置 secret、上游原始 token 或无界个人信息; - 面向 OpenBao 的 JWT 只是登录 assertion,不是 Bao client token。 ## 日志与审计 允许记录: - request ID; - 上游 issuer ID 和 credential type; - 规范化 principal; - 目标 audience、批准后的 scope/profile; - 自报 `client_id_claimed`(每个 v1 请求必有,明确标记为未经认证); - policy/version; - 结果、错误类别、输出 JWT `jti` 和时间。 禁止记录: - Authorization header; - subject token 或输出 JWT; - mTLS 私钥或完整证书链; - OpenBao token; - OAuth client secret; - token endpoint 请求/响应体原文。 OpenTelemetry HTTP instrumentation 不得配置 request/response body 或 Authorization、 Cookie 等凭据 header 捕获。span name 和 `http.route` 只能使用固定路由模板;原始 URL query、`client_id_claimed`、principal、subject、scope 和 `jti` 不得成为 HTTP metric label。业务 scope/profile 指标只能使用管理员配置的受控枚举。 ## 发布前安全验收 - 错误 issuer、签名、audience、算法、过期时间和未生效时间均被拒绝。 - 同名但来自另一 Kubernetes cluster 的 ServiceAccount 不能匹配 trust binding。 - 客户端不能通过请求 audience、scope、TTL 或 claims 扩大授权,且不能请求 token profile。 - 缺少或格式错误的 `client_id` 被拒绝;改变合法 `client_id` 不改变 principal、授权、 profile 或输出 claims。 - `client_id` 必须匹配 `^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$`,在进入审计前完成校验, 防止控制字符、日志注入和无界字段大小。 - 一个面向 OpenBao 的 JWT 不能用于 Kubernetes 或其他 API。 - 删除或禁用 trust binding 后不再签发新 token。 - 删除或禁用 trust binding 不会立即撤销已经签发的自包含 JWT;最大 TTL 是该变更的 最长生效延迟。 - CEL 编译或运行失败时拒绝签发,且不破坏最后一个有效策略版本。 - 日志、错误、metrics、trace、数据库和测试 artifact 不含 canary token。 - JWKS 轮换期间,轮换前已签发且未过期的 token 仍可验证。 - OpenBao、数据库或签名后端不可用时失败关闭,不签发降级 token。