Introduction
HashiCorp Vault is a secrets management tool that provides secure storage and access control for sensitive data like API keys, passwords, and certificates. Without Vault, secrets end up hardcoded in code, stored in environment variables, or scattered across configuration files — any of which are security risks. This guide teaches you to deploy Vault, store secrets, and integrate it with your applications.
Why Vault?
- Dynamic secrets: Vault can generate database credentials on-demand (auto-expiring)
- Secret leasing: Secrets have TTLs and are automatically revoked
- Audit logging: Every secret access is logged
- Fine-grained access: Policies control exactly what each app can read
- Encryption as a service: Encrypt data without managing keys yourself
Installation and Initialization
# Install Vault (Linux)
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install vault
# Docker deployment
docker run -d --name vault --cap-add IPC_LOCK -p 8200:8200 -e VAULT_ADDR=http://0.0.0.0:8200 -v vault-data:/vault/data -v ./vault-config.hcl:/vault/config/vault.hcl hashicorp/vault:latest server# vault-config.hcl
ui = true
storage "file" {
path = "/vault/data"
}
listener "tcp" {
address = "0.0.0.0:8200"
tls_disable = 1 # Enable TLS in production!
}
api_addr = "http://vault.company.com:8200"# Initialize Vault (run once)
export VAULT_ADDR='http://localhost:8200'
vault operator init -key-shares=5 -key-threshold=3
# This outputs:
# Unseal Key 1: XXXX
# Unseal Key 2: XXXX
# ...
# Initial Root Token: hvs.XXXX
# SAVE THESE SECURELY! You need 3 of 5 keys to unseal
# Unseal Vault (need 3 keys)
vault operator unseal <key1>
vault operator unseal <key2>
vault operator unseal <key3>
# Verify
vault status
# Login with root token
vault login hvs.XXXXStoring and Retrieving Secrets
# Enable KV secrets engine
vault secrets enable -path=secret kv-v2
# Store a secret
vault kv put secret/myapp/database username=appuser password=Str0ngP@ss! host=db.company.com
# Read a secret
vault kv get secret/myapp/database
# Key Value
# --- -----
# username appuser
# password Str0ngP@ss!
# Get specific field
vault kv get -field=password secret/myapp/database
# Update (add new version)
vault kv patch secret/myapp/database password=NewP@ss!
# List versions
vault kv metadata get secret/myapp/database
# Read specific version
vault kv get -version=1 secret/myapp/databaseAccess Policies
# policies/myapp-policy.hcl
# Allow myapp to read its own secrets only
path "secret/data/myapp/*" {
capabilities = ["read", "list"]
}
# Allow renewing its own token
path "auth/token/renew-self" {
capabilities = ["update"]
}
# Deny everything else (implicit)# Create policy
vault policy write myapp policies/myapp-policy.hcl
# Create token with policy
vault token create -policy=myapp -ttl=1h -renewable=true
# Or use AppRole auth (better for applications)
vault auth enable approle
vault write auth/approle/role/myapp token_policies=myapp token_ttl=1h token_max_ttl=4h
# Get role credentials
vault read auth/approle/role/myapp/role-id
vault write -f auth/approle/role/myapp/secret-id
# App logs in with these credentials
vault write auth/approle/login role_id=<role-id> secret_id=<secret-id>Dynamic Database Secrets
The killer feature: Vault generates temporary DB credentials that auto-expire:
# Enable database secrets engine
vault secrets enable database
# Configure PostgreSQL connection
vault write database/config/myapp-db plugin_name=postgresql-database-plugin allowed_roles="myapp-role" connection_url="postgresql://vault_admin:vaultpass@db.company.com:5432/appdb" username="vault_admin" password="vaultpass"
# Define role that creates short-lived credentials
vault write database/roles/myapp-role db_name=myapp-db 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="1h" max_ttl="24h"
# Request dynamic credentials (each request gets unique credentials!)
vault read database/creds/myapp-role
# Key Value
# --- -----
# lease_duration 1h
# username v-myapp-AbCdEfGh1234
# password A1B2C3D4E5F6G7H8I9J0Application Integration
#!/usr/bin/env python3
# vault_client.py - Fetch secrets from Vault at startup
import hvac
import os
import time
class VaultSecrets:
def __init__(self, vault_addr, role_id, secret_id):
self.client = hvac.Client(url=vault_addr)
self.client.auth.approle.login(
role_id=role_id,
secret_id=secret_id
)
def get_secret(self, path, key=None):
data = self.client.secrets.kv.v2.read_secret_version(
path=path,
mount_point='secret'
)
if key:
return data['data']['data'][key]
return data['data']['data']
def get_database_credentials(self, role):
'''Get dynamic database credentials.'''
creds = self.client.secrets.database.generate_credentials(name=role)
return {
'username': creds['data']['username'],
'password': creds['data']['password'],
'lease_id': creds['lease_id'],
'lease_duration': creds['lease_duration']
}
def renew_token(self):
'''Renew Vault token before expiry.'''
self.client.auth.token.renew_self()
# Usage in application
vault = VaultSecrets(
vault_addr=os.environ['VAULT_ADDR'],
role_id=os.environ['VAULT_ROLE_ID'],
secret_id=os.environ['VAULT_SECRET_ID']
)
# Get static secret
api_key = vault.get_secret('myapp/external-api', 'api_key')
# Get dynamic database credentials
db_creds = vault.get_database_credentials('myapp-role')
db_url = f"postgresql://{db_creds['username']}:{db_creds['password']}@db:5432/appdb"Kubernetes Integration with Vault Agent
# vault-agent-config.hcl (as ConfigMap)
auto_auth {
method "kubernetes" {
mount_path = "auth/kubernetes"
config = {
role = "myapp"
}
}
sink "file" {
config = {
path = "/home/vault/.vault-token"
}
}
}
template {
source = "/vault/templates/config.tmpl"
destination = "/vault/secrets/config.env"
perms = 0400
}# config.tmpl
{{ with secret "secret/data/myapp/config" }}
DATABASE_URL=postgresql://{{ .Data.data.db_user }}:{{ .Data.data.db_pass }}@db:5432/app
API_KEY={{ .Data.data.api_key }}
{{ end }}# Pod spec with Vault Agent sidecar
spec:
serviceAccountName: myapp
initContainers:
- name: vault-agent
image: vault:latest
command: ["vault", "agent", "-config=/vault/config/vault-agent.hcl", "-exit-after-auth"]
volumeMounts:
- name: vault-config
mountPath: /vault/config
- name: vault-secrets
mountPath: /vault/secrets
containers:
- name: app
env:
- name: CONFIG_PATH
value: /vault/secrets/config.env
volumeMounts:
- name: vault-secrets
mountPath: /vault/secretsVault transforms secret management from a scattered, insecure practice to a centralized, audited system. Start with static KV secrets, then graduate to dynamic database credentials — the productivity and security gains are immediate.
