- DevOps Lifecycle -Newspaper WordPress Theme
AzureSecure Secret Management in AKS with Azure Key Vault CSI Driver and...

Secure Secret Management in AKS with Azure Key Vault CSI Driver and Auto-Rotation Enabled

In today’s cloud-native environments, securely managing secrets such as database credentials, API tokens, and certificates is vital. For Kubernetes workloads running on Azure Kubernetes Service (AKS), Azure Key Vault provides a highly secure and centralized way to manage secrets. When integrated with the Kubernetes Secrets Store CSI (Container Storage Interface) driver and Azure Workload Identity, secrets can be dynamically and securely injected into pods with automatic rotation support. This blog demonstrates how to set up this integration in a production-grade environment.


🌍 Use Case Scenario

Problem: In most Kubernetes applications, secrets are hardcoded or statically injected, requiring pod restarts when secrets like database passwords are rotated. This creates downtime and maintenance overhead.

Solution: Integrate AKS with Azure Key Vault using the CSI driver and enable automatic secret rotation. Secrets will be mounted into pods and updated dynamically without restarting the pod, ensuring zero-downtime secret updates.


⚙️ Architecture Overview

The architecture includes:

  • AKS Cluster
  • Azure Key Vault
  • Workload Identity (OIDC) for secure identity management
  • CSI Secrets Store Driver for mounting secrets
  • Auto-Rotation of secrets via polling
Secure Secrets Management in AKS with Azure Key Vault CSI Driver and Auto Rotation

📊 Step-by-Step Implementation

To create a AKS cluster using CLI please follow this blog: AKS Cluster Setup Using Azure CLI with OIDC & Azure Key Vault Integration

1. Enable OIDC and Workload Identity on exiting AKS cluster

az aks update \
  --name <cluster-name> \
  --resource-group <rg> \
  --enable-oidc-issuer \
  --enable-workload-identity
image 6

To enable Azure Key Vault CSI driver after the cluster is created:

az aks enable-addons \
  --addons azure-keyvault-secrets-provider \
  --name <cluster-name> \
  --resource-group <rg>
  

You can verify through azure portal under your kuberenets cluster dashboard “Security Configuration” tab

image 7

Verify that each node in your cluster’s node pool has a Secrets Store CSI Driver pod and a Secrets Store Provider Azure pod running

kubectl get pods -n kube-system -l 'app in (secrets-store-csi-driver,secrets-store-provider-azure)' -o wide
image 8 scaled

1.2 Keyvault creation and configuration

Create a key vault with Azure role-based access control (Azure RBAC).

az keyvault create -n my-demo-k8s-key-vault -g keyvault-demo -l eastus --enable-rbac-authorization

2. Create a Managed Identity

Please export following values on your terminal, make sure you have added your subscription id..etc

export SUBSCRIPTION_ID=fe4a1fdb-6a1c-4a6d-a6b0-dbb12f6a00f8
export RESOURCE_GROUP=keyvault-demo
export UAMI=azurekeyvaultsecretsprovider-keyvault-demo-cluster
export KEYVAULT_NAME=my-demo-k8s-key-vault
export CLUSTER_NAME=keyvault-demo-cluster

az account set --subscription $SUBSCRIPTION_ID

To Create a managed identity, following azure cli command

az identity create --name $UAMI --resource-group $RESOURCE_GROUP

export USER_ASSIGNED_CLIENT_ID="$(az identity show -g $RESOURCE_GROUP --name $UAMI --query 'clientId' -o tsv)"

export IDENTITY_TENANT=$(az aks show --name $CLUSTER_NAME --resource-group $RESOURCE_GROUP --query identity.tenantId -o tsv)

Create a role assignment that grants the workload ID access the key vault

export KEYVAULT_SCOPE=$(az keyvault show --name $KEYVAULT_NAME --query id -o tsv)

az role assignment create --role "Key Vault Administrator" --assignee $USER_ASSIGNED_CLIENT_ID --scope $KEYVAULT_SCOPE

Get the AKS cluster OIDC Issuer URL

export AKS_OIDC_ISSUER="$(az aks show --resource-group $RESOURCE_GROUP --name $CLUSTER_NAME --query "oidcIssuerProfile.issuerUrl" -o tsv)"

echo $AKS_OIDC_ISSUER

3. Annotate Kubernetes ServiceAccount

export SERVICE_ACCOUNT_NAME="workload-identity-sa"
export SERVICE_ACCOUNT_NAMESPACE="default" 
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
  name: ${SERVICE_ACCOUNT_NAME}
  namespace: ${SERVICE_ACCOUNT_NAMESPACE}
  annotations:
    azure.workload.identity/client-id: "${USER_ASSIGNED_CLIENT_ID}"
EOF

Or If above syntax confusing, SA direct yaml syntax given below fill details accordingly

apiVersion: v1
kind: ServiceAccount
metadata:
  name: workload-identity-sa
  annotations:
    azure.workload.identity/client-id: <your-client-id>
    namespace: default

Setup Federation

export FEDERATED_IDENTITY_NAME="aksfederatedidentity" 

