V

Vault fetch

Auth & signers available

Fetch secrets from HashiCorp Vault (KV v2) into the run.

Install
$ loadr plugin install vault-fetch
examples/plugins/vault-fetch.yaml
# KV secrets pulled from HashiCorp Vault once at run start and shared with every
# VU, driven by the `loadr-plugin-vault-fetch` native SERVICE plugin.
#
# Like the oauth2-minter, a service plugin has a start/stop lifecycle: loadr
# calls `start(config)` once before the run and `stop()` once after. On start
# this plugin authenticates to Vault (a static token or an AppRole login) and
# reads the KV secret at `path` ONCE — over pure hyper + hyper-rustls, no Vault
# SDK and no `vault` binary — caches every field, then binds a tiny local line
# endpoint and returns its address, e.g.:
#
#   → vault-fetch secret endpoint at 127.0.0.1:43117
#
# Every VU that opens that endpoint and writes a field name gets that field's
# CACHED value back (an empty line for an unknown field, or the whole secret as
# JSON for `*`). The read happens once, centrally, before any VU begins, so a
# Vault outage, an expired secret_id, or a wrong `path` fails the run at startup
# rather than as a wave of auth failures mid-test — and the secrets never live
# in the plan file.
#
# Build + install the plugin, then run:
#   cargo build -p loadr-plugin-vault-fetch --release
#   mkdir -p dist && cp plugins/loadr-plugin-vault-fetch/plugin.toml dist/ \
#     && cp target/release/libloadr_plugin_vault_fetch.so dist/
#   loadr plugin install dist
#   VAULT_ROLE_ID=... VAULT_SECRET_ID=... loadr run examples/plugins/vault-fetch.yaml
#
# Or point the plan's `plugins:` entry at the built artifact directly (below).
name: vault-fetch
description: KV secrets fetched from Vault at run start and shared with every VU

plugins:
  # Resolve `vault-fetch` by name from the plugins dir (after
  # `loadr plugin install`). To run straight from a build tree instead, set:
  #   path: target/release/libloadr_plugin_vault_fetch.so
  - name: vault-fetch
    config:
      # Vault API address. Must be http(s)://…; use https:// in any non-local
      # setup so the token and secret never cross the wire in the clear.
      addr: https://vault:8200
      # KV read path, e.g. "secret/data/app" for a KV v2 mount. Each field of
      # the returned secret becomes a value the VUs can draw by name.
      path: secret/data/app
      # Exactly one auth method. Bootstrap credentials come from ${env.…}, never
      # inline: an AppRole login (preferred in CI, secret_id is short-lived)…
      auth:
        approle:
          role_id: ${env.VAULT_ROLE_ID}
          secret_id: ${env.VAULT_SECRET_ID}
      # …or a pre-issued token instead of the approle block:
      #   auth:
      #     token: ${env.VAULT_TOKEN}
      # Vault Enterprise namespace, sent as X-Vault-Namespace (optional).
      # namespace: team-a
      # Keep the AppRole lease alive for the length of a long run (default false).
      renew: false
      # Per-request timeout for the login and KV read (default 10s).
      timeout: 10s
      # Local address the secret endpoint binds to. Port 0 = ephemeral; the
      # bound address is printed when the run starts.
      bind: 127.0.0.1:0

scenarios:
  # Steady authed traffic: every VU across every worker draws the SAME shared
  # secret values fetched once on start. Point requests at the field values
  # drawn from the endpoint address printed at run start.
  authed_traffic:
    executor: constant-vus
    vus: 50
    duration: 30s
    flow:
      # Each VU reads one field's cached value from the secret endpoint. One
      # request line (the field name) -> that field's value + a newline.
      - request:
          name: fetch db password
          url: tcp://127.0.0.1:0   # replace with the printed secret endpoint address
          socket:
            send_text: "db_password\n"   # a field name -> its value + newline
            read_bytes: 4096
            read_timeout: 2s
          checks:
            - { type: duration, name: endpoint is fast, max: 25ms }

thresholds:
  checks: [ "rate>0.99" ]

A real run: install from the signed index, then watch the plugin work.

A runtime plugin, never in the binary

Installing pulls a per-platform driver from the signed index, verifies its SHA-256 and checks its ABI before it ever loads. Remove it any time with loadr plugin remove vault-fetch.