4.6 KiB
首轮协议与签名 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/httpmiddleware 组合,不接管 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,
验证以下链路:
读取 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 调用使用:
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 仍可验证。
可执行验证
纯单元和互操作测试:
make test
真实 Transit 集成测试需要一个仅用于测试、已经启用 transit/ 并创建
transit/keys/workload-sts RSA key 的 OpenBao:
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。