Skip to content

feat(secrets-vault): add a Vault KV v2 secret backend #94

Description

@terylt

Description

Add a Vault KV v2 backend for the SecretProvider trait, registered as a provider kind so a declared secret can resolve from Vault instead of a file or an environment variable. Implement the minimal Vault HTTP API through the host-supplied HttpTransport. No Vault SDK, no generated client, no second HTTP stack: PPE performs no outbound HTTP of its own, and a Vault client built on its own reqwest would bypass the host's TLS, egress, timeout, retry, and response-size policy.

This is #66 narrowed. The trait, the registry, the declared secret block, startup ordering, refresh policy, and the consumer belong to the seam and assertion issues; what is left here is the backend.

Acceptance criteria

  • A vault provider kind registers as a SecretProviderFactory, available behind a secrets-vault facade feature and absent from the default build.
  • Reference grammar <mount>/<path>#<field>, mapped to GET /v1/<mount>/data/<path>, with the field read from data.data. An absent path, absent field, non-string value, or empty value is an error rather than an empty result.
  • Every call goes through HttpTransport. A KV read takes an idempotent retry policy; a login and a renew-self do not, since a retried login that actually succeeded mints a second token and a second lease.
  • Auth method is explicit with no default. Kubernetes and AppRole only.
  • Kubernetes login rereads the mounted service-account token on every login. Kubelet rotates a projected token in place, so a token cached at startup stops working within the hour.
  • AppRole secret_id comes from an environment variable, a file, or a clearly documented development-only literal.
  • The address must be https:// unless the operator explicitly sets insecure_http.
  • A renewable token is renewed before expiry and a non-renewable one is obtained again. A 403 during a read triggers one reauthentication attempt and a retry, distinguished from "the secret does not exist".
  • Vault's {"errors": [...]} envelope is a distinct error from a transport failure. Vault tokens, bootstrap credentials, and retrieved values never appear in errors, logs, serialized state, or Debug output, and are cleared on drop where practical.
  • Tests cover both auth methods, field extraction, renewal, reauthentication on 403, rotation, startup failure, refresh failure, and concurrent reads. A pinned integration test documents the Vault server version it was run against.
  • make audit stays green with no new license exception.

Token renewal and background tasks

Secret refresh belongs to the seam, but the Vault session's own lifecycle does not, and it carries a hazard this repo has already been bitten by. A spawned ticker binds to whichever runtime called initialize(), and a host that initializes on a short-lived runtime has it cancelled before it ticks once. That is issue #29: the JWKS refresh task died silently and rotation never recovered, which is why identity-jwt now refreshes from the verify path instead (builtins/plugins/identity-jwt/src/resolver.rs:487).

Token renewal has no request path to hang off, since after startup nothing calls Vault until the next refresh tick. The reactive reauthentication on 403 covers most of it. This issue should decide whether a renewal timer exists at all, or whether renewal happens lazily on the next read, and say what happens on a host with no long-lived runtime.

The concrete renewal schedule, as a fraction of lease_duration with jitter and a backoff on a failed attempt, also needs a value here rather than at review time.

Out of scope

KV v1, secret writes, dynamic secrets, runtime delegation, and OpenAPI-driven code generation. Per-caller credential resolution is a separate delegator issue and shares only this client.

Depends on

#92

Activity

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

Metadata

Metadata

Assignees

Projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions