HOW-TO GUIDE | APPSEC
Updated 10 min read

How to Implement Secrets Management with HashiCorp Vault

1 in 10
GitHub repositories scanned by GitGuardian contain at least one secret: API keys, passwords, or certificates committed to source code
Dynamic
Vault database secrets engine creates unique per-request credentials with configurable TTL: eliminates shared, long-lived database passwords
5 min
Typical TTL for dynamic Vault database credentials: leaked credentials expire automatically, limiting the exploitation window
100%
Vault access logged in its audit backend: every secret read, write, and auth event is recorded with requesting entity identity

SponsoredRetool

Retool's new app builder is where AI-generated code ships safely

Building apps with AI is easy. Getting them to production safely is another story.

Start building for free today

The alternative to Vault is secrets scattered everywhere: database passwords in application.properties files, API keys in CI/CD environment variables shared across the team, TLS certificates on developer laptops, and cloud credentials in ~/.aws/credentials. Any one of these locations can leak, and there is no audit trail to know which secrets were accessed by whom and when.

Vault centralizes all of this behind a single authenticated API with fine-grained policies and a complete audit log. Applications prove their identity (via AppRole, Kubernetes service account, or AWS IAM role), receive only the credentials their policy allows, and every access is logged.

Installation and Initial Setup

Install Vault (Linux):

# Add HashiCorp GPG key and repository
wget -O- https://apt.releases.hashicorp.com/gpg | gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/hashicorp.list
apt update && apt install vault

# Start Vault in dev mode for local testing (NOT production: dev mode is unsealed and in-memory)
vault server -dev -dev-root-token-id="root"

# Set environment variables
export VAULT_ADDR='http://127.0.0.1:8200'
export VAULT_TOKEN='root'

# Verify
vault status

Production setup: basic config file:

# /etc/vault.d/vault.hcl
storage "raft" {
  path    = "/opt/vault/data"
  node_id = "vault-node-1"
}

listener "tcp" {
  address     = "0.0.0.0:8200"
  tls_cert_file = "/opt/vault/tls/vault.crt"
  tls_key_file  = "/opt/vault/tls/vault.key"
}

api_addr = "https://vault.internal.company.com:8200"
cluster_addr = "https://vault-node-1.internal:8201"
ui = true

Initialize and unseal (production):

# Initialize Vault: generates unseal keys and root token
vault operator init -key-shares=5 -key-threshold=3

# CRITICAL: Save the unseal keys and root token securely: if you lose them, data is unrecoverable
# Use Shamir's Secret Sharing: 5 keys generated, 3 required to unseal
# Distribute keys to 5 trusted people or store in separate secure locations

# Unseal (requires 3 of 5 key shares)
vault operator unseal <key-share-1>
vault operator unseal <key-share-2>
vault operator unseal <key-share-3>

KV Secrets Engine: Storing Static Secrets

KV-v2 (Key-Value version 2) is the primary secrets engine for storing static secrets. It maintains a version history and supports soft-delete and metadata.

# Enable KV-v2 secrets engine
vault secrets enable -path=secret kv-v2

# Write a secret
vault kv put secret/myapp/database \
  host="db.internal.company.com" \
  username="app_user" \
  password="$(openssl rand -base64 32)"

# Read a secret
vault kv get secret/myapp/database

# Read just the password field
vault kv get -field=password secret/myapp/database

# Read a specific version
vault kv get -version=2 secret/myapp/database

# List all secrets at a path
vault kv list secret/myapp/

# Soft-delete a secret version
vault kv delete secret/myapp/database

# Read via API (for application integration)
curl -H "X-Vault-Token: $VAULT_TOKEN" \
  $VAULT_ADDR/v1/secret/data/myapp/database

Organizing secrets by service and environment:

# Recommended path structure
secret/
  prod/
    webapp/
      database
      redis
      stripe-api-key
  staging/
    webapp/
      database
  shared/
    tls-certificates/
    internal-ca
Free daily briefing

Briefings like this, every morning before 9am.

Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.

Dynamic Database Secrets

Dynamic secrets are generated on-demand and automatically expired: the application gets a unique database username and password that did not exist before the request and will not exist after the lease expires.

# Enable the database secrets engine
vault secrets enable database

# Configure a PostgreSQL connection
vault write database/config/webapp-postgres \
  plugin_name=postgresql-database-plugin \
  allowed_roles="webapp-role" \
  connection_url="postgresql://{{username}}:{{password}}@db.internal:5432/appdb" \
  username="vault-admin" \
  password="$(cat /run/secrets/vault-db-admin-password)"

# Create a role: this defines what credentials look like when generated
vault write database/roles/webapp-role \
  db_name=webapp-postgres \
  creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
  default_ttl="5m" \
  max_ttl="1h"

# Generate dynamic credentials (what your application calls)
vault read database/creds/webapp-role
# Returns: username=v-appid-webapp-abc123-1234567890, password=A1B2C3..., lease_duration=5m

The generated credentials exist only for 5 minutes (or until the lease is explicitly revoked). If the credentials leak, they expire automatically. There is no shared database password to rotate.

Renew a lease (if the application needs more time):

vault lease renew database/creds/webapp-role/<lease-id>

Revoke all credentials for a role immediately (incident response):

vault lease revoke -prefix database/creds/webapp-role
# Revokes all active leases: every application using these credentials loses access instantly

AppRole Auth and Policies

AppRole is the standard auth method for applications running outside Kubernetes. Applications exchange a Role ID (non-secret, like a username) and a Secret ID (secret, like a password) for a Vault token.

Enable AppRole and create an application role:

# Enable AppRole auth
vault auth enable approle

# Create a policy for the webapp (what it is allowed to read)
vault policy write webapp-policy - <<EOF
# Allow reading only the specific secrets this app needs
path "secret/data/prod/webapp/*" {
  capabilities = ["read"]
}

# Allow generating dynamic database credentials
path "database/creds/webapp-role" {
  capabilities = ["read"]
}

# Allow renewing leases
path "sys/leases/renew" {
  capabilities = ["update"]
}
EOF

# Create an AppRole for the webapp
vault write auth/approle/role/webapp \
  token_policies="webapp-policy" \
  token_ttl=1h \
  token_max_ttl=4h \
  secret_id_ttl=10m \
  secret_id_num_uses=1  # Secret ID is single-use: a new one must be requested each time

# Get the Role ID (non-secret: can be included in app config)
vault read auth/approle/role/webapp/role-id
# role_id: db02de05-fa39-4855-059b-67221c5c2f63

# Generate a Secret ID (inject this at deploy time via CI/CD, not stored with the app)
vault write -f auth/approle/role/webapp/secret-id
# secret_id: 6a174c20-f6de-a53c-74d2-6018fcceff64

# Application login (what the app does at startup)
vault write auth/approle/login \
  role_id="$VAULT_ROLE_ID" \
  secret_id="$VAULT_SECRET_ID"
# Returns a token the app uses for subsequent requests

Kubernetes auth method (for containerized workloads):

vault auth enable kubernetes
vault write auth/kubernetes/config \
  kubernetes_host="https://kubernetes.default.svc" \
  kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt

vault write auth/kubernetes/role/webapp \
  bound_service_account_names=webapp \
  bound_service_account_namespaces=production \
  policies=webapp-policy \
  ttl=1h

# Application running in Kubernetes authenticates with its service account token
# No Secret ID required: the Kubernetes identity IS the credential
vault write auth/kubernetes/login \
  role=webapp \
  jwt=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)

The bottom line

Vault solves secrets sprawl by centralizing credentials behind an authenticated API with fine-grained policies and complete audit logging. Start with KV-v2 for existing static secrets, then migrate to dynamic secrets for database credentials: per-request, auto-expiring credentials eliminate the shared-password rotation problem. Use AppRole for VM-based applications, Kubernetes auth for containerized workloads. The audit log that records every secret access is often as valuable as the secrets management itself.

Frequently asked questions

What is HashiCorp Vault used for?

Vault centralizes secrets management: storing API keys, database passwords, TLS certificates, and cloud credentials in one place with authenticated access, fine-grained policies, and complete audit logging. It also supports dynamic secrets: database credentials generated on-demand with a short TTL (like 5 minutes), so leaked credentials expire automatically rather than needing manual rotation.

How do applications authenticate to HashiCorp Vault?

