Press "Enter" to skip to content

Access Secrets from Azure Key Vault in Azure Kubernetes Service

Before we begin to discuss how to access secrets from Azure Key Vault in Azure Kubernetes Service, let us have a quick intro to Secrets in Kubernetes.

When you hear secrets, what comes to mind is confidentiality and secrecy. In the world of Kubernetes secrets are essentially any value that you don’t want the world to know about.

The following elements, password, an API key, a connection string to a database, all fall under what a secret is. Now when comparing Secrets and ConfigMaps in Kubernetes, the main difference is the confidential data.

Both ConfigMaps and Secrets store the data the same way, with key/value pairs, but ConfigMaps are designed for plain text data, and secrets on the other hand are meant for data that must be secured and confidential to the application exclusively.

By default, Secrets are stored at rest in Key Vault, in a secure encrypted store. Secrets are only stored in the AKS cluster when a pod is running with the secret mounted as a volume in a pod. As soon as the hosting pods are removed, the secret is removed from the cluster and this is a better approach as opposed to Kubernetes secrets which gets retained after the hosting pod is removed.

RESOURCE_GROUP=corp-infrastructure-rg
KV_RESOURCE_GROUP=corp-kv-infrastructure-rg
LOCATION=eastus
AKS_CLUSTER=corpakscluster

#Create a resource group for the AKS cluster:

az group create --name $RESOURCE_GROUP --location $LOCATION

az group create --name

 az aks create \
   --resource-group $RESOURCE_GROUP \
   --name $AKS_CLUSTER \
   --network-plugin azure \
   --enable-managed-identity \
   --enable-addons azure-keyvault-secrets-provider \
   --generate-ssh-keys

az aks create

 "identity": {
        "clientId": "1456c162-3f04-40bc-a079-f1f3f7d22b16",
        "objectId": "9f8165b6-206f-4596-932f-31e80469700f",
}

Download the cluster credentials and configure kubectl to use them:

az aks get-credentials --resource-group $RESOURCE_GROUP --name $AKS_CLUSTER
Merged "corpakscluster" as current context in /home/%user%/.kube/config

Check that the Secrets Store CSI Driver and the Azure Key Vault Provider are installed in the cluster:

$ kubectl get pods -n kube-system -l 'app in (secrets-store-csi-driver, secrets-store-provider-azure)'

kubectl get pods

When we enable the Azure Key Vault secret provider, the add-on will create a user assigned managed identity in the node managed resource group. Store its resource ID in a variable for later use

View the resource ID of the user assigned managed identity;

az aks show -g $RESOURCE_GROUP -n $AKS_CLUSTER --query addonProfiles.azureKeyvaultSecretsProvider.identity.clientId -o tsv
1456c162-3f04-40bc-a079-f1f3f7d22b16

azure key vault secrets provider managed identity

Store the resource ID of the user assigned managed identity in a variable;

KV_IDENTITY_RESOURCE_ID=$(az aks show -g $RESOURCE_GROUP -n $AKS_CLUSTER --query addonProfiles.azureKeyvaultSecretsProvider.identity.clientId -o tsv)

Create Azure Key Vault
Create a resource group for Azure Key vault

az group create --name $KV_RESOURCE_GROUP --location $LOCATION

Create a key vault while storing its name in a variable:

KEY_VAULT_NAME="akscorpkeyvault${RANDOM}"
az keyvault create --name $KEY_VAULT_NAME --resource-group $KV_RESOURCE_GROUP --location $LOCATION
{
 "name": "akscorpkeyvault5493"
"objectId": "ebejced9-2f89-8176-a9u3-657f75eb36bb"
"tenantId": "46edb775-xy69-41z6-7be1-03e4a0997e49"
}

Create a secret and a key in the Vault for later demonstration:

az keyvault secret set --vault-name $KEY_VAULT_NAME -n FirstSecret --value StoredValueinFirstSecret
 "name": "FirstSecret",
  "tags": {
    "file-encoding": "utf-8"
  },
  "value": "StoredValueinFirstSecret"
}

Create a key in the Vault for later demonstration:

