Secrets Management with Vault

SecurityVaultDevOpsSecrets
Share on LinkedIn

Forty microservices each mount a Kubernetes Secret copied from last year's onboarding doc. Rotation means editing YAML in forty repos and hoping nobody cached the old DB password in a crash dump. HashiCorp Vault stores secrets centrally, audits every read, and can mint database credentials that expire in an hour. The operational cost is real—unseal, HA, backups—but static secrets in git is technical debt with compound interest.

Core concepts

Validate this in staging with production-like data volume before declaring done. Capture metrics baseline the week before change and compare for seven days after—subtle regressions hide in aggregates until a large tenant hits the path. Update the on-call runbook with the failure signature and rollback command so responders need not rediscover steps during an incident.

Static secrets in KV v2

vault kv put secret/prod/payment stripe_key=$STRIPE_KEY
vault kv get -field=stripe_key secret/prod/payment

Versioned; rollback supported. Still static—rotate via CI pushing new version and signaling apps to reload.

Validate this in staging with production-like data volume before declaring done. Capture metrics baseline the week before change and compare for seven days after—subtle regressions hide in aggregates until a large tenant hits the path. Update the on-call runbook with the failure signature and rollback command so responders need not rediscover steps during an incident.

Dynamic PostgreSQL credentials

Configure database secrets engine with connection URL and creation SQL:

CREATE ROLE "{{name}}" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';
GRANT SELECT ON orders TO "{{name}}";

Application:

 creds = vault.read("database/creds/orders-readonly")
 conn = psycopg2.connect(user=creds["username"], password=creds["password"], ...)

Renew lease before TTL; Vault revokes role on expiry.

Validate this in staging with production-like data volume before declaring done. Capture metrics baseline the week before change and compare for seven days after—subtle regressions hide in aggregates until a large tenant hits the path. Update the on-call runbook with the failure signature and rollback command so responders need not rediscover steps during an incident.

Kubernetes auth

# Pod annotation for Vault Agent Injector
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "api-service"
vault.hashicorp.com/agent-inject-secret-config: "secret/data/prod/api"

Sidecar renders file or env vars; app unchanged. Map ServiceAccount → Vault role with minimal policy.

Validate this in staging with production-like data volume before declaring done. Capture metrics baseline the week before change and compare for seven days after—subtle regressions hide in aggregates until a large tenant hits the path. Update the on-call runbook with the failure signature and rollback command so responders need not rediscover steps during an incident.

Policy least privilege

path "secret/data/prod/api" {
  capabilities = ["read"]
}
path "database/creds/orders-readonly" {
  capabilities = ["read"]
}

Separate policies per service. Deny by default.

Validate this in staging with production-like data volume before declaring done. Capture metrics baseline the week before change and compare for seven days after—subtle regressions hide in aggregates until a large tenant hits the path. Update the on-call runbook with the failure signature and rollback command so responders need not rediscover steps during an incident.

Unseal and HA

Production uses auto-unseal via cloud KMS. Shamir shards for break-glass only. Monitor seal status; sealed Vault denies all reads—page immediately.

Validate this in staging with production-like data volume before declaring done. Capture metrics baseline the week before change and compare for seven days after—subtle regressions hide in aggregates until a large tenant hits the path. Update the on-call runbook with the failure signature and rollback command so responders need not rediscover steps during an incident.

Enable audit device to immutable storage. Every secret read ties to identity—compliance and incident forensics.

Unseal monitoring pages immediately—sealed Vault denies all reads. Auto-unseal via KMS for production; Shamir shards break-glass only.

Policy least privilege per service account. Deny by default. Audit device to immutable storage for every secret read during incidents.

Kubernetes auth maps service account JWT to role—no root token in cluster. Vault Agent Injector sidecar pattern keeps apps unchanged while credentials rotate.

Validate this in staging with production-like data volume before declaring done. Capture metrics baseline the week before change and compare for seven days after—subtle regressions hide in aggregates until a large tenant hits the path. Update the on-call runbook with the failure signature and rollback command so responders need not rediscover steps during an incident.

Document the decision, owner, and rollback path in your team wiki the same week you ship. Future you will not remember which environment variable toggled the behavior unless it is written next to the runbook entry and linked from the alert. That habit costs ten minutes per change and saves hours when pagination or auth misbehaves under a single large tenant.

Run the change through your standard PR checklist: tests, observability, and a two-minute rollback drill in staging. Small operational habits accumulate into systems that survive on-call nights without heroics.

Share a short write-up in your engineering channel after rollout: what shipped, what metric you watch, and who owns follow-up. That closes the loop for teammates who were not in the PR and surfaces gaps in docs before the next person repeats the same investigation.

Prefer boring, repeatable process over one heroic migration weekend.

Resources

Frequently asked questions

When should I use Vault instead of cloud secret managers?

Vault fits multi-cloud, on-prem hybrid, and dynamic secret generation (database users with TTL) across uniform API. Cloud-native teams often use AWS Secrets Manager or GCP Secret Manager with less ops overhead. Vault wins when you need one abstraction spanning Kubernetes, VMs, and CI with detailed policy-as-code.

What are dynamic secrets?

Vault generates short-lived credentials on demand—Postgres user valid 1 hour, then auto-revoked. Applications request creds at startup and refresh before lease expiry. Compromised credentials self-destruct instead of living until someone rotates a static password in a spreadsheet.

How do pods authenticate to Vault?

Kubernetes auth method maps service account JWT to Vault role and policy. Pod mounts projected SA token, exchanges for Vault token, reads secret path. No long-lived Vault root token in cluster—bootstrap with auto-unseal (KMS) and break-glass procedures only.

Hiring a senior Android / Flutter engineer?

I architect and ship production mobile software — Kotlin, Jetpack Compose, Flutter — for robotics, EV infrastructure, fintech, and real-time systems. Open to remote roles in Europe and the US.

Get in touch →