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

4.7 KiB
Raw Permalink Blame History

首轮协议与签名 PoC

历史文档: 此 PoC 已结束且不会演进为服务。项目由 SPIRE 替代,参见 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 支持自定义 TokenEndpointHandlerextension 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/httpnet/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.0otelhttp 随 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。