az keyvault key create --vault-name $KEY_VAULT_NAME -n FirstKey --protection software
    "n": "t6PMnN5hTR2Oicy/fuTzQgXo49EgkS7B61gJWOeQjfw8u9tO+YoRbnPgWMnDsQWE3xE/MJyt6R0w0QwHsQa28KjdzCfq6qvJSlTSyhFfU9VJIf2YkjFtSlOpoyqYXKmHC6cS3pLrWsxDdVZTpZrgcZ8ec2deowrLDnn9mL5OKljGHmEaptocVHGWGfs9VNlxNqDAhRC4IKCQSIt6pnXc+eLo6Es0J50WhqHTGdqMG5brJGSlgEVaZobeBuvyFIxEvtt33MDjjkdiXCjKoTl8IS7/LNlvLYtDTWRvazK390IUXpldICw0xAp3layR/IDZA0diLEwQzbdESkyO18osPQ==",

Grant the AKS key vault managed identity permissions to read (GET) your key vault and view its contents:

Set policy to access keys in your key vault

az keyvault set-policy -n $KEY_VAULT_NAME --key-permissions get --spn $KV_IDENTITY_RESOURCE_ID
"objectId": "ebejced9-2f89-8176-a9u3-657f75eb36bb", granted the permissions to read the object     "objectId": "9f8165b6-206f-4596-932f-31e80469700f"
 "keys": [
            "get"
          ],

Set policy to access secrets in your key vault

az keyvault set-policy -n $KEY_VAULT_NAME --secret-permissions get --spn $KV_IDENTITY_RESOURCE_ID
"objectId": "ebejced9-2f89-8176-a9u3-657f75eb36bb", granted the permissions to read the object     "objectId": "9f8165b6-206f-4596-932f-31e80469700f"
"secrets": [
            "get"
          ]

Set policy to access certs in your key vault

az keyvault set-policy -n $KEY_VAULT_NAME --certificate-permissions get --spn $KV_IDENTITY_RESOURCE_ID
 "certificates": [
            "get"
          ],
Create Kubernetes resources
Store the tenant ID in a variable, you can get the value from the Azure AD tenant overview page:
TENANT_ID=${{put your tenant ID here}}  | TENANT_ID=46edb775-xy69-41z6-7be1-03e4a0997e49

Create a SecretProviderClass by using the following YAML, using your own values for userAssignedIdentityID, keyvaultName, tenantId, and the objects to retrieve from your key vault:


cat <<EOF | kubectl apply -f -
---
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: azure-kvname-user-msi
spec:
  provider: azure
  parameters:
    usePodIdentity: "false"
    useVMManagedIdentity: "true" # true since using managed identity
    userAssignedIdentityID: 1456c162-3f04-40bc-a079-f1f3f7d22b16 #$KV_IDENTITY_RESOURCE_ID
    keyvaultName: akscorpkeyvault5493    #$KEY_VAULT_NAME
    cloudName: ""
    objects:  |
      array:
        - |
          objectName: FirstSecret        #ExampleSecret
          objectType: secret    # object types: secret, key, or cert
          objectVersion: ""     # default to latest if empty
        - |
          objectName: FirstKey        #ExampleKey
          objectType: key
          objectVersion: ""
    tenantId: 46edb775-xy69-41z6-7be1-03e4a0997e49 #$TENANT_ID
EOF

secretproviderclass.secrets-store.csi.x-k8s.io/azure-kvname-user-msi configured

At this point, you need a pod that mounts the secret and the key using the secret provider class we just created earlier above:


cat <<EOF | kubectl apply -f -
---
kind: Pod
apiVersion: v1
metadata:
  name: busybox-secrets-store-inline-user-msi
spec:
  containers:
    - name: busybox
      image: k8s.gcr.io/e2e-test-images/busybox:1.29-1
      command:
        - "/bin/sleep"
        - "10000"
      volumeMounts:
      - name: secrets-store01-inline
        mountPath: "/mnt/secrets-store"
        readOnly: true
  volumes:
    - name: secrets-store01-inline
      csi:
        driver: secrets-store.csi.k8s.io
        readOnly: true
        volumeAttributes:
          secretProviderClass: "azure-kvname-user-msi"
EOF


pod/busybox-secrets-store-inline-user-msi created

Validate secrets were mounted from the pod created earlier:

kubectl exec busybox-secrets-store-inline-user-msi -- ls /mnt/secrets-store/

Read the content(s) of the secret and key:

kubectl exec busybox-secrets-store-inline-user-msi -- cat /mnt/secrets-store/FirstSecret
kubectl exec busybox-secrets-store-inline-user-msi -- cat /mnt/secrets-store/FirstKey