Vault Adapter Tutorial¶
The Vault adapter provides KV v2 secret management, dynamic credential leases, and transit encryption against HashiCorp Vault. Under the hood it uses the official hvac client (usage reference).
Installation¶
Configuration¶
Configure the Vault adapter via environment variables or a VaultConfig object.
create_vault_client() builds an hvac.Client the same way the
hvac overview shows:
url, optional namespace, verify (bool or CA path), and optional mTLS cert=(cert, key).
Environment Variables¶
VAULT__ENABLED=true
VAULT__ADDR=https://vault.example.com:8200
VAULT__NAMESPACE= # Vault Enterprise namespace (optional)
VAULT__AUTH_METHOD=token
VAULT__TOKEN=s.xxxxx
VAULT__MOUNT_POINT=secret
VAULT__VERIFY_SSL=true
# VAULT__CA_CERT_PATH=/etc/ssl/certs/vault-ca.pem
# VAULT__CLIENT_CERT_PATH=/etc/ssl/certs/client.pem
# VAULT__CLIENT_KEY_PATH=/etc/ssl/private/client.key
VAULT__CONNECT_TIMEOUT=5.0
VAULT__READ_TIMEOUT=10.0
VAULT__SECRET_CACHE_TTL=0
VAULT__AUTO_RENEW_TOKEN=false
Supported VAULT__AUTH_METHOD values (hvac backends):
token, approle, kubernetes, userpass, ldap, okta, jwt, aws,
azure, gcp, github, cert.
AppRole example:
VAULT__AUTH_METHOD=approle
VAULT__APPROLE_ROLE_ID=...
VAULT__APPROLE_SECRET_ID_FILE=/run/secrets/approle_secret_id
Kubernetes example:
VAULT__AUTH_METHOD=kubernetes
VAULT__KUBERNETES_ROLE=my-app
VAULT__KUBERNETES_JWT_PATH=/var/run/secrets/kubernetes.io/serviceaccount/token
Userpass / LDAP / Okta example:
VAULT__AUTH_METHOD=userpass
VAULT__USERNAME=app-user
VAULT__PASSWORD_FILE=/run/secrets/vault_password
JWT example:
AWS IAM example:
VAULT__AUTH_METHOD=aws
VAULT__AWS_ACCESS_KEY=...
VAULT__AWS_SECRET_KEY=...
VAULT__AWS_ROLE=my-role
VAULT__AWS_REGION=us-east-1
GitHub / cert / cloud JWT (azure, gcp) follow the same pattern — see VaultConfig
fields (GITHUB_TOKEN, CERT_NAME + client cert paths, AZURE_*, GCP_*).
Direct Configuration¶
from archipy.configs.config_template import VaultConfig
config = VaultConfig(
ENABLED=True,
ADDR="https://vault.example.com:8200",
AUTH_METHOD="token",
TOKEN="s.xxxxx", # noqa: S106
MOUNT_POINT="secret",
VERIFY_SSL=True,
# CA_CERT_PATH="/etc/ssl/certs/vault-ca.pem",
# CLIENT_CERT_PATH="/etc/ssl/certs/client.pem",
# CLIENT_KEY_PATH="/etc/ssl/private/client.key",
)
Basic Usage¶
KV operations map to hvac
KV v2
(create_or_update_secret, read_secret_version, list_secrets,
delete_metadata_and_all_versions).
import logging
from archipy.adapters.vault.adapters import VaultAdapter
from archipy.models.errors import ConfigurationError, NotFoundError, PermissionDeniedError
logger = logging.getLogger(__name__)
try:
vault = VaultAdapter()
except ConfigurationError as e:
logger.error("Failed to create Vault adapter: %s", e)
raise
else:
logger.info("Vault adapter created successfully")
try:
vault.write_secret("myapp/db", {"password": "s3cret"})
secret = vault.read_secret("myapp/db")
logger.info("Loaded secret keys: %s", list(secret.keys()))
except (NotFoundError, PermissionDeniedError) as e:
logger.error("Vault secret operation failed: %s", e)
raise
Advanced Features¶
Dynamic Credentials¶
Without parameters, the adapter calls hvac
secrets.database.generate_credentials
(with a generic read fallback for non-database mounts). Pass parameters (even {}) to
force a write to {mount}/creds/{role} for engines like SSH OTP. Lease renew/revoke use
sys.renew_lease / sys.revoke_lease.
import logging
from archipy.adapters.vault.adapters import VaultAdapter
logger = logging.getLogger(__name__)
vault = VaultAdapter()
lease = vault.get_dynamic_credentials("database", "readonly")
logger.info("Got dynamic user %s (lease=%s)", lease.data.get("username"), lease.lease_id)
renewed = vault.renew_lease(lease.lease_id)
logger.info("Renewed lease duration=%s", renewed.lease_duration)
vault.revoke_lease(lease.lease_id)
logger.info("Revoked lease %s", lease.lease_id)
# SSH OTP-style engines that require a write body:
# lease = vault.get_dynamic_credentials("ssh", "otp-role", parameters={"ip": "127.0.0.1"})
Transit Encrypt / Decrypt¶
import logging
from archipy.adapters.vault.adapters import VaultAdapter
logger = logging.getLogger(__name__)
vault = VaultAdapter()
ciphertext = vault.encrypt("app-key", "hello-vault")
plaintext = vault.decrypt("app-key", ciphertext)
logger.info("Round-trip ok: %s", plaintext == "hello-vault")
Startup Secret Injection¶
When VAULT__ENABLED=true, BaseConfig also loads KV v2 secrets listed in
VAULT__SECRET_PATHS during settings initialization. Flat keys using __
map onto nested config fields (e.g. REDIS__PASSWORD → REDIS.PASSWORD).
See Configuration Management for priority order and settings-source details.
See Also¶
- hvac overview — Client init and auth
- hvac usage — Secrets engines and system backend
- Configuration Management — Vault-backed settings source
- Error Handling — Domain error mapping conventions
- BDD Testing — Testcontainers-based adapter tests
- Vault API Reference — Ports and adapter docs
- Configs API Reference —
VaultConfigfields