Archived
初始化项目文档与设计规范
This commit is contained in:
+114
@@ -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。
|
||||
Reference in New Issue
Block a user