๐Ÿ”

HashiCorp Vault

HashiCorp Vault: secrets engines, auth methods, policies, dynamic secrets, PKI, encryption, and CLI

vault CLI

Core vault commands: server, init, unseal, status, and login

bashยทServer & init
# Start a dev server (in-memory, auto-unsealed โ€” NOT for production)
vault server -dev -dev-root-token-id="root"

# Start with a config file
vault server -config=/etc/vault.d/vault.hcl

# Initialize a new Vault cluster (first time only)
vault operator init
# Outputs 5 unseal keys + 1 root token โ€” store these securely!

# Initialize with custom key shares and threshold
vault operator init -key-shares=3 -key-threshold=2

# Check Vault status
vault status
bashยทUnseal & seal
# Unseal Vault (run multiple times with different keys until threshold met)
vault operator unseal <unseal-key-1>
vault operator unseal <unseal-key-2>

# Seal Vault immediately (emergency shutdown)
vault operator seal

# Rekey โ€” replace unseal keys with new ones
vault operator rekey -init -key-shares=5 -key-threshold=3
vault operator rekey <existing-unseal-key>

# Generate a new root token (requires quorum of unseal keys)
vault operator generate-root -init
vault operator generate-root <unseal-key>
bashยทLogin & environment
# Set Vault address (or export VAULT_ADDR)
export VAULT_ADDR='https://vault.example.com:8200'
export VAULT_TOKEN='hvs.xxxxxxxxxxxx'
export VAULT_NAMESPACE='admin'         # Vault Enterprise

# Login with token
vault login hvs.xxxxxxxxxxxx

# Login with different auth methods
vault login -method=userpass username=alice
vault login -method=ldap username=alice
vault login -method=aws

# Check current token info
vault token lookup

# Revoke current token
vault token revoke -self

# Output as JSON
vault status -format=json

KV Secrets Engine

Key/Value secrets engine v1 and v2 (versioned)

bashยทKV v2 (versioned)
# Enable KV v2 at a path
vault secrets enable -path=secret kv-v2

# Write a secret
vault kv put secret/myapp/config db_user=admin db_pass=s3cr3t

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

# Get a specific field
vault kv get -field=db_pass secret/myapp/config

# Get as JSON
vault kv get -format=json secret/myapp/config | jq '.data.data'

# Update (creates new version)
vault kv put secret/myapp/config db_user=admin db_pass=newpass

# Patch (update only specified fields)
vault kv patch secret/myapp/config db_pass=updated

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

# Delete latest version (soft delete)
vault kv delete secret/myapp/config

# Undelete (restore) a version
vault kv undelete -versions=2 secret/myapp/config

# Destroy specific versions permanently
vault kv destroy -versions=1,2 secret/myapp/config
bashยทKV versioning
# Read a specific version
vault kv get -version=2 secret/myapp/config

# View version metadata
vault kv metadata get secret/myapp/config

# Set max versions to keep
vault kv metadata put -max-versions=10 secret/myapp/config

# Set delete-version-after (auto-expire versions)
vault kv metadata put -delete-version-after=72h secret/myapp/config

# Rollback to a previous version
vault kv rollback -version=3 secret/myapp/config

# Delete all versions and metadata permanently
vault kv metadata delete secret/myapp/config

Dynamic Secrets

Database and AWS dynamic credentials with automatic rotation

bashยทDatabase secrets engine
# Enable the database secrets engine
vault secrets enable database

# Configure a PostgreSQL connection
vault write database/config/mypostgres   plugin_name=postgresql-database-plugin   allowed_roles="readonly,readwrite"   connection_url="postgresql://{{username}}:{{password}}@db.example.com:5432/mydb"   username="vault_admin"   password="vault_admin_pass"

# Rotate the root credentials (Vault takes over)
vault write -force database/rotate-root/mypostgres

# Create a role that generates short-lived credentials
vault write database/roles/readonly   db_name=mypostgres   creation_statements="CREATE ROLE "{{name}}" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT ON ALL TABLES IN SCHEMA public TO "{{name}}";"   default_ttl="1h"   max_ttl="24h"

# Generate dynamic credentials
vault read database/creds/readonly
# => username: v-readonly-xyz123
# => password: A1b2C3d4...
# => lease_duration: 1h
bashยทAWS secrets engine
# Enable the AWS secrets engine
vault secrets enable aws

# Configure with IAM credentials
vault write aws/config/root   access_key=AKIAIOSFODNN7EXAMPLE   secret_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY   region=us-east-1

# Create a role for dynamic IAM user credentials
vault write aws/roles/s3-reader   credential_type=iam_user   policy_document=-<<EOF
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": ["s3:GetObject", "s3:ListBucket"],
    "Resource": "*"
  }]
}
EOF

# Generate credentials
vault read aws/creds/s3-reader

# Assume role (no long-lived IAM user created)
vault write aws/roles/deployer   credential_type=assumed_role   role_arns=arn:aws:iam::123456789012:role/DeployRole

vault read aws/sts/deployer

Auth Methods

Enabling and configuring auth methods: token, AppRole, Kubernetes, AWS, LDAP

bashยทToken auth
# Create a token with a policy
vault token create -policy=readonly -ttl=1h

# Create a periodic token (never expires if renewed)
vault token create -policy=readonly -period=24h

# Create an orphan token (no parent)
vault token create -orphan -policy=readonly

# Renew a token
vault token renew hvs.xxxxxxxxxxxx

# Revoke a token and all its children
vault token revoke hvs.xxxxxxxxxxxx

# Look up token details
vault token lookup hvs.xxxxxxxxxxxx

# Create a token with use limit
vault token create -policy=readonly -use-limit=5
bashยทAppRole auth
# Enable AppRole auth method
vault auth enable approle

# Create a named role
vault write auth/approle/role/myapp   token_policies="myapp-policy"   token_ttl=1h   token_max_ttl=4h   secret_id_ttl=10m   secret_id_num_uses=1

# Get the Role ID (static โ€” embed in your app config)
vault read auth/approle/role/myapp/role-id

# Generate a Secret ID (dynamic โ€” deliver securely at runtime)
vault write -force auth/approle/role/myapp/secret-id

# Login with Role ID + Secret ID
vault write auth/approle/login   role_id=<role-id>   secret_id=<secret-id>

# Lookup a SecretID accessor
vault write auth/approle/role/myapp/secret-id/lookup   secret_id_accessor=<accessor>
bashยทKubernetes auth
# Enable Kubernetes auth method
vault auth enable kubernetes

# Configure with the cluster's API server
vault write auth/kubernetes/config   kubernetes_host=https://kubernetes.default.svc   kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt   token_reviewer_jwt=@/var/run/secrets/kubernetes.io/serviceaccount/token

# Create a role binding a ServiceAccount to a Vault policy
vault write auth/kubernetes/role/myapp   bound_service_account_names=myapp-sa   bound_service_account_namespaces=production   token_policies=myapp-policy   token_ttl=1h

# Login from within a pod
vault write auth/kubernetes/login   role=myapp   jwt=@/var/run/secrets/kubernetes.io/serviceaccount/token
bashยทAWS & LDAP auth
# Enable AWS auth (IAM-based โ€” no credentials needed in EC2/ECS/Lambda)
vault auth enable aws

vault write auth/aws/config/client   access_key=AKIAIOSFODNN7EXAMPLE   secret_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

vault write auth/aws/role/ec2-role   auth_type=iam   bound_iam_principal_arn=arn:aws:iam::123456789012:role/EC2Role   policies=ec2-policy   token_ttl=1h

# Login (typically done by Vault Agent or SDK)
vault login -method=aws role=ec2-role

# Enable LDAP auth
vault auth enable ldap

vault write auth/ldap/config   url="ldaps://ldap.example.com"   userdn="ou=users,dc=example,dc=com"   groupdn="ou=groups,dc=example,dc=com"   binddn="cn=vault,ou=service,dc=example,dc=com"   bindpass="ldap_bind_pass"   userattr="uid"

vault write auth/ldap/groups/engineering policies=engineering-policy

Policies

Writing and managing HCL policies for access control

hclยทPolicy HCL syntax
# /etc/vault/policies/myapp.hcl

# Allow reading specific secrets
path "secret/data/myapp/*" {
  capabilities = ["read", "list"]
}

# Allow full access to own namespace
path "secret/data/myapp/config" {
  capabilities = ["create", "read", "update", "delete", "list"]
}

# Allow generating database credentials
path "database/creds/readonly" {
  capabilities = ["read"]
}

