Skip to main content

HashiCorp Vault secret source for Hermes Agent (bulk KV v2).

Project description

hermes-vault-secret-source

CI

A HashiCorp Vault secret source for Hermes Agent, packaged as a standalone pip plugin.

At Hermes startup it reads one Vault KV v2 secret path and injects every key found there as an environment variable, the same way the bundled Bitwarden Secrets Manager source injects a whole project. This is a bulk source, so explicit per-variable bindings from mapped sources (like 1Password) take precedence over it.

Install

pip install "hermes-vault-secret-source @ git+https://github.com/cryptoyasenka/hermes-vault-secret-source"

The package is not on PyPI yet, so install it straight from the repository as shown above. Once it is published, this simplifies to:

pip install hermes-vault-secret-source

Either way this pulls in hvac, the official Python client for Vault. Install it into the same environment as hermes-agent.

Hermes discovers the plugin automatically through the hermes_agent.plugins entry point, but plugins are opt-in. Enable it once:

hermes plugins enable hermes-hashicorp-vault

Configure

Add a vault block under secrets in your Hermes config:

secrets:
  vault:
    enabled: true
    # addr is read from $VAULT_ADDR by default; set `addr` to hard-code it.
    path: apps/my-agent          # KV v2 path whose keys become env vars
    mount_point: secret          # KV v2 mount (default: secret)
    auth_method: token           # "token" (default) or "approle"
    cache_ttl_seconds: 300       # 0 disables caching

Credentials are never written into config; they come from environment variables:

Auth method Env vars used
token VAULT_ADDR, VAULT_TOKEN
approle VAULT_ADDR, VAULT_ROLE_ID, VAULT_SECRET_ID

The env-var names are overridable per field (addr_env, token_env, role_id_env, secret_id_env) if your deployment uses different names.

All config keys

Key Default Meaning
enabled false Master switch
addr "" Vault URL; overrides addr_env when set
addr_env VAULT_ADDR Env var holding the Vault URL
path "" KV v2 path to read (required)
mount_point secret KV v2 secrets-engine mount
auth_method token token or approle
token_env VAULT_TOKEN Env var with the token (token auth)
role_id_env VAULT_ROLE_ID Env var with the role_id (approle)
secret_id_env VAULT_SECRET_ID Env var with the secret_id (approle)
namespace "" Vault Enterprise namespace
verify true TLS verify: true/false or a CA-bundle path
cache_ttl_seconds 300 Disk+memory cache TTL; 0 disables caching
request_timeout_seconds 30 Per-request Vault HTTP timeout
override_existing true Vault values overwrite existing env values

How it behaves

  • Never raises. A misconfigured or unreachable Vault produces a clean FetchResult with an ErrorKind (not_configured, auth_failed, ref_invalid, network, timeout, ...), never a startup crash.
  • Protects its own credentials. The auth env vars (VAULT_ADDR, VAULT_TOKEN, VAULT_ROLE_ID, VAULT_SECRET_ID) are marked protected, so a secret stored in Vault cannot clobber the credential used to reach Vault.
  • Caches values, never tokens. The optional on-disk cache stores only the resolved secret values; the token/secret_id are SHA-256 fingerprinted before they touch the cache key.
  • Skips non-env-safe keys. A KV key that is not a valid environment variable name is skipped with a warning rather than injected.

Threat model

This plugin runs on the Hermes startup path with access to Vault credentials, so its security posture is deliberately narrow. Every protection below is already implemented; each maps to a concrete mechanism in the source.

Startup cannot be crashed by this source

fetch() never raises. Every configuration problem returns a FetchResult with a machine-readable ErrorKind (not_configured, auth_failed, ref_invalid, network, timeout, binary_missing, internal), and the backend in _client.py only ever raises RuntimeError, which fetch() catches and classifies. A misconfigured, unreachable, or unauthenticated Vault degrades to a clean skip, never a traceback on the non-interactive startup path.

A Vault secret cannot hijack the credential used to reach Vault

protected_env_vars() marks the auth env vars (VAULT_ADDR, VAULT_TOKEN, VAULT_ROLE_ID, VAULT_SECRET_ID, or their configured overrides) as protected. The orchestrator will not overwrite a protected variable, so a key that happens to be named VAULT_TOKEN inside the Vault payload cannot clobber the token the process is already using to authenticate. Names that are not valid env-var identifiers are dropped from the protected set, so a misconfigured section cannot silently weaken it.

Tokens never reach the on-disk cache in the clear

The optional two-layer cache stores only resolved secret values. The Vault token (or the AppRole role_id and secret_id) is SHA-256 fingerprinted by _fingerprint() before it becomes part of the cache key, so neither the in-memory key nor the vault_cache.json file on disk contains the raw credential. Caching is opt-out: cache_ttl_seconds: 0 disables it entirely.

Only environment-safe keys are injected

Each KV v2 key is validated with the host's is_valid_env_name() before it is contributed. A key that is not a valid environment-variable name is skipped with a warning instead of being injected, so a stray Vault key cannot produce a malformed process environment.

TLS verification is on by default

verify defaults to true. It is disabled only when the operator explicitly sets a false-y value, and a string value is treated as a CA-bundle path. TLS therefore fails closed rather than open.

Development

Requires a hermes-agent source checkout (for the agent.* packages and the conformance kit at tests/secret_sources/conformance.py).

# host = hermes-agent checkout, plugin = this repo. Put the host on PYTHONPATH so
# `import tests` resolves to the host conformance package. This repo keeps its own
# tests in `checks/` (not `tests/`) precisely so it cannot shadow that package.
PYTHONPATH="/path/to/hermes-agent:$(pwd)" pytest

checks/test_conformance.py runs the host SecretSourceConformance contract; checks/test_vault_source.py covers behavior with hvac mocked (no network, no Vault binary needed).

To run against a real dev server:

vault server -dev            # prints VAULT_ADDR + a root token
export VAULT_ADDR=http://127.0.0.1:8200
export VAULT_TOKEN=<root-token-from-output>
vault kv put secret/apps/my-agent API_KEY=abc DB_URL=postgres://x

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

hermes_vault_secret_source-0.1.0.tar.gz (13.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

hermes_vault_secret_source-0.1.0-py3-none-any.whl (11.9 kB view details)

Uploaded Python 3

File details

Details for the file hermes_vault_secret_source-0.1.0.tar.gz.

File metadata

File hashes

Hashes for hermes_vault_secret_source-0.1.0.tar.gz
Algorithm Hash digest
SHA256 7fa917de3c1ed9d697ca83dd3d0e92350ef2a092991efc695cafa633024ed185
MD5 3ce190cc6293f6e64b817d744e75e68e
BLAKE2b-256 19fd3723b5d707395c4a7fbf3d826d73fd4ccfd45be78ed4de4f499f886568c7

See more details on using hashes here.

Provenance

The following attestation bundles were made for hermes_vault_secret_source-0.1.0.tar.gz:

Publisher: release.yml on cryptoyasenka/hermes-vault-secret-source

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file hermes_vault_secret_source-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for hermes_vault_secret_source-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9094212134f74bbc9035a383950f0ce50a85317fc7ea39eed9fb941612fb74f2
MD5 f86fae5e7ae4bf80d0e58fbcacf28505
BLAKE2b-256 6e059f9776662c995a4495e45f617087070f7945302b9c031409b212bcdf2a6c

See more details on using hashes here.

Provenance

The following attestation bundles were made for hermes_vault_secret_source-0.1.0-py3-none-any.whl:

Publisher: release.yml on cryptoyasenka/hermes-vault-secret-source

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page