# 首轮协议与签名 PoC > **历史文档:** 此 PoC 已结束且不会演进为服务。项目由 SPIRE 替代,参见 > [`superseded-by-spire.md`](superseded-by-spire.md)。 | 项目 | 内容 | | --- | --- | | 状态 | 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 -> 构造 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:`。实现验证响应 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。