HashiCorp Vault — Kubernetes Exercises
Practice Vault + Kubernetes integration with these hands-on exercises.
Exercise 1 — Deploy Vault with Helm
Task: Deploy a dev-mode Vault on Kubernetes.
- Add the HashiCorp Helm repo
- Install Vault in dev mode
- Verify it’s running
- Port-forward and access the UI
Show Answer
helm repo add hashicorp https://helm.releases.hashicorp.com
helm repo update
helm install vault hashicorp/vault \
--set "server.dev.enabled=true" \
--namespace vault --create-namespace
# Wait for pods
kubectl -n vault get pods -w
# Port forward
kubectl -n vault port-forward svc/vault 8200:8200 &
# Access
export VAULT_ADDR=http://127.0.0.1:8200
export VAULT_TOKEN=root
vault status
Exercise 2 — Configure Kubernetes Auth
Task: Set up K8s auth and allow a service account to read secrets.
- Enable Kubernetes auth in Vault
- Create a policy for reading
secret/data/myapp/* - Create a role bound to SA
myappin namespacedefault - Test authentication from inside a pod
Show Answer
# Enable K8s auth
kubectl -n vault exec vault-0 -- vault auth enable kubernetes
kubectl -n vault exec vault-0 -- vault write auth/kubernetes/config \
kubernetes_host="https://kubernetes.default.svc"
# Create policy
kubectl -n vault exec vault-0 -- vault policy write myapp - <<EOF
path "secret/data/myapp/*" {
capabilities = ["read"]
}
EOF
# Create role
kubectl -n vault exec vault-0 -- vault write auth/kubernetes/role/myapp \
bound_service_account_names=myapp \
bound_service_account_namespaces=default \
policies=myapp \
ttl=1h
# Create SA and test pod
kubectl create serviceaccount myapp
# Store a test secret
kubectl -n vault exec vault-0 -- vault kv put secret/myapp/config \
username=admin password=s3cret
# Test from a pod
kubectl run test-auth --rm -it \
--image=curlimages/curl \
--serviceaccount=myapp \
--restart=Never -- sh -c '
JWT=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
curl -s --request POST \
--data "{\"jwt\": \"$JWT\", \"role\": \"myapp\"}" \
http://vault.vault.svc.cluster.local:8200/v1/auth/kubernetes/login | head -c 200
'
Exercise 3 — Agent Injector Annotations
Task: Deploy an app with injected secrets using annotations.
- Store credentials at
secret/myapp/db - Create a Deployment with Vault annotations
- Verify secrets appear at
/vault/secrets/
Show Answer
# myapp-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 1
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "myapp"
vault.hashicorp.com/agent-inject-secret-db.env: "secret/data/myapp/db"
vault.hashicorp.com/agent-inject-template-db.env: |
{{ with secret "secret/data/myapp/db" -}}
DB_USER={{ .Data.data.username }}
DB_PASS={{ .Data.data.password }}
{{- end }}
spec:
serviceAccountName: myapp
containers:
- name: myapp
image: busybox
command: ["sh", "-c", "cat /vault/secrets/db.env && sleep 3600"]
kubectl apply -f myapp-deployment.yaml
kubectl logs -f deployment/myapp -c myapp
# DB_USER=admin
# DB_PASS=s3cret
Exercise 4 — CSI Provider
Task: Mount Vault secrets using the CSI Provider.
- Install the Secrets Store CSI Driver
- Create a SecretProviderClass for
secret/myapp/db - Deploy a pod that mounts the secrets as a volume
Show Answer
# secret-provider.yaml
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: vault-db
spec:
provider: vault
parameters:
vaultAddress: "http://vault.vault.svc.cluster.local:8200"
roleName: "myapp"
objects: |
- objectName: "db-password"
secretPath: "secret/data/myapp/db"
secretKey: "password"
- objectName: "db-username"
secretPath: "secret/data/myapp/db"
secretKey: "username"
---
# pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: csi-test
spec:
serviceAccountName: myapp
containers:
- name: app
image: busybox
command: ["sh", "-c", "cat /mnt/secrets/db-password && sleep 3600"]
volumeMounts:
- name: vault-secrets
mountPath: /mnt/secrets
readOnly: true
volumes:
- name: vault-secrets
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: vault-db
Exercise 5 — External Secrets Operator
Task: Sync a Vault secret to a Kubernetes Secret using ESO.
Show Answer
# secret-store.yaml
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: vault
spec:
provider:
vault:
server: "http://vault.vault.svc.cluster.local:8200"
path: "secret"
version: "v2"
auth:
kubernetes:
mountPath: "kubernetes"
role: "myapp"
serviceAccountRef:
name: myapp
---
# external-secret.yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: myapp-db
spec:
refreshInterval: 1h
secretStoreRef:
name: vault
kind: SecretStore
target:
name: myapp-db-k8s-secret
data:
- secretKey: username
remoteRef:
key: myapp/db
property: username
- secretKey: password
remoteRef:
key: myapp/db
property: password
kubectl apply -f secret-store.yaml
kubectl apply -f external-secret.yaml
# Verify the K8s Secret was created
kubectl get secret myapp-db-k8s-secret -o jsonpath='{.data.password}' | base64 -d
# s3cret
What’s Next?
With Kubernetes integration covered, let’s look at running Vault in High Availability — Raft consensus, auto-unseal, and disaster recovery.
Next Chapter: High Availability