Skip to content

feat(kms): add OpenBao / Vault Transit KeyManager adapter (V1.0-KMS-3)#200

Open
mrhein wants to merge 1 commit into
cloud37:mainfrom
mrhein:feat/openbao-transit-adapter
Open

feat(kms): add OpenBao / Vault Transit KeyManager adapter (V1.0-KMS-3)#200
mrhein wants to merge 1 commit into
cloud37:mainfrom
mrhein:feat/openbao-transit-adapter

Conversation

@mrhein

@mrhein mrhein commented Jun 28, 2026

Copy link
Copy Markdown

Add a KeyManager adapter that wraps per-object DEKs via the OpenBao / HashiCorp Vault Transit secrets engine (transit/encrypt + transit/decrypt); the KEK stays non-exportable in the server. The same adapter targets both OpenBao and Vault (identical Transit API) and is registered under the provider names openbao, openbao-transit, vault, and vault-transit.

Highlights:

  • Auth: token, approle, and kubernetes methods, with an in-process token renewal goroutine (LifetimeWatcher + re-login on max_ttl / revoke / restart) — no Vault Agent sidecar required. Construction is fail-closed.
  • Rotation: implements RotatableKeyManager driving the server-side transit/keys//rotate RPC; the gateway follows the server's latest_version. Plugs into the existing /admin/kms/rotate/* drain machine.
  • HealthCheck probes auth/token/lookup-self (detects token expiry — sys/health alone returns 200 with a dead token, GAP-KMS3-3) and transit/keys/ (key exists / readable; 404 -> ErrKeyNotFound).
  • No client-side candidate loop / dual_read_window: Transit self-routes a decrypt by the "vault:vN:" ciphertext prefix.
  • TLS 1.2+ with restricted suites; ca_cert + insecure_skip_verify performs real CA-pinning via VerifyConnection (skips only the hostname check).
  • Builds under -tags=fips (no local AES; wrapping is server-side).
  • Secrets (token / secret_id) resolved from env:/file: at build time so the config struct never holds them; never logged or placed in a KeyEnvelope.

Also fixes a pre-existing bug affecting every rotatable adapter: the retry, circuit-breaker, DEK-cache and password-fallback wrappers exposed only the KeyManager method set, so the admin rotation API's RotatableKeyManager type assertion failed whenever a wrapper was active. The wrappers now conditionally forward RotatableKeyManager to the wrapped/primary adapter.

Tests: unit + ConformanceSuite (mock Transit server), decorator/fallback rotation regression tests, and build-tagged integration tests (OpenBao + MinIO via testcontainers, plus an external-infra E2E). Docs: ADR-0015, KMS_COMPATIBILITY.md, config.yaml.example, CHANGELOG.

Refs: V1.0-KMS-3. Resolves GAP-KMS3-1, GAP-KMS3-3.

Add a KeyManager adapter that wraps per-object DEKs via the OpenBao /
HashiCorp Vault Transit secrets engine (transit/encrypt + transit/decrypt);
the KEK stays non-exportable in the server. The same adapter targets both
OpenBao and Vault (identical Transit API) and is registered under the
provider names openbao, openbao-transit, vault, and vault-transit.

Highlights:
- Auth: token, approle, and kubernetes methods, with an in-process token
  renewal goroutine (LifetimeWatcher + re-login on max_ttl / revoke /
  restart) — no Vault Agent sidecar required. Construction is fail-closed.
- Rotation: implements RotatableKeyManager driving the server-side
  transit/keys/<key>/rotate RPC; the gateway follows the server's
  latest_version. Plugs into the existing /admin/kms/rotate/* drain machine.
- HealthCheck probes auth/token/lookup-self (detects token expiry —
  sys/health alone returns 200 with a dead token, GAP-KMS3-3) and
  transit/keys/<key> (key exists / readable; 404 -> ErrKeyNotFound).
- No client-side candidate loop / dual_read_window: Transit self-routes a
  decrypt by the "vault:vN:" ciphertext prefix.
- TLS 1.2+ with restricted suites; ca_cert + insecure_skip_verify performs
  real CA-pinning via VerifyConnection (skips only the hostname check).
- Builds under -tags=fips (no local AES; wrapping is server-side).
- Secrets (token / secret_id) resolved from env:/file: at build time so the
  config struct never holds them; never logged or placed in a KeyEnvelope.

Also fixes a pre-existing bug affecting every rotatable adapter: the retry,
circuit-breaker, DEK-cache and password-fallback wrappers exposed only the
KeyManager method set, so the admin rotation API's RotatableKeyManager type
assertion failed whenever a wrapper was active. The wrappers now
conditionally forward RotatableKeyManager to the wrapped/primary adapter.

Tests: unit + ConformanceSuite (mock Transit server), decorator/fallback
rotation regression tests, and build-tagged integration tests (OpenBao +
MinIO via testcontainers, plus an external-infra E2E). Docs: ADR-0015,
KMS_COMPATIBILITY.md, config.yaml.example, CHANGELOG.

Refs: V1.0-KMS-3. Resolves GAP-KMS3-1, GAP-KMS3-3.
@mrhein mrhein requested a review from kenchrcum as a code owner June 28, 2026 15:11
@kenchrcum

Copy link
Copy Markdown
Collaborator

Oh, wow, thanks! Totally forgot about the OpenBao project and its free implementation of Vault API! Will give this some checks and get back to you!

@kenchrcum

Copy link
Copy Markdown
Collaborator

Hi @mrhein — this is a really solid contribution. The token-renewal goroutine, the two-probe HealthCheck that resolves GAP-KMS3-3, and the TLS CA-pinning semantics test are exactly what we had in mind. Thank you for the careful work.

One ask before we merge: please take a look at docs/TESTING.md (docs/TESTING.md), specifically the three-tier taxonomy. The container test you added belongs in tier 2 (//go:build conformance, test/conformance/), where it will be picked up by the PR gate automatically. The existing test/conformance/kms_test.go (test/conformance/kms_test.go) is a direct model for what that looks like with a live KMS container.

On our side — we should make docs/TESTING.md easier to find for new contributors. We will add a link from CONTRIBUTING.md so the next person lands in the right place from the start.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants