HashiCorp Vault — Kubernetes Auth

2 minute read

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