Back to Distributed Systems & Kubernetes Series

Vault Track Part 2: Kubernetes Auth Method

June 6, 2026 Wasil Zafar 35 min read

The Kubernetes auth method is Vault's most powerful feature for Kubernetes deployments. A Pod presents its service account JWT to Vault. Vault verifies it with the Kubernetes API Server. Vault returns a short-lived token scoped to a policy. No static credentials anywhere in the Pod spec or environment.

Table of Contents

  1. How It Works
  2. Configure Kubernetes Auth
  3. Creating Roles
  4. Testing Authentication
  5. ServiceAccount Setup
  6. Exercises
  7. Key Takeaways & Next Steps

How It Works

The Kubernetes auth flow:

  1. Kubernetes mounts a service account JWT at /var/run/secrets/kubernetes.io/serviceaccount/token in every Pod.
  2. The Pod (or Vault Agent sidecar) sends this JWT to Vault's /v1/auth/kubernetes/login endpoint along with the role name.
  3. Vault calls the Kubernetes API to validate the JWT signature and check the token hasn't been revoked.
  4. Vault matches the service account name and namespace against a configured role.
  5. Vault returns a short-lived Vault token scoped to the policies attached to that role.
  6. The Pod uses the Vault token to read secrets. The token expires; the Pod re-authenticates as needed.
Zero credential bootstrap: The service account JWT is automatically injected by Kubernetes — no manual secret distribution. This is the core advantage of Kubernetes auth over alternatives like AppRole.

Configure Kubernetes Auth

Enable the Auth Method

export VAULT_ADDR=http://127.0.0.1:8200    # Or via port-forward
export VAULT_TOKEN=

# Enable the Kubernetes auth method
vault auth enable kubernetes

# If Vault is running inside Kubernetes, it can detect the K8s API automatically.
# If Vault is outside (e.g., cloud-hosted HCP Vault), provide the K8s API URL.

Configure the Backend

# Option 1: Vault is running INSIDE Kubernetes
# Vault can use its own pod's service account to talk to the K8s API.
# No manual token_reviewer_jwt needed (Kubernetes 1.21+ with bound SA tokens).

vault write auth/kubernetes/config \
  kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443"

# Verify
vault read auth/kubernetes/config
# Option 2: Vault is OUTSIDE Kubernetes (e.g., HCP Vault, separate VM)
# Create a dedicated service account for Vault to call the K8s API:

kubectl create serviceaccount vault-auth -n vault

# In Kubernetes 1.24+, service account tokens are not auto-created.
# Create a long-lived token explicitly:
kubectl apply -f - <

Creating Roles

Binding to Namespace + ServiceAccount

# First, write the policy for grade-api
vault policy write grade-api - <
# Read back the role to verify
vault read auth/kubernetes/role/grade-api
# Key                                 Value
# ---                                 -----
# bound_service_account_names         [grade-api-sa]
# bound_service_account_namespaces    [grade-api]
# policies                            [grade-api]
# ttl                                 1h
# token_max_ttl                       0s

Multi-Namespace Roles

# Allow multiple service accounts across multiple namespaces
vault write auth/kubernetes/role/shared-reader \
  bound_service_account_names="grade-api-sa,analytics-sa" \
  bound_service_account_namespaces="grade-api,analytics" \
  policies="shared-secrets-reader" \
  ttl=1h

# Wildcard — allow any service account in a namespace (use carefully)
vault write auth/kubernetes/role/namespace-wide \
  bound_service_account_names="*" \
  bound_service_account_namespaces=grade-api \
  policies=grade-api \
  ttl=1h

# Wildcard — allow same SA name in any namespace (fine for sidecar use cases)
vault write auth/kubernetes/role/any-ns \
  bound_service_account_names=vault-reader \
  bound_service_account_namespaces="*" \
  policies=readonly \
  ttl=1h

Testing Authentication

# Run a test Pod in the grade-api namespace with the correct service account
kubectl run vault-test \
  --image=hashicorp/vault:latest \
  --serviceaccount=grade-api-sa \
  --namespace=grade-api \
  --rm -it \
  --restart=Never \
  -- sh

# Inside the Pod:
# The JWT is automatically mounted here:
JWT=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)

# Authenticate with Vault
curl -s \
  --request POST \
  --data "{\"jwt\": \"$JWT\", \"role\": \"grade-api\"}" \
  http://vault.vault.svc.cluster.local:8200/v1/auth/kubernetes/login | jq .

# Response includes the Vault token:
# {
#   "auth": {
#     "client_token": "hvs.CAESIB...",
#     "accessor": "...",
#     "policies": ["default", "grade-api"],
#     "ttl": 3600,
#     ...
#   }
# }

# Use the token to read a secret
VAULT_TOKEN=hvs.CAESIB...
curl -s \
  --header "X-Vault-Token: $VAULT_TOKEN" \
  http://vault.vault.svc.cluster.local:8200/v1/secret/data/grade-api/db | jq .data.data

ServiceAccount Setup

# Create the ServiceAccount for grade-api
apiVersion: v1
kind: ServiceAccount
metadata:
  name: grade-api-sa
  namespace: grade-api
  annotations:
    # For EKS with IRSA — Vault can also verify AWS IAM identity
    # eks.amazonaws.com/role-arn: arn:aws:iam::123456789:role/grade-api

---
# Use the ServiceAccount in the Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: grade-api
  namespace: grade-api
spec:
  template:
    spec:
      serviceAccountName: grade-api-sa    # Use the dedicated SA
      automountServiceAccountToken: true  # Must be true (default)
      containers:
        - name: grade-api
          image: ghcr.io/your-org/grade-api:latest
          # Part 3: Vault Agent sidecar will inject secrets as files or env vars

Exercises

Exercise 1 — Configure Kubernetes Auth: Enable the Kubernetes auth method on your Vault instance. Configure it using Vault's own service account (Vault running inside the cluster). Create a role bound to a test service account in the default namespace.
Exercise 2 — Test Authentication: Create a ServiceAccount in the default namespace. Run a curl-based Pod with that service account. Use the mounted JWT to authenticate to Vault and read a test secret. Verify the token TTL is 1 hour.
Exercise 3 — Least Privilege: Create two roles: grade-api bound to namespace grade-api with read-only access to secret/grade-api/*, and payment-api bound to namespace payment with read-only access to secret/payment/*. Verify cross-namespace access is denied.

Key Takeaways & Next Steps

Key Takeaways:
  • Kubernetes auth uses the Pod's auto-mounted service account JWT — no manual credential distribution
  • Vault verifies JWTs with the Kubernetes API server — the trust anchor is the cluster's OIDC/SA signing key
  • Roles bind a Vault policy to specific Kubernetes service accounts and namespaces
  • Always use bound_service_account_namespaces — don't wildcard namespaces in production unless you understand the blast radius
  • Short TTLs (1h or less) limit the damage if a Vault token is leaked
  • Each microservice should have its own ServiceAccount and its own Vault role — principle of least privilege

Next in This Track

In Part 3: Secret Injection, we use the Vault Agent Injector and the Vault Secrets Operator to inject secrets directly into Pods as environment variables or mounted files — with automatic renewal and no application code changes.