DevSecOps - Kubernetes DevOps & Security
HashiCorp Vault Kubernetes
Demo Vault Annotations Template
In this tutorial, we’ll explore how to use HashiCorp Vault annotations and templates to inject secrets into Kubernetes Pods via the Vault Agent Injector. Annotations control both the injection process and how the Vault Agent interacts with Vault.
Table of Contents
- Prerequisites
- Vault Annotation Overview
- 1. Injecting the Full Secret Map
- 2. Rendering a Single Field with Templates
- 3. Injecting Multiple Secrets with Templates
- Pod Initialization and Containers
- Conclusion
- References
Prerequisites
- A running Kubernetes cluster (v1.16+).
- A Vault server with KV v2 secrets stored at
crds/data/mysql
. - An existing
php
Deployment applied in your cluster.
KV v2 Path Notice
When using KV v2, remember that paths include /data/
(e.g., crds/data/mysql
).
Vault Annotation Overview
Vault annotations fall into two main categories:
Annotation Group | Controls |
---|---|
Agent annotations | Secret retrieval, templating, injection toggles |
Vault annotations | Connection settings (address, TLS, auth role) |
Below is a quick reference for the five annotations used in this demo:
Annotation | Purpose | Default / Values |
---|---|---|
vault.hashicorp.com/agent-inject | Enable or disable injection | "true" / "false" (default) |
vault.hashicorp.com/agent-inject-status | Update existing secrets instead of fresh injection | "update" |
vault.hashicorp.com/agent-inject-secret-<name> | Define a secret path under a unique <name> (e.g., username ) | — |
vault.hashicorp.com/agent-inject-template-<name> | Provide a template for rendering the <name> secret; must match the <name> | — |
vault.hashicorp.com/role | Vault role used for agent authentication | — |
1. Injecting the Full Secret Map
By default, the Vault Agent Injector writes both the data and metadata of a KV secret into a single file.
Create
patch-annotations.yaml
:spec: template: metadata: annotations: vault.hashicorp.com/agent-inject: "true" vault.hashicorp.com/agent-inject-secret-username: "crds/data/mysql" vault.hashicorp.com/role: "phpapp"
Apply the patch:
kubectl patch deploy php -p "$(cat patch-annotations.yaml)"
Verify the injected content:
POD=$(kubectl get po -l app=php -o jsonpath='{.items[0].metadata.name}') kubectl exec -it $POD -- cat /vault/secrets/username
Output:
data: map[password:12345 username:root] metadata: map[created_time:... deletion_time: destroyed:false version:1]
2. Rendering a Single Field with Templates
To extract only a specific field (e.g., username
), use a templating annotation.
Create
patch-annotations-template.yaml
:spec: template: metadata: annotations: vault.hashicorp.com/agent-inject: "true" vault.hashicorp.com/agent-inject-status: "update" vault.hashicorp.com/agent-inject-secret-username: "crds/data/mysql" vault.hashicorp.com/agent-inject-template-username: | {{- with secret "crds/data/mysql" -}} {{ .Data.data.username }} {{- end }} vault.hashicorp.com/role: "phpapp"
Apply the patch and wait for the new Pod:
kubectl patch deploy php -p "$(cat patch-annotations-template.yaml)"
Confirm the output:
POD=$(kubectl get po -l app=php -o jsonpath='{.items[0].metadata.name}') kubectl exec -it $POD -- cat /vault/secrets/username
Expected:
root
3. Injecting Multiple Secrets with Templates
You can inject several secrets into separate files by defining multiple <name>
annotations.
Create
patch-annotations-multi.yaml
:spec: template: metadata: annotations: vault.hashicorp.com/agent-inject: "true" vault.hashicorp.com/agent-inject-status: "update" vault.hashicorp.com/agent-inject-secret-username: "crds/data/mysql" vault.hashicorp.com/agent-inject-template-username: | {{- with secret "crds/data/mysql" -}} {{ .Data.data.username }} {{- end }} vault.hashicorp.com/agent-inject-secret-password: "crds/data/mysql" vault.hashicorp.com/agent-inject-template-password: | {{- with secret "crds/data/mysql" -}} {{ .Data.data.password }} {{- end }} vault.hashicorp.com/agent-inject-secret-apikey: "crds/data/mysql" vault.hashicorp.com/agent-inject-template-apikey: | {{- with secret "crds/data/mysql" -}} {{ .Data.data.apikey }} {{- end }} vault.hashicorp.com/role: "phpapp"
Apply the patch:
kubectl patch deploy php -p "$(cat patch-annotations-multi.yaml)"
List the injected files:
POD=$(kubectl get po -l app=php -o jsonpath='{.items[0].metadata.name}') kubectl exec -it $POD -- ls /vault/secrets
Expected:
username password apikey
Verify each secret:
kubectl exec -it $POD -- cat /vault/secrets/username # root kubectl exec -it $POD -- cat /vault/secrets/password # 12345 kubectl exec -it $POD -- cat /vault/secrets/apikey # Vbdj794HNUH8945tojr3
Pod Initialization and Containers
After applying annotations, inspect the Pod:
kubectl describe pod <pod-name>
You’ll see three containers:
- vault-agent-init (initContainer)
- vault-agent (sidecar)
- php (your application)
These handle authentication, periodic secret renewal, and your app’s access to /vault/secrets
.
Conclusion
In this demo, you learned how to:
- Enable full secret map injection
- Render specific secret fields with templates
- Inject multiple secrets into separate files
Using Vault annotations and templates helps keep your Kubernetes workloads secure and your secrets management automated.
References
Watch Video
Watch video content