初始化项目文档与设计规范

This commit is contained in:
2026-09-11 15:44:26 +00:00
commit cc30a7cb6a
6 changed files with 1181 additions and 0 deletions
+114
View File
@@ -0,0 +1,114 @@
# 首轮协议与签名 PoC
| 项目 | 内容 |
| --- | --- |
| 状态 | Complete |
| 日期 | 2026-09-10 |
本 PoC 为 v0.1 实现选择验证 HTTP/OAuth2 库边界,以及 OpenBao Transit RS256 能否生成
由标准 JOSE 实现验证的 JWT/JWKS。
## OAuth2 框架
检查了 Fosite v0.49.0 的公开文档和 token endpoint 实现。Fosite 支持自定义
`TokenEndpointHandler`,extension grant 可以通过 `CanSkipClientAuth` 跳过 client
authentication,但它没有现成的 RFC 8693 Token Exchange handler。
`NewAccessRequest` 仍会先执行 Fosite 的 client authentication/store lookup。v1 的
`client_id` 是不注册、不认证的必填审计标签,因此要接入 Fosite 需要:
- 自定义 RFC 8693 request/response handler;
- 绕开或适配 Fosite client store;
- 自定义 Transit-backed JWT strategy;
- 禁用本项目不提供的 authorize、refresh、OIDC、introspection 和 revocation 功能。
这套适配没有替项目减少核心安全逻辑,却引入了与 v1 不一致的 client/storage 模型。
因此 v0.1 不采用 Fosite,也不把它加入运行依赖。
v0.1 使用:
- Go `net/http` 与 `net/url` 处理窄 token endpoint;
- `chi/v5` 只承担路由和标准 `net/http` middleware 组合,不接管 OAuth 表单 binding;
- 项目代码执行 RFC 8693 子集的字段约束和 OAuth 错误映射;
- `go-jose/v4` 作为独立 JWS/JWK 互操作验证实现;
- 官方 `openbao/api/v2` 调用 Transit。
若未来加入完整 client registration、多个 grant 或 authorization endpoint,应重新评估
Fosite 或独立 Authorization Server 产品。
HTTP 层比较了 chi 与 Echo。Echo 的集中错误处理和自动 binding 对通用 Web API 很方便,
但本项目只有少量固定端点,OAuth 表单又需要显式约束字段来源。选择 chi 可以直接暴露
`http.Handler`、复用标准 middleware,并避免引入第二套 handler context 和未使用的
binding/rendering 抽象。
HTTP server instrumentation 使用官方 `otelhttp`,并启用 OpenTelemetry Go trace/metric
SDK 的依赖注入边界。SDK 不记录 HTTP body;项目也不启用 header capture。OTLP exporter
类型留给进程启动配置选择,避免把 collector 的 gRPC/HTTP 拓扑固化进协议层。
当前采用 OpenTelemetry Go SDK v1.46.0;`otelhttp` 随 contrib instrumentation 独立采用
v0.71.0,仍属于其 experimental instrumentation version set,因此升级时需要单独检查
HTTP semantic convention 与 metric 名称变化。
## Transit RS256
PoC 使用本地临时 OpenBao v2.6.1 dev server,创建不可导出的 `rsa-2048` Transit key,
验证以下链路:
```text
读取 Transit key metadata
-> 固定 latest_version
-> kid = workload-sts-v<version>
-> 构造 RS256 protected header 与 claims
-> 使用同一 key_version 请求 Transit 签名
-> 从 Transit public_key 生成 JWKS
-> go-jose 解析 compact JWT
-> 使用 JWKS 中相同 kid 的 RSA 公钥验签
```
测试结果为通过。Transit 调用使用:
```text
path: transit/sign/workload-sts/sha2-256
key type: rsa-2048
signature_algorithm: pkcs1v15
key_version: 显式指定
input: base64(JWS signing input)
```
### PoC 发现
`kid` 必须在构造 JWS protected header 前确定,因此 signer 先读取 active Transit key
version,再用该 version 签名。不能使用“签名后从 `vault:vN:` 响应推导 `kid`”的流程,
因为修改 header 会改变 signing input。
签名响应为 `vault:vN:<base64-signature>`。实现验证响应 version 与请求 version 一致,
并将原始 RSA signature 转为 JWS 使用的 Base64URL 编码。
JWKS 为 Transit metadata 中所有保留版本发布独立 key,`kid` 包含 key version,使轮换前
签发且尚未过期的 JWT 仍可验证。
## 可执行验证
纯单元和互操作测试:
```sh
make test
```
真实 Transit 集成测试需要一个仅用于测试、已经启用 `transit/` 并创建
`transit/keys/workload-sts` RSA key 的 OpenBao:
```sh
export WORKLOAD_STS_TEST_BAO_ADDR=http://127.0.0.1:8200
export WORKLOAD_STS_TEST_BAO_TOKEN=test-only-token
make test-transit
```
集成测试只读取 key metadata 和请求签名,不创建、轮换或删除 Transit key。
## 尚未覆盖
- OpenBao JWT auth 对输出 JWT 的实际登录验证;
- kube-apiserver 外部 JWT authenticator 的实际验证;
- Transit key rotation 后新旧 `kid` 的端到端验证;
- workload-sts 自身通过 Kubernetes auth 获取最小权限 Transit token;
- HTTP token endpoint、TokenReview、binding/CEL 和 PostgreSQL policy version。