Archived
初始化项目文档与设计规范
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
# 系统架构
|
||||
|
||||
本文是 [`specification.md`](specification.md) 的架构视图。规范定义外部行为,本文解释
|
||||
组件边界;二者冲突时以规范为准。当前 v0.1 设计已经批准。
|
||||
|
||||
## 组件与数据流
|
||||
|
||||
```text
|
||||
Kubernetes SA JWT
|
||||
|
|
||||
v
|
||||
Credential Verifiers
|
||||
|
|
||||
v
|
||||
Principal Normalization
|
||||
|
|
||||
v
|
||||
Issuance Policy + CEL Conditions
|
||||
|
|
||||
v
|
||||
OAuth 2.0 Token Endpoint
|
||||
|
|
||||
v
|
||||
Audience-bound JWT Issuer
|
||||
|
|
||||
+----------------+----------------+
|
||||
| |
|
||||
v v
|
||||
OpenBao JWT auth Kubernetes/API service
|
||||
| |
|
||||
v v
|
||||
OpenBao client token Native authorization
|
||||
```
|
||||
|
||||
服务验证上游 credential,但不把它直接转发给下游。签发决策成功后生成新的 JWT,
|
||||
其 issuer、audience、TTL 和 claims 由本服务控制。
|
||||
|
||||
下游资源服务器负责最终授权。OpenBao 根据 JWT auth role 和 policy 签发自己的 client
|
||||
token;Kubernetes 根据外部 JWT authenticator 和 RBAC 授权。本服务不复制这些权限模型。
|
||||
|
||||
## 组件边界
|
||||
|
||||
### OAuth 2.0 协议层
|
||||
|
||||
协议层负责 Authorization Server metadata、token endpoint、标准 OAuth2 错误和响应。
|
||||
第一阶段只实现 RFC 8693 Token Exchange 所需子集,不实现交互式用户登录。
|
||||
|
||||
协议层只实现已批准的 RFC 8693 子集。HTTP 路由和 middleware 使用与 `net/http` 兼容的
|
||||
`chi/v5`,OAuth 表单仍由 Go 标准库显式解析,不使用框架自动 binding;JOSE 与 OpenBao
|
||||
调用使用成熟库。首轮 PoC 发现现有 OAuth2 框架仍需自定义 token
|
||||
exchange、client store 适配和 Transit signer,不能减少本项目的核心安全逻辑,因此 v0.1
|
||||
不引入完整 Authorization Server 框架。业务代码仍将 credential verification、授权和
|
||||
token materialization 保持为独立边界。详见 [`poc.md`](poc.md)。
|
||||
|
||||
HTTP router 外层使用 `otelhttp` 生成 server span 和标准 HTTP metrics。span 与 metric 只使用
|
||||
固定路由模板及受控低基数字段,不捕获请求/响应 body、Authorization header、
|
||||
`subject_token`、输出 token、`client_id`、principal 或 `jti`。trace 与 metric provider
|
||||
通过启动依赖注入;未配置 exporter 时保持 no-op,不把 telemetry 输出到标准输出。
|
||||
|
||||
### Credential Verifier
|
||||
|
||||
每种 verifier 把特定上游凭据转换为不可伪造的内部事实:
|
||||
|
||||
```text
|
||||
issuer
|
||||
source subject
|
||||
source credential type
|
||||
source audiences
|
||||
issued-at / expiry
|
||||
verified attributes
|
||||
verification method
|
||||
```
|
||||
|
||||
verifier 不能直接决定目标 audience 或输出 claims。未经验证的请求字段不能进入
|
||||
`verified attributes`。
|
||||
|
||||
第一阶段支持:
|
||||
|
||||
- Kubernetes projected ServiceAccount JWT。
|
||||
|
||||
OIDC、SPIFFE、云 workload identity 和外部 metadata 系统以后以 verifier 扩展,不改变
|
||||
token endpoint 合同。不同 credential issuer 可以使用不同验证逻辑,但 upstream issuer
|
||||
不是 OAuth client。
|
||||
|
||||
所有 token request 都必须携带 `client_id`。v1 的 client authentication method 为
|
||||
`none`,因此该值只以 `client_id_claimed` 进入审计,不参与身份映射或授权。以后可以为
|
||||
同一字段增加 public client registration、mTLS 或 `private_key_jwt` 认证,无需改变请求
|
||||
是否包含 `client_id` 的合同。
|
||||
|
||||
`client_id` 使用 3–63 字符的 DNS-label-like 语法,只允许小写字母、数字和中横线,且
|
||||
中横线不能位于首尾。
|
||||
|
||||
### Principal Normalization
|
||||
|
||||
trust binding 把上游 verifier 与 source subject 映射为稳定、不可变的内部 principal:
|
||||
|
||||
```text
|
||||
sub = 01993f4d-5e1a-7000-8000-000000000001
|
||||
principal_name = ci/homelab-infra-plan
|
||||
```
|
||||
|
||||
可读名称允许受控改名,JWT `sub` 使用的 ID 不随名称变化。映射规则由管理员配置;
|
||||
客户端不能在 token 请求中选择或覆盖 `sub`。一个上游身份可以没有任何映射,此时认证
|
||||
成功但 token exchange 被拒绝。
|
||||
|
||||
### Issuance Policy
|
||||
|
||||
授权层只回答:
|
||||
|
||||
> 某个已验证 principal 是否可以为指定 audience 和 scope 获取 JWT?
|
||||
|
||||
trust binding 中的 issuance rule 先限制 principal、audience 和 scope,并直接绑定唯一
|
||||
token profile;CEL 只表达需要结合已验证上下文的条件。客户端不能请求 profile。CEL
|
||||
不能调用网络、读取机密或修改状态。
|
||||
|
||||
授权层不决定 OpenBao secret path、Bao token TTL 或 Kubernetes RBAC。
|
||||
|
||||
### JWT Issuer
|
||||
|
||||
JWT issuer 根据批准的 token profile 生成 claims 并请求签名后端签名。客户端只能请求
|
||||
允许缩小权限的参数,不能直接提交输出 `sub`、任意 claim、签名算法或 TTL。
|
||||
|
||||
生产签名密钥保存在 OpenBao Transit 中;数据库和服务实例不保存私钥。
|
||||
workload-sts 使用自己的 Kubernetes ServiceAccount JWT 登录 OpenBao,取得只允许访问
|
||||
指定 Transit key 的短期运行凭据。它不替客户端登录 OpenBao,也不接触客户端随后取得
|
||||
的 Bao token。第一阶段使用 RS256;签名后端保留接口边界,以便单元测试使用内存
|
||||
signer。密钥轮换的具体运维合同在部署设计中补充。
|
||||
|
||||
### Database
|
||||
|
||||
HTTP 实例无本地持久状态。数据库保存配置及需要一致性的控制面状态,例如:
|
||||
|
||||
- upstream issuer 与 verifier 配置;
|
||||
- trust binding;
|
||||
- principal、audience、scope 与 token profile;
|
||||
- CEL policy 及版本;
|
||||
- 调用方自报 `client_id` 的有界审计值;
|
||||
- 审计索引和配置变更记录。
|
||||
|
||||
数据库不得保存 bearer token、原始 subject token、OpenBao client token 或签名私钥。
|
||||
自包含 JWT 的普通验证不依赖数据库在线 introspection。
|
||||
|
||||
## Kubernetes 信任方向
|
||||
|
||||
系统明确区分两条方向相反的链:
|
||||
|
||||
```text
|
||||
Kubernetes SA JWT -> workload-sts
|
||||
```
|
||||
|
||||
projected ServiceAccount JWT 的 audience 是 workload-sts,用于证明 Pod 的上游身份。
|
||||
|
||||
```text
|
||||
workload-sts JWT -> kube-apiserver
|
||||
```
|
||||
|
||||
目标 audience 是特定 Kubernetes cluster。kube-apiserver 通过外部 JWT authenticator
|
||||
验证 issuer 和 claims,再交由 RBAC 授权。workload-sts 不签发 Kubernetes
|
||||
ServiceAccount token。
|
||||
|
||||
## 外部 workload attestation
|
||||
|
||||
PVE、microVM、vsock metadata、Gitea runner 调度和云 metadata 不属于本仓库。对应系统
|
||||
以后可以输出本服务可验证的标准身份载体,例如 JWT-SVID 或已登记 issuer 的 JWT。
|
||||
|
||||
本服务不依赖或理解 VMID、vsock CID、Gitea job 环境变量、runner registration token
|
||||
或虚拟机生命周期。外部系统负责防重放、实例代际和 attestation,本服务负责验证其最终
|
||||
credential 并执行签发策略。
|
||||
|
||||
## 部署边界
|
||||
|
||||
系统是无状态 HTTP 服务加数据库,可以部署在 Kubernetes、VM 或其他运行环境。部署位置
|
||||
不是信任语义的一部分;OAuth issuer 必须保持为稳定的
|
||||
`https://identity.ad.ddupan.top`。
|
||||
|
||||
workload-sts 依赖 OpenBao Transit 完成生产签名,但只使用自己的最小权限运行身份;
|
||||
OpenBao 的人工管理与 break-glass 路径不得反向依赖 workload-sts。
|
||||
|
||||
## 文档入口
|
||||
|
||||
- 规范行为与验收标准:[`specification.md`](specification.md)
|
||||
- 威胁模型与安全要求:[`security.md`](security.md)
|
||||
- 首轮库与签名 PoC:[`poc.md`](poc.md)
|
||||
Reference in New Issue
Block a user