az identity federated-credential create --name $FEDERATED_IDENTITY_NAME --identity-name $UAMI --resource-group $RESOURCE_GROUP --issuer ${AKS_OIDC_ISSUER} --subject system:serviceaccount:${SERVICE_ACCOUNT_NAMESPACE}:${SERVICE_ACCOUNT_NAME}

4. Create SecretProviderClass

cat <<EOF | kubectl apply -f -
# This is a SecretProviderClass example using workload identity to access your key vault
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: azure-kvname-wi # needs to be unique per namespace
spec:
  provider: azure
  parameters:
    usePodIdentity: "false"
    clientID: "${USER_ASSIGNED_CLIENT_ID}" # Setting this to use workload identity
    keyvaultName: ${KEYVAULT_NAME}       # Set to the name of your key vault
    cloudName: ""                         # [OPTIONAL for Azure] if not provided, the Azure environment defaults to AzurePublicCloud
    objects:  |
      array:
        - |
          objectName: DB-Password         # Set to the name of your secret
          objectType: secret              # object types: secret, key, or cert
          objectVersion: ""              
    tenantId: "${IDENTITY_TENANT}"        # The tenant ID of the key vault
EOF

Or use below direct yaml secretproviderclass and fill details accordingly

apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: azure-kvname-wi
spec:
  provider: azure
  parameters:
    keyvaultName: <your-kv-name>
    tenantId: <your-tenant-id>
    clientID: <your-client-id>
    objects: |
      array:
        - objectName: DB-Password
          objectType: secret
          objectVersion: ""

5. Deploy a Workload (e.g., BusyBox Test Pod)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: busybox-secrets
spec:
  replicas: 1
  selector:
    matchLabels:
      app: busybox-secrets
  template:
    metadata:
      labels:
        app: busybox-secrets
        azure.workload.identity/use: "true"
    spec:
      serviceAccountName: workload-identity-sa
      containers:
        - name: busybox
          image: registry.k8s.io/e2e-test-images/busybox:1.29-4
          command: ["/bin/sleep", "10000"]
          volumeMounts:
            - name: secrets-store-vol
              mountPath: "/mnt/secrets-store"
              readOnly: true
      volumes:
        - name: secrets-store-vol
          csi:
            driver: secrets-store.csi.k8s.io
            readOnly: true
            volumeAttributes:
              secretProviderClass: azure-kvname-wi
              rotationPollInterval: "30s"

Now you can exec into the pod and verify the secret are mounted into the container. You can run following command to verify the same

image 9
kubectl exec <pod> -- ls /mnt/secrets-store/
kubectl exec <pod> -- cat /mnt/secrets-store/DB-Password

⟳ Enable Auto-Rotation of Secrets

Run the following command to enable the rotation feature:

az aks addon update \
  --resource-group <rg> \
  --name <cluster-name> \
  --addon azure-keyvault-secrets-provider \
  --enable-secret-rotation \
  --rotation-poll-interval 30s

Verify it with:

kubectl -n kube-system describe ds aks-secrets-store-csi-driver

Ensure the arguments include:

  • --enable-secret-rotation=true
  • --rotation-poll-interval=30s

To work Azure Key Vault auto rotation in deployment, Make sure following settings are there in first

1) Under SecretProviderClass Leave objectVersion blank

2) Add to volume attributes on deployment file: rotationPollInterval

volumeAttributes:
  secretProviderClass: azure-kvname-wi
  rotationPollInterval: "30s"

🔍 Testing the Rotation

  1. Manually update the secret version in Azure Key Vault.
  2. Wait for rotationPollInterval.
  3. Check the mounted file inside the pod:
kubectl exec <pod> -- ls /mnt/secrets-store/
kubectl exec <pod> -- cat /mnt/secrets-store/DB-Password
image 4 scaled
  1. Observe that the new value is reflected without restarting the pod.
image 5 scaled

📗 Best Practices

  • Avoid subPath in volume mounts (breaks rotation).
  • Ensure your application reads secrets from file, not env vars.
  • Consider implementing file watchers for dynamic config reloads.
  • Monitor CSI driver logs: kubectl logs ds/aks-secrets-store-csi-driver -n kube-system

🌟 Conclusion

With Azure Key Vault, CSI driver, and workload identity, you can achieve secure, automated, and zero-downtime secret management for your AKS workloads. Auto-rotation ensures credentials like DB passwords can be updated without disrupting live applications.

Feel free to follow me on LinkedIn and share your thoughts. For a complete YAML reference or demo repo, connect with me or leave a comment!

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Subscribe Today

GET EXCLUSIVE FULL ACCESS TO PREMIUM CONTENT

SUPPORT NONPROFIT JOURNALISM

EXPERT ANALYSIS OF AND EMERGING TRENDS IN CHILD WELFARE AND JUVENILE JUSTICE

TOPICAL VIDEO WEBINARS

Get unlimited access to our EXCLUSIVE Content and our archive of subscriber stories.

Exclusive content

- Advertisement -Newspaper WordPress Theme

Latest article

More article