Kubernetes Secrets are only base64-encoded, they end up in Git or Helm values far too easily, and rotating a database password means redeploying everything. With HashiCorp Vault and the Vault Agent Injector, pods authenticate with their Kubernetes service account and receive secrets as files, without the application knowing Vault exists.
1. Enable Kubernetes auth in Vault
vault auth enable kubernetes
vault write auth/kubernetes/config \
kubernetes_host="https://kubernetes.default.svc:443"
When Vault runs inside the same cluster, it can use its own service account token and CA to validate other pods' tokens.
2. Policy and role
# account-search.hcl
path "secret/data/account-search/*" {
capabilities = ["read"]
}
vault policy write account-search account-search.hcl
vault write auth/kubernetes/role/account-search \
bound_service_account_names=account-search \
bound_service_account_namespaces=apps \
policies=account-search \
ttl=1h
Only pods running as the account-search service account in the apps namespace can log in with this role, and they can only read their own path.
3. Annotate the Deployment
spec:
template:
metadata:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "account-search"
vault.hashicorp.com/agent-inject-secret-db.properties: "secret/data/account-search/db"
vault.hashicorp.com/agent-inject-template-db.properties: |
{{- with secret "secret/data/account-search/db" -}}
spring.datasource.username={{ .Data.data.username }}
spring.datasource.password={{ .Data.data.password }}
{{- end }}
spec:
serviceAccountName: account-search
The injector adds an init container and a sidecar. The rendered file appears at /vault/secrets/db.properties and the sidecar keeps it fresh when the secret changes or the lease renews.
4. Let Spring Boot read it
# application.yml
spring:
config:
import: "optional:file:/vault/secrets/db.properties"
No Vault SDK, no code change. Locally, developers use their own properties file at the same path.
Things to watch
- The template path uses
secret/data/...for KV version 2. Forgetting/data/is the most common “permission denied”. - Rotation is not reload. The file updates, but a running connection pool keeps the old password until restart unless your app re-reads it. Pair rotation with a rolling restart or use Vault dynamic database credentials.
- Alternatives: the Vault Secrets Operator syncs into native Kubernetes Secrets, and the Secrets Store CSI driver mounts secrets as volumes. Pick one approach per cluster and stick to it.
- Keep Vault patched and restrict who can restore snapshots; Vault is now the most valuable target in the cluster.
No comments:
Post a Comment