HashiCorp Vault — Kubernetes Auth
The Kubernetes auth method lets pods authenticate to Vault using their Kubernetes Service Account JWT tokens.
How It Works
1. Pod starts with a service account token
2. Pod (or agent) sends JWT to Vault
3. Vault validates JWT against Kubernetes TokenReview API
4. Vault checks: service account name + namespace match a role?
5. If valid → Vault returns a token with attached policies
Enable and Configure
# Enable Kubernetes auth
vault auth enable kubernetes
# Configure (from inside the cluster)
vault write auth/kubernetes/config \
kubernetes_host="https://kubernetes.default.svc"
# If running Vault outside the cluster, provide CA and token:
vault write auth/kubernetes/config \
kubernetes_host="https://my-k8s-api:6443" \
kubernetes_ca_cert=@/path/to/k8s-ca.crt \
token_reviewer_jwt="$(cat /path/to/reviewer-token)"
Create Roles
A role binds a Kubernetes service account to Vault policies:
# Basic role
vault write auth/kubernetes/role/myapp \
bound_service_account_names=myapp \
bound_service_account_namespaces=default \
policies=myapp-policy \
ttl=1h
# Wildcard namespace
vault write auth/kubernetes/role/shared-reader \
bound_service_account_names=default \
bound_service_account_namespaces="*" \
policies=reader \
ttl=30m
# Multiple service accounts and namespaces
vault write auth/kubernetes/role/platform-services \
bound_service_account_names="api-svc,worker-svc,cron-svc" \
bound_service_account_namespaces="production,staging" \
policies="platform-policy" \
ttl=1h
Service Account Setup
# Create a service account for your app
apiVersion: v1
kind: ServiceAccount
metadata:
name: myapp
namespace: default
---
# Optionally create a long-lived token (K8s 1.24+)
apiVersion: v1
kind: Secret
metadata:
name: myapp-token
annotations:
kubernetes.io/service-account.name: myapp
type: kubernetes.io/service-account-token
Authenticate from a Pod
# Inside a pod with the myapp service account:
JWT=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
# Authenticate to Vault
curl -s --request POST \
--data "{\"jwt\": \"$JWT\", \"role\": \"myapp\"}" \
"$VAULT_ADDR/v1/auth/kubernetes/login" | jq
Response:
{
"auth": {
"client_token": "hvs.CAESIG...",
"policies": ["default", "myapp-policy"],
"metadata": {
"role": "myapp",
"service_account_name": "myapp",
"service_account_namespace": "default"
},
"lease_duration": 3600,
"renewable": true
}
}
Role Parameters
| Parameter | Description |
|---|---|
bound_service_account_names |
Allowed SA names (comma-separated) |
bound_service_account_namespaces |
Allowed namespaces (comma-separated or *) |
policies |
Vault policies to attach |
ttl |
Token TTL |
max_ttl |
Maximum token TTL |
token_type |
service or batch |
audience |
Expected JWT audience |
Vault Token Reviewer
Vault needs permission to call the Kubernetes TokenReview API:
# ClusterRoleBinding for Vault's service account
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: vault-auth-delegator
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:auth-delegator
subjects:
- kind: ServiceAccount
name: vault
namespace: vault
If using the Helm chart, this is created automatically.
Namespace-Scoped Policies
# Policy template using Kubernetes metadata
path "secret/data/{{identity.entity.aliases.AUTH_MOUNT_ACCESSOR.metadata.service_account_namespace}}/*" {
capabilities = ["read"]
}
Each namespace can only read its own secrets.
Quick Reference
| Command | Description |
|---|---|
vault auth enable kubernetes |
Enable K8s auth |
vault write auth/kubernetes/config ... |
Configure K8s auth |
vault write auth/kubernetes/role/NAME ... |
Create a role |
vault list auth/kubernetes/role |
List roles |
vault read auth/kubernetes/role/NAME |
Read role details |
Next: Agent Injector Deep Dive