Integrating Azure Key Vault with AKS using Terraform
Introduction
Managing and using secretes, certificates is something very important part of any kind of project.
Default Kubernetes secrete management will not be sufficient for securing secretes; they are just base64 encoded strings which can be easily decoded anyone. true security is, using Azure Key vault for storing secretes.
By using the Azure Key Vault Provider for Secrets Store CSI Driver
, you can securely store secrets in Azure Key Vault and use them in your applications without having to manage the secrets directly in your Kubernetes cluster.
The Azure Key Vault Provider for Secrets Store CSI Driver
is a Container Storage Interface (CSI) driver that allows you to use Azure Key Vault as a secrets store for your Kubernetes cluster. It provides a way for your pods to securely access secrets stored in Key Vault and use them in your applications. The driver acts as a bridge between your Kubernetes cluster and Azure Key Vault.
Technical Scenario
As a cloud engineer
you've been asked to integrate Azure Key Vault service with Azure Kubernetes Service(AKS)
so that all your application configuration is fully secured. we are going to use Azure Key Vault Provider for Secrets Store CSI Driver for achiving the goal.
Prerequisites
Before start the implementation, ensure you have the following prerequisites:
- Azure subscription
- Terraform installed and configured
- Azure CLI, Kubectl, Helm tools installed
- AKS cluster
- Azure Key Vault
Objective
In this exercise, we will cover the following steps to integrating Azure key vault with AKS using terraform:
-
Step-1: Install the Secrets Store CSI Driver and the Azure Keyvault Provider
-
Step 2: Craete access policy in Azure Key Vault for AKS cluster
-
Step 3: Create Secret Provider Class
-
Step 4: Update and Apply Deployment YAML File
-
Step 5: Create Secret in Azure Key Vault
-
Step 6: Validate the Secrets Store Deployment
Architecture Diagram
This digram explains components used in this lab like CSI driver, Keyvault Provider & AKS VMSS access in Key vault.
login to Azure
Before you start this lab, ensure you are logged into the correct Azure subscription using Visual Studio Code:
# Login to Azure
az login
# Shows current Azure subscription
az account show --output table
# Lists all available Azure subscriptions
az account list --output table
# Sets Azure subscription to desired subscription using ID
az account set -s "anji.keesari"
Connect to Cluster
To interact with your Azure Kubernetes Service (AKS) cluster, you need to establish a connection. Depending on your role, you can use either the User or Admin credentials:
# Azure Kubernetes Service Cluster User Role
az aks get-credentials -g "rg-aks-dev" -n "aks-cluster1-dev"
# Azure Kubernetes Service Cluster Admin Role
az aks get-credentials -g "rg-aks-dev" -n "aks-cluster1-dev" --admin
# verify the aks connection by running following commands
kubectl get no
kubectl get namespace -A
Implementation Details
This guide will take you through the steps to configure and run the Azure Key Vault Provider for Secrets Store CSI driver on Kubernetes for Integrating Azure Key Vault with AKS cluster.
Utilize Terraform to deploy the Secrets Store CSI Driver Helm chart onto AKS.
Step-1: Install the Secrets Store CSI Driver and the Azure Keyvault Provider
To install Helm charts using Terraform, you'll need to add the necessary Terraform providers. Here's an example of how to include them in your provider.tf
file:
Let's add following terraform providers in provider.tf
file
provider "kubernetes" {
host = azurerm_kubernetes_cluster.aks.kube_admin_config.0.host
client_certificate = base64decode(azurerm_kubernetes_cluster.aks.kube_admin_config.0.client_certificate)
client_key = base64decode(azurerm_kubernetes_cluster.aks.kube_admin_config.0.client_key)
cluster_ca_certificate = base64decode(azurerm_kubernetes_cluster.aks.kube_admin_config.0.cluster_ca_certificate)
#load_config_file = false
}
provider "helm" {
debug = true
kubernetes {
host = azurerm_kubernetes_cluster.aks.kube_admin_config.0.host
client_certificate = base64decode(azurerm_kubernetes_cluster.aks.kube_admin_config.0.client_certificate)
client_key = base64decode(azurerm_kubernetes_cluster.aks.kube_admin_config.0.client_key)
cluster_ca_certificate = base64decode(azurerm_kubernetes_cluster.aks.kube_admin_config.0.cluster_ca_certificate)
}
}
provider "kubectl" {
host = azurerm_kubernetes_cluster.aks.kube_admin_config.0.host
client_certificate = base64decode(azurerm_kubernetes_cluster.aks.kube_admin_config.0.client_certificate)
client_key = base64decode(azurerm_kubernetes_cluster.aks.kube_admin_config.0.client_key)
cluster_ca_certificate = base64decode(azurerm_kubernetes_cluster.aks.kube_admin_config.0.cluster_ca_certificate)
load_config_file = false
}
Since we've added new providers in terraform project we need to run terraform init once again;
Terraform init
Install Helm Chart using terraform
Deploy the CSI Driver:
You need to deploy the Azure Key Vault Provider for Secrets Store CSI Driver in your Kubernetes cluster. Here we are going to use Helm chart to deploy the driver.
Running the this helm chart will install both the Secrets Store CSI Driver and Azure Key Vault provider.
locals {
csi_driver_settings = {
linux = {
enabled = true
resources = {
requests = {
cpu = "100m"
memory = "100Mi"
}
limits = {
cpu = "100m"
memory = "100Mi"
}
}
}
secrets-store-csi-driver = {
install = true
linux = {
enabled = true
}
logLevel = {
debug = true
}
syncSecret = {
enabled = true
}
}
}
}
resource "helm_release" "csi_driver" {
name = "csi-secrets-store-provider-azure"
chart = "csi-secrets-store-provider-azure"
# version = "0.0.8"
repository = "https://azure.github.io/secrets-store-csi-driver-provider-azure/charts"
namespace = "kube-system"
max_history = 4
atomic = true
reuse_values = false
timeout = 1800
values = [yamlencode(local.csi_driver_settings)]
depends_on = [
azurerm_kubernetes_cluster.aks
]
}
Validation
Verify the Secrets Store CSI Driver and Azure Key Vault provider are successfully installed
To verify the Secrets Store CSI Driver
is installed, run this command:
kubectl get pods -l app=secrets-store-csi-driver -n kube-system
# You should see the driver pods running on each agent node:
NAME READY STATUS RESTARTS AGE
secrets-store-csi-driver-spbfq 3/3 Running 0 3h52m
To verify the Azure Key Vault provider
is installed, run this command:
kubectl get pods -l app=csi-secrets-store-provider-azure -n kube-system
# You should see the provider pods running on each agent node:
NAME READY STATUS RESTARTS AGE
csi-secrets-store-provider-azure-tpb4j 1/1 Running 0 3h52m
Note
I recently observed that the latest version of AKS clusters now includes these pods by default. Therefore, if you have created an AKS cluster with the latest Kubernetes version, you can safely skip step-1.
Step-2: Craete access policy in Azure Key Vault for AKS cluster
Granting the necessary permissions for the Kubernetes cluster to access secrets in the Key Vault is a crucial step in securing your applications. There are multiple approaches to achieve this, such as using Helm charts, Azure Command-Line Interface (az) commands, or Azure Active Directory (AAD) pod identity. In this lab, we'll focus on granting access to the Azure Kubernetes Service (AKS) Virtual Machine Scale Set (VMSS) identity, when I tried with AAD pod identity I've encountered couple of issues therefore I am selecting VMSS identity here.
Manual Steps: Enable Managed Service Identity (MSI) for the AKS VMSS.
-
Navigate to the AKS MC_ resource group in the Azure portal, typically named in the format
MC_rg-rgname-dev_aks-aksname-dev_region
. -
Select the AKS Virtual Machine Scale Set (VMSS) - often named something like
aks-agentpool-12345678-vmss
. -
In the left navigation pane, click on "Identity," and set the Status to "On" in the "System Assigned" tab. This action creates a new MSI. Copy the generated ID; you'll need it later.
Update the Terraform script with the AKS VMSS identity.
Once the AKS VMSS identity is enabled, proceed to update the Terraform script to incorporate this identity. Below are the steps:
- In the Terraform script, create a new file (e.g.,
role_assignment.tf
) and add the following code:
resource "azurerm_key_vault_access_policy" "aks_vmss_access_policy" {
key_vault_id = azurerm_key_vault.kv.id
tenant_id = data.azurerm_subscription.current.tenant_id
object_id = var.aks_vmss_identity // Replace with the Object (principal) ID of the VMSS
secret_permissions = [
"Get",
]
depends_on = [
azurerm_key_vault.kv
]
}
- Declare a variable for AKS VMSS identity in your Terraform script:
variable "aks_vmss_identity" {
description = "Manually enable the AKS VMSS MSI before running this."
type = string
}
- Copy the Object (principal) ID of the VMSS generated during the manual step and update the variable in the Terraform script:
Validate Key Vault Access Policy
The Terraform code above creates a new Key Vault access policy, granting the specified permissions to the AKS VMSS identity. Note that the object_id
should be dynamically retrieved; however, the solution to achieve dynamic reading is pending.
Ensure to automate the manual step in the future for a fully streamlined process.
Step-3: Create Secret Provider Class
To effectively utilize and configure the Secrets Store CSI Driver for your Azure Kubernetes Service (AKS) cluster, you'll need to create a SecretProviderClass
custom resource. This is an essential step as it defines how the Secrets Store CSI Driver interacts with your Azure Key Vault and synchronizes secrets to your Kubernetes cluster.
The creation of the SecretProviderClass
can be seamlessly integrated into your Helm charts or YAML manifests. Below is an example YAML file (secretproviderclass.yaml) illustrating a SecretProviderClass setup using a system-assigned identity to access the Azure Key Vault:
# This is a SecretProviderClass example using system-assigned identity to access with key vault
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: keyvault-secret-provider
namespace: sample
spec:
provider: azure
secretObjects: # [OPTIONAL] SecretObjects defines the desired state of synced Kubernetes secret objects
- secretName: secret-provider-dev # name of the Kubernetes secret object
type: Opaque # type of Kubernetes secret object (for example, Opaque, kubernetes.io/tls)
data:
- objectName: sample-secret # name of the mounted content to sync; this could be the object name or the object alias
key: sample-secret # data field to populate
- objectName: connectionstring-dbname # name of the mounted content to sync; this could be the object name or the object alias
key: connectionstring-dbname # data field to populate
parameters:
usePodIdentity: "false"
useVMManagedIdentity: "true" # Set to true for using managed identity
userAssignedIdentityID: "" # If empty, then defaults to use the system assigned identity on the VM
keyvaultName: kv-kvname-dev # azure key vault service name
cloudName: "AzurePublicCloud" # [OPTIONAL for Azure] if not provided, the Azure environment defaults to AzurePublicCloud
objects: |
array:
- |
objectName: sample-secret
objectType: secret # object types: secret, key, or cert
objectVersion: "" # [OPTIONAL] object versions, default to latest if empty
- |
objectName: connectionstring-dbname
objectType: secret # object types: secret, key, or cert
objectVersion: "" # [OPTIONAL] object versions, default to latest if empty
tenantId: ee93c4a0-f87d-46ad-b4be-3ee05cefecws # The tenant ID of the key vault
Explanation:
- provider: Specifies the provider, in this case, "azure."
- secretObjects: Defines the desired state of synced Kubernetes secret objects.
- parameters: Configuration parameters for the Secrets Store CSI Driver.
- objects: Specifies the array of objects to sync from Azure Key Vault to the Kubernetes cluster.
kubectl apply
Execute the following kubectl apply command to apply the configuration:
kubectl apply -f secretproviderclass.yaml
# output
# secretproviderclass.secrets-store.csi.x-k8s.io/azure-kvname-system-msi created
This confirms that the SecretProviderClass
named keyvault-secret-provider
has been successfully created in the sample
namespace.
For more details, you can refer to the official Microsoft documentation
Step-4: Update and apply deployment YAML file
To seamlessly integrate the Secrets Store CSI Driver into your Azure Kubernetes Service (AKS) cluster, you must update your deployment YAML file to reference the newly created SecretProviderClass
. This ensures that your applications can securely access and utilize the secrets stored in Azure Key Vault.
apiVersion: apps/v1
kind: Deployment
metadata:
name: aspnet-api
namespace: tenant1
spec:
replicas: 1
selector:
matchLabels:
app: aspnet-api
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
minReadySeconds: 5
template:
metadata:
labels:
app: aspnet-api
spec:
nodeSelector:
"kubernetes.io/os": linux
serviceAccountName: default
containers:
- name: aspnet-api-image
image: acrname.azurecr.io/aspnet-api:20221006.2
imagePullPolicy: Always
ports:
- name: http
containerPort: 80
protocol: TCP
volumeMounts:
- name: secrets-store01-inline
mountPath: "/mnt/secrets-store"
readOnly: true
env:
- name: ASPNETCORE_ENVIRONMENT
value: "Development"
- name: ConnectionStrings__CoreDB
valueFrom:
secretKeyRef:
name: secret-provider-dev
key: connectionstring-coredb
volumes:
- name: secrets-store01-inline
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: "keyvault-secret-provider"
Explanation: - volumeMounts: Mounts the secrets store volume to the specified path in your container. - volumes: Specifies the volume configuration, including the CSI driver and the associated SecretProviderClass.
Then, apply the updated deployment YAML file to the cluster:
Once you've updated your deployment YAML file, apply the changes to the cluster using the following kubectl apply command:
This action ensures that your deployment now incorporates the Secrets Store CSI Driver, and your application pods will securely access the secrets from Azure Key Vault.
Step-5: Create Secret in Azure Key Vault
Azure Key Vault can store keys, secrets, and certificates. In the following example, a plain-text secret called ExampleSecret is configured.
Step-6: Validate the Secrets Store deployment
To show the secrets that are held in secrets-store, run the following command:
kubectl exec busybox-secrets-store-inline --namespace kube-system -- ls /mnt/secrets-store/
# output
ExampleSecret
To show the test secret held in secrets-store, run the following command:
kubectl exec busybox-secrets-store-inline --namespace kube-system -- cat /mnt/secrets-store/ExampleSecret
#output
MyAKSHCIExampleSecret
References
Here is a comprehensive list of resources that were used in the development of this technical guide:
-
Use Azure Key Vault Provider for Kubernetes Secrets Store CSI Driver with AKS hybrid - Microsoft Azure's official documentation on utilizing the Azure Key Vault Provider for Kubernetes Secrets Store CSI Driver with AKS.
-
MSDN Documentation - Refer to the MSDN documentation for detailed insights into how secrets are pulled from the Key Vault service.
-
secrets-store-csi-driver-provider-azure GitHub Repo - Explore the GitHub repository for the Azure provider of the Secrets Store CSI Driver.
-
Azure Key Vault Provider for Secrets Store CSI Driver Documentation - The official documentation for the Azure Key Vault Provider for Secrets Store CSI Driver.
-
Helm Chart Installation details - Get detailed information on installing Helm Charts for the Secrets Store CSI Driver.
Troubleshooting Resources
-
Troubleshooting: Secret is not creating in AKS after fetching it with CSI driver - Stack Overflow discussion on troubleshooting issues with secret creation in AKS.
-
AKS with Azure Key Vault: Env variables don't load - Server Fault thread addressing environment variable loading issues with AKS and Azure Key Vault.
-
Azure Key Vault integration with AKS - Not clear what to do if not working - Stack Overflow discussion on integration issues with AKS and Key Vault.
-
GitHub Issue: Azure Key Vault integration with AKS - GitHub issue addressing problems related to Azure Key Vault integration.
-
AKS and Azure Key Vault Integration - Stack Overflow discussion on integrating Azure Key Vault with AKS.
-
Reloading an env variable synced to a mounted Azure Key Vault secret - A guide on reloading environment variables synced with Azure Key Vault secrets.
-
Known Limitations of Secrets Store CSI Driver - Understand the known limitations of the Secrets Store CSI Driver.
-
Enable and Disable Autorotation - Learn about enabling and disabling autorotation with the CSI Secrets Store Driver.
Feel free to explore these resources for a more in-depth understanding and troubleshooting of the Secrets Store CSI Driver integration with Azure Kubernetes Service.