This guide helps diagnose and resolve Vault authentication issues, particularly “permission denied” errors when the operator tries to authenticate with Vault.
If you’re getting “permission denied” errors, start here:
# Check operator logs for specific error details
kubectl logs -n vault-sync-operator-system -l control-plane=controller-manager --tail=50 | grep -i "auth\|permission\|denied"
Error: permission denied during initial login
Symptoms: Operator cannot authenticate with Vault at all
Error: permission denied during secret operations
Symptoms: Operator authenticates successfully but cannot read/write secrets
# Test if operator can reach Vault
kubectl exec -n vault-sync-operator-system deployment/vault-sync-operator-controller-manager -- \
curl -s $VAULT_ADDR/v1/sys/health
# Expected output should show Vault status
# Verify auth backend is enabled
vault auth list | grep kubernetes
# Check auth backend configuration
vault read auth/kubernetes/config
# Verify the configuration shows:
# - kubernetes_host: Should match your cluster API server
# - kubernetes_ca_cert: Should be present
# - token_reviewer_jwt: Should be configured
Common Issues:
kubernetes_ca_certkubernetes_host URLtoken_reviewer_jwt# Check if the service account exists
kubectl get serviceaccount vault-sync-operator-controller-manager -n vault-sync-operator-system
# Verify the service account has a token
kubectl get secret -n vault-sync-operator-system | grep vault-sync-operator-controller-manager
# Check token accessibility
kubectl exec -n vault-sync-operator-system deployment/vault-sync-operator-controller-manager -- \
ls -la /var/run/secrets/kubernetes.io/serviceaccount/
# Extract the service account token
SA_TOKEN=$(kubectl exec -n vault-sync-operator-system deployment/vault-sync-operator-controller-manager -- \
cat /var/run/secrets/kubernetes.io/serviceaccount/token)
# Test authentication manually
vault write auth/kubernetes/login role=vault-sync-operator jwt="$SA_TOKEN"
Expected Output:
Key Value
--- -----
token s.xxxxxxxxxxxxxxxxxxxxx
token_accessor xxxxxxxxxxxxxxxxxxxxx
token_duration 24h
token_renewable true
token_policies ["default" "vault-sync-operator"]
If this fails, check:
vault-sync-operator# Check the role configuration
vault read auth/kubernetes/role/vault-sync-operator
Expected Output:
Key Value
--- -----
bound_service_account_names [vault-sync-operator-controller-manager]
bound_service_account_namespaces [vault-sync-operator-system]
policies [vault-sync-operator]
ttl 24h
Common Issues:
bound_service_account_names doesn’t match actual service account namebound_service_account_namespaces doesn’t match deployment namespace# Check if policy exists
vault policy read vault-sync-operator
# Test policy capabilities for common paths
vault policy test vault-sync-operator secret/data/test-path
Expected Policy for KV v2:
path "secret/data/*" {
capabilities = ["read"]
}
path "secret/metadata/*" {
capabilities = ["read"]
}
Expected Policy for KV v1:
path "secret/*" {
capabilities = ["read"]
}
# First, authenticate and get a token
VAULT_TOKEN=$(vault write -field=token auth/kubernetes/login role=vault-sync-operator jwt="$SA_TOKEN")
# Test reading a secret (adjust path based on your KV engine version)
VAULT_TOKEN=$VAULT_TOKEN vault kv get secret/test-app
# Or for KV v1
VAULT_TOKEN=$VAULT_TOKEN vault read secret/test-app
Problem: Role has wrong service account name
# Check actual service account name
kubectl get deployment vault-sync-operator-controller-manager -n vault-sync-operator-system -o jsonpath='{.spec.template.spec.serviceAccountName}'
# Update role if needed
vault write auth/kubernetes/role/vault-sync-operator \
bound_service_account_names=vault-sync-operator-controller-manager \
bound_service_account_namespaces=vault-sync-operator-system \
policies=vault-sync-operator \
ttl=24h
Problem: Role bound to wrong namespace
# Check actual namespace
kubectl get deployment vault-sync-operator-controller-manager -o jsonpath='{.metadata.namespace}'
# Update role if needed
vault write auth/kubernetes/role/vault-sync-operator \
bound_service_account_names=vault-sync-operator-controller-manager \
bound_service_account_namespaces=vault-sync-operator-system \
policies=vault-sync-operator \
ttl=24h
Problem: Policy paths don’t match KV engine version
# Check KV engine version
vault secrets list -detailed | grep secret/
# For KV v2, use these paths in policy:
path "secret/data/*" {
capabilities = ["read"]
}
path "secret/metadata/*" {
capabilities = ["read"]
}
# For KV v1, use these paths in policy:
path "secret/*" {
capabilities = ["read"]
}
Problem: Auth backend configured with wrong Kubernetes API server
# Check current configuration
vault read auth/kubernetes/config
# Collect cluster and service details
KUBE_CA_CERT=$(kubectl config view --raw --minify --flatten -o jsonpath='{.clusters[].cluster.certificate-authority-data}' | base64 --decode)
KUBE_HOST=$(kubectl config view --raw --minify --flatten --output='jsonpath={.clusters[].cluster.server}')
TOKEN_REVIEW_JWT=$(kubectl get secret vault-sync-operator-controller-manager-token -n vault-sync-operator-system -o go-template='' | base64 --decode)
# Update configuration
vault write auth/kubernetes/config \
token_reviewer_jwt="$TOKEN_REVIEW_JWT" \
kubernetes_host="$KUBE_HOST" \
kubernetes_ca_cert="$KUBE_CA_CERT" \
disable_local_ca_jwt="true"
# Check operator logs
kubectl logs -n vault-sync-operator-system -l control-plane=controller-manager -f
# Check pod status
kubectl get pods -n vault-sync-operator-system
# Check service account details
kubectl describe serviceaccount vault-sync-operator-controller-manager -n vault-sync-operator-system
# Test connectivity from operator pod
kubectl exec -n vault-sync-operator-system deployment/vault-sync-operator-controller-manager -- \
curl -s $VAULT_ADDR/v1/sys/health
# Check environment variables
kubectl exec -n vault-sync-operator-system deployment/vault-sync-operator-controller-manager -- env | grep VAULT
# Check Vault status
vault status
# List auth methods
vault auth list
# Check audit logs (if enabled)
vault audit list
# Test token capabilities
vault token capabilities vault-sync-operator secret/data/test-path
# Check secret engines
vault secrets list
# Test manual KV operations
vault kv put secret/test-app username=test password=test
vault kv get secret/test-app
# Check RBAC permissions
kubectl auth can-i get serviceaccount \
--as=system:serviceaccount:vault-sync-operator-system:vault-sync-operator-controller-manager
# Check service account token
kubectl get secret -n vault-sync-operator-system | grep vault-sync-operator-controller-manager
# Verify deployment environment
kubectl describe deployment vault-sync-operator-controller-manager -n vault-sync-operator-system
For k3s clusters, additional considerations:
# k3s might have different token paths
kubectl exec -n vault-sync-operator-system deployment/vault-sync-operator-controller-manager -- \
find /var/run/secrets -name "token" -type f
# Get k3s API server address
kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}'
# Verify from within cluster
kubectl exec -n vault-sync-operator-system deployment/vault-sync-operator-controller-manager -- \
printenv | grep KUBERNETES
# Check CA cert accessibility
kubectl exec -n vault-sync-operator-system deployment/vault-sync-operator-controller-manager -- \
ls -la /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
Here’s a complete, tested sequence for setting up Vault Kubernetes auth:
# 1. Enable Kubernetes auth (from within a pod that has access to service account)
vault auth enable kubernetes
# 2. Configure the auth backen
KUBE_CA_CERT=$(kubectl config view --raw --minify --flatten -o jsonpath='{.clusters[].cluster.certificate-authority-data}' | base64 --decode)
KUBE_HOST=$(kubectl config view --raw --minify --flatten --output='jsonpath={.clusters[].cluster.server}')
TOKEN_REVIEW_JWT=$(kubectl get secret vault-sync-operator-controller-manager-token -n vault-sync-operator-system -o go-template='' | base64 --decode)
vault write auth/kubernetes/config \
token_reviewer_jwt="$TOKEN_REVIEW_JWT" \
kubernetes_host="$KUBE_HOST" \
kubernetes_ca_cert="$KUBE_CA_CERT" \
disable_local_ca_jwt="true"
# 3. Create policy
vault policy write vault-sync-operator - <<EOF
# 3. Create policy
path "secret/data/*" {
capabilities = ["create", "update", "delete", "read"]
}
path "secret/metadata/*" {
capabilities = ["list", "read"]
}
path "auth/token/renew-self" {
capabilities = ["update"]
}
path "auth/token/lookup-self" {
capabilities = ["read"]
}
EOF
# 4. Create role
vault write auth/kubernetes/role/vault-sync-operator \
bound_service_account_names=vault-sync-operator-controller-manager \
bound_service_account_namespaces=vault-sync-operator-system \
policies=vault-sync-operator \
ttl=24h
# 5. Verify configuration
vault read auth/kubernetes/role/vault-sync-operator
vault policy read vault-sync-operator
# 6. Test authentication
kubectl exec -n vault-sync-operator-system deployment/vault-sync-operator-controller-manager -- \
vault write auth/kubernetes/login role=vault-sync-operator \
jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"
If you’re still experiencing problems after following this guide:
Create an issue with the following information:
vault read auth/kubernetes/configvault read auth/kubernetes/role/vault-sync-operatorvault policy read vault-sync-operator