# Allow renewing own token
path "auth/token/renew-self" {
  capabilities = ["update"]
}

# Allow looking up own token
path "auth/token/lookup-self" {
  capabilities = ["read"]
}

# Deny access explicitly (takes precedence)
path "secret/data/admin/*" {
  capabilities = ["deny"]
}
bashยทPolicy management
# Write a policy from file
vault policy write myapp /etc/vault/policies/myapp.hcl

# Write a policy from stdin
vault policy write readonly - <<EOF
path "secret/data/*" {
  capabilities = ["read", "list"]
}
EOF

# List all policies
vault policy list

# Read a policy
vault policy read myapp

# Delete a policy
vault policy delete myapp

# Test what a token can access (requires Vault Enterprise or root)
vault token capabilities secret/data/myapp/config

# Check capabilities of the current token
vault token capabilities secret/data/myapp/config

PKI Secrets Engine

Internal CA, certificate issuance, and rotation with the PKI engine

bashยทSetup CA hierarchy
# Enable PKI for root CA
vault secrets enable -path=pki pki
vault secrets tune -max-lease-ttl=87600h pki   # 10 years

# Generate root CA
vault write -field=certificate pki/root/generate/internal   common_name="Example Root CA"   ttl=87600h > root_ca.crt

# Configure CRL and issuing URLs
vault write pki/config/urls   issuing_certificates="https://vault.example.com:8200/v1/pki/ca"   crl_distribution_points="https://vault.example.com:8200/v1/pki/crl"

# Enable PKI for intermediate CA
vault secrets enable -path=pki_int pki
vault secrets tune -max-lease-ttl=43800h pki_int  # 5 years

# Generate intermediate CSR
vault write -format=json pki_int/intermediate/generate/internal   common_name="Example Intermediate CA" | jq -r '.data.csr' > int_ca.csr

# Sign intermediate with root
vault write -format=json pki/root/sign-intermediate   csr=@int_ca.csr format=pem_bundle ttl=43800h   | jq -r '.data.certificate' > int_ca.crt

# Set the signed intermediate certificate
vault write pki_int/intermediate/set-signed certificate=@int_ca.crt
bashยทIssue certificates
# Create a role for issuing certs
vault write pki_int/roles/example-dot-com   allowed_domains="example.com"   allow_subdomains=true   max_ttl=72h   require_cn=false

# Issue a certificate
vault write pki_int/issue/example-dot-com   common_name="api.example.com"   ttl=24h

# Issue with SANs
vault write pki_int/issue/example-dot-com   common_name="api.example.com"   alt_names="api-internal.example.com,localhost"   ip_sans="10.0.1.10"   ttl=24h

# Sign an external CSR
vault write pki_int/sign/example-dot-com   csr=@myapp.csr   common_name="myapp.example.com"   ttl=24h

# Revoke a certificate by serial number
vault write pki_int/revoke serial_number=<serial>

# Rotate CRL manually
vault write pki_int/crl/rotate

Transit (Encryption as a Service)

Encrypt, decrypt, sign, and verify data without storing it in Vault

bashยทEncrypt & decrypt
# Enable the transit secrets engine
vault secrets enable transit

# Create an encryption key
vault write -f transit/keys/myapp-key

# Create an AES-256-GCM key with convergent encryption
vault write transit/keys/myapp-key   type=aes256-gcm96   convergent_encryption=true   derived=true

# Encrypt plaintext (must be base64-encoded)
vault write transit/encrypt/myapp-key   plaintext=$(echo -n "my secret value" | base64)
# => ciphertext: vault:v1:ABCdef...

# Decrypt ciphertext
vault write transit/decrypt/myapp-key   ciphertext="vault:v1:ABCdef..."
# => plaintext: base64-encoded result
echo "base64-result" | base64 --decode

# Batch encrypt multiple values
vault write transit/encrypt/myapp-key   batch_input='[{"plaintext":"dmFsdWUx"},{"plaintext":"dmFsdWUy"}]'
bashยทKey rotation & signing
# Rotate the encryption key (new version created, old still decrypts)
vault write -f transit/keys/myapp-key/rotate

# Update minimum decryption version (retire old key versions)
vault write transit/keys/myapp-key/config   min_decryption_version=2

# Rewrap ciphertext to latest key version
vault write transit/rewrap/myapp-key   ciphertext="vault:v1:ABCdef..."

