This repository has been archived on 2026-09-13. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files

115 lines
4.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 首轮协议与签名 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。