Applications use auth methods matched to their deployment environment: AppRole (Role ID + one-time Secret ID) for VM-based applications, Kubernetes service account tokens for containerized workloads, AWS IAM roles for EC2/ECS/Lambda, and OIDC for human users. Each method issues a short-lived Vault token that the application uses for subsequent API requests.

What is HashiCorp Vault and what problem does it solve?

HashiCorp Vault is a secrets management platform that centralizes storage, access control, and auditing of secrets: API keys, database credentials, TLS certificates, and SSH keys. Without Vault, secrets are typically stored in environment variables, config files, or CI/CD secret stores — scattered across systems with no centralized audit log of who accessed what. Vault provides: a single API for secret access across all environments; fine-grained access policies (which applications can access which secrets, with optional MFA and time-bound approval); complete audit logs of every secret access; and dynamic secrets (credentials generated on-demand and auto-expired) for databases and cloud providers, eliminating long-lived static credentials entirely.

What are HashiCorp Vault dynamic secrets and how do they work?

Dynamic secrets are credentials generated by Vault on demand and automatically revoked after a configurable lease period. For databases (PostgreSQL, MySQL, MongoDB): Vault creates a new database user with the requested permissions when an application requests credentials, returns the username and password, and drops the user when the lease expires (e.g., 1 hour). For AWS: Vault generates an IAM user or assumes a role and returns temporary access keys. This eliminates long-lived credentials entirely: if the credentials are stolen, they expire within the lease window. Dynamic secrets are the highest-value Vault feature for eliminating credential theft risk.

What is the difference between HashiCorp Vault and AWS Secrets Manager?

Both are secrets management platforms, but they differ in scope. AWS Secrets Manager is cloud-native to AWS: it manages secrets with tight AWS IAM integration, automatic rotation for RDS databases, and simple deployment — but it only works in AWS. HashiCorp Vault is multi-cloud and on-premises: it supports secrets management across AWS, Azure, GCP, on-premises systems, and Kubernetes, making it the choice for multi-cloud or hybrid environments. AWS Secrets Manager is the right choice for AWS-only environments that want minimal operational overhead. Vault is the right choice when you need a single secrets management platform across heterogeneous infrastructure or require dynamic secrets for non-AWS resources.

How do you handle Vault's unseal keys securely in a production deployment?

Vault's unseal keys require careful operational handling because losing them makes encrypted data permanently unrecoverable, and exposing them breaks the security model. Vault uses Shamir's Secret Sharing to split the master key: you specify how many key shares to generate (recommended: 5) and how many are required to unseal (recommended: 3). This means no single person holds enough key material to unseal Vault alone. Distribute the five key shares to five different trusted individuals, with each person storing their share in a separate secure location (a password manager they control, a physical safe, or an HSM). Never store all shares in the same system or with the same person. For production environments, use Vault's auto-unseal feature backed by a cloud KMS (AWS KMS, Azure Key Vault, or GCP Cloud KMS): this removes the need for manual key entry on startup and eliminates the human-process dependency, while the cloud KMS handles key custody. The root token generated during initialization should be used once to configure Vault, then revoked immediately: admin access should use individual Vault user accounts with appropriate policies, not the omnipotent root token. Document the unsealing procedure and distribute it separately from the key shares so the process can be executed by the key holders even during an emergency.

Sources & references

  1. HashiCorp Vault Documentation
  2. Vault Learn: Getting Started
  3. OWASP: Secrets Management Cheat Sheet

Free resources

25
Free download

Critical CVE Reference Card 2025–2026

25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.

No spam. Unsubscribe anytime.

Free download

Ransomware Incident Response Playbook

Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.

No spam. Unsubscribe anytime.

Free newsletter

Get threat intel before your inbox does.

50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.

Unsubscribe anytime. We never sell your data.

Eric Bang
Author

Founder & Cybersecurity Evangelist, Decryption Digest

Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.

Black Hat Giveaway

Win a $2,495 Black Hat pass.

Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.

Joins Decryption Digest daily briefing. Unsubscribe anytime.

Giveaway: Black Hat USA 2026 Full-Access Pass ($2,495 value)

Details →
Daily Briefing

Subscribe to enter the giveaway

Every subscriber is automatically entered. You also get daily threat intel every morning: zero-days, ransomware, and nation-state campaigns. Free. No spam.

Already subscribed? You're already entered.

Giveaway

Win a $2,495 Black Hat USA 2026 pass.