# Create an ECDSA signing key
vault write -f transit/keys/signing-key type=ecdsa-p256

# Sign data
vault write transit/sign/signing-key   input=$(echo -n "data to sign" | base64)
# => signature: vault:v1:MEUCIQDx...

# Verify a signature
vault write transit/verify/signing-key   input=$(echo -n "data to sign" | base64)   signature="vault:v1:MEUCIQDx..."

# Generate an HMAC
vault write transit/hmac/myapp-key   input=$(echo -n "data" | base64)

Leases & Renewal

Managing secret leases, renewal, and revocation

bashยทLease management
# List all leases for a path
vault list sys/leases/lookup/database/creds/readonly

# Look up a specific lease
vault write sys/leases/lookup   lease_id="database/creds/readonly/abc123"

# Renew a lease
vault lease renew database/creds/readonly/abc123

# Renew with specific increment (in seconds)
vault lease renew -increment=3600 database/creds/readonly/abc123

# Revoke a specific lease immediately
vault lease revoke database/creds/readonly/abc123

# Revoke all leases under a prefix
vault lease revoke -prefix database/creds/readonly

# Force-revoke (ignore errors)
vault lease revoke -force -prefix database/creds/readonly

Vault Agent

Vault Agent for auto-auth, secret templating, and caching

hclยทAgent config
# /etc/vault-agent/agent.hcl

vault {
  address = "https://vault.example.com:8200"
}

# Auto-auth using Kubernetes service account
auto_auth {
  method "kubernetes" {
    mount_path = "auth/kubernetes"
    config = {
      role = "myapp"
    }
  }

  sink "file" {
    config = {
      path = "/tmp/vault-token"
    }
  }
}

# Cache secrets locally (reduces load on Vault)
cache {
  use_auto_auth_token = true
}

listener "tcp" {
  address     = "127.0.0.1:8007"
  tls_disable = true
}

# Template โ€” render secrets to disk
template {
  source      = "/etc/vault-agent/templates/db.ctmpl"
  destination = "/etc/myapp/db.env"
  perms       = "0640"
  command     = "systemctl reload myapp"
}
bashยทTemplate syntax
# /etc/vault-agent/templates/db.ctmpl
# Uses Consul Template syntax

{{- with secret "secret/data/myapp/config" -}}
DB_HOST={{ .Data.data.db_host }}
DB_USER={{ .Data.data.db_user }}
DB_PASS={{ .Data.data.db_pass }}
{{- end }}

# Dynamic database credentials (auto-renewed)
{{- with secret "database/creds/readonly" -}}
DB_USERNAME={{ .Data.username }}
DB_PASSWORD={{ .Data.password }}
{{- end }}

# PKI certificate
{{- with secret "pki_int/issue/example-dot-com" "common_name=myapp.example.com" "ttl=24h" -}}
{{ .Data.certificate }}
{{ .Data.private_key }}
{{- end }}

# Run Vault Agent
# vault agent -config=/etc/vault-agent/agent.hcl

Secrets Engine Management

Enabling, tuning, moving, and auditing secrets engines

bashยทManage secrets engines
# List all enabled secrets engines
vault secrets list

# List with details
vault secrets list -detailed

# Enable a secrets engine at a custom path
vault secrets enable -path=myapp/kv kv-v2

# Tune a secrets engine (e.g., default lease TTL)
vault secrets tune   -default-lease-ttl=1h   -max-lease-ttl=24h   secret/

# Move a secrets engine (preserves data)
vault secrets move secret/ secret-old/

# Disable a secrets engine (DESTROYS all data at that path)
vault secrets disable secret/

# Enable audit logging to a file
vault audit enable file file_path=/var/log/vault/audit.log

# Enable audit logging to syslog
vault audit enable syslog

# List audit devices
vault audit list
bashยทSys & health
# Check Vault health (no auth required)
curl -s https://vault.example.com:8200/v1/sys/health | jq

# List all mounted paths (sys/mounts)
vault read sys/mounts

# Read Vault configuration
vault read sys/config/state/sanitized

# View current HA leader
vault operator raft list-peers         # Integrated storage (Raft)

# Take a snapshot of Raft storage
vault operator raft snapshot save vault-backup.snap

# Restore from snapshot
vault operator raft snapshot restore vault-backup.snap

# Step down as leader
vault operator step-down