Building an AKS baseline architecture - Part 4 - AAD Pod Identity
Posts from this series:
Building an AKS baseline architecture - Part 1 - Cluster creation
Building an AKS baseline architecture - Part 2 - Governance
Building an AKS baseline architecture - Part 3 - GitOps with Flux2
Building an AKS baseline architecture - Part 4 - AAD Pod Identity
Overview
When we develop solutions, they usually include multiple Azure services that need to communicate between them. Moving the compute part of the solution to AKS leaves the same problem - who are we going to authenticate the communication between those Azure services? With the traditional approach, using service principals or connection keys, we have the problem of safely storing and rotating the credentials. Microsoft solved this problem by introducing Managed Identities. We can use Managed Identities for the AKS Control plane and Kubelet, but can we use them for pods inside AKS? The answer is YES - with the open-source component named AAD Pod Identity.
AAD Pod Identity enables Kubernetes applications to access cloud resources securely with Azure Active Directory. In addition to the security benefit, Managed Identities uses long-lived tokens and can handle Azure AD hiccups with a maximum duration between 12 and 24 hours.
Note: In preview, a managed add-on is available that you can enable for your AKS cluster, which will provide you with the same functionality. However, this add-on will never transition to General Availability because Microsoft is working on a V2 version.
How it works?
The solution consists of two core components:
- Managed Identity Controller (MIC) - manage the AzureAssignedIdentity. Assigns the specified Users Managed Identity to the nodepool (VMSS) on a pod scheduling event.
- Node Managed Identity (NMI) - intercept the requests to the Azure Instance Metadata Service and get the correct token based on the AzureIdentityBindings
and four Custom Resource Definitions (CRDs):
- AzureIdentity - describes the Azure Identity resource. It supports: User Assigned Identity and Service Principal (with password or certificate)
- AzureIdentityBindings - enables binding of the AzureIdentity to a pod using the specified selector
- AzureAssignedIdentity - managed by the Managed Identity Controller, it describes the state of the identity binding
- AzurePodIdentityException - enables pods to access the Azure Instance Metadata Service directly, without the request being intercepted by NMI
The following diagram describe the complete workflow of how AAD Pod Identity works:
Role Based Access Control Assignments
The AKS cluster needs to have the necessary permissions to assign and unassign the user-managed identities on the underlying Virtual Machine Scale Set. You need to have the role of Owner or User Access Administrator to perform RBAC assignments in Azure.
To create the assignments using Azure CLI, use the instructions below. Consider automating the process and do the assignments using a pipeline:
RESOURCE_GROUP= # resource group where the AKS cluster is deployed
CLUSTER_NAME= # the name of the AKS cluster
SUBSCRIPTION_ID= # the id of the subscription
IDENTITIES_RESOURCE_GROUP= # resource group name where the identities you plan to assign to pods are stored. If you plan to bind their lifetime to the cluster, you can keep them in the NODE_RESOURCE_GROUP. Otherwise, it is a best practice to use a dedicated resource group
# Obtain the ID of the managed identity assigned on the node pools
CLUSTER_IDENTITY_ID=$(az aks show -g $RESOURCE_GROUP -n $CLUSTER_NAME --query identityProfile.kubeletidentity.clientId -o tsv)
# Get the node resource group
NODE_RESOURCE_GROUP=$(az aks show -g $RESOURCE_GROUP -n $CLUSTER_NAME --query nodeResourceGroup -o tsv --only-show-errors)
# Assign the necessary role to the AKS cluster identity, to be able to modify the VMSS settings
az role assignment create --role "Virtual Machine Contributor" --assignee $CLUSTER_IDENTITY_ID --scope /subscriptions/$SUBSCRIPTION_ID/resourcegroups/$NODE_RESOURCE_GROUP
# Option 1 - Assign the necessary role to the AKS cluster identity, to be able to attach the managed identities in the IDENTITIES_RESOURCE_GROUP to the VMSS.
az role assignment create --role "Managed Identity Operator" --assignee $CLUSTER_IDENTITY_ID --scope /subscriptions/$SUBSCRIPTION_ID/resourcegroups/$IDENTITIES_RESOURCE_GROUP
# Option 2 - Assing the necessary role to the AKS cluster identity only to particular user managed identities:
az role assignment create --role "Managed Identity Operator" --assignee $CLUSTER_IDENTITY_ID --scope /subscriptions/$SUBSCRIPTION_ID/resourcegroups/$IDENTITIES_RESOURCE_GROUP/providers/Microsoft.ManagedIdentity/userAssignedIdentities/$IDENTITY_NAME
Security considerations with Kubenet
Running aad-pod-identity in a cluster with Kubenet network plugin is not a recommended configuration because of the security implication. Kubenet is susceptible to ARP spoofing, making it possible for other pods to impersonate a pod with access to identity. Using CAP_NET_RAW capability, an attacker pod could impersonate a pod with valid access and then request a token.
To mitigate the vulnerability, you need to add a securityContext in your pod defintion that drops the NET_RAW capability:
securityContext:
capabilities:
drop:
- NET_RAW
To enforce this mitigation at the cluster level, use Azure Policy add-on for AKS and enroll the Kubernetes cluster containers should only use allowed capabilities policy. Add NET_RAW into Required drop capabilities in the policy parameter values.
Deploy with Flux2
# PULL THE HELM CHART LOCALY
helm repo add aad-pod-identity https://raw.githubusercontent.com/Azure/aad-pod-identity/master/charts
helm pull aad-pod-identity/aad-pod-identity
# PUSH THE HELM CHART TO ACR
az acr helm push -n $ACR_NAME aad-pod-identity-*.tgz
# PUSH THE IMAGE TO ACR
AADPODIDENTITY_CHART_VERSION=$(helm show chart aad-pod-identity/aad-pod-identity | grep version | awk -F" " '{print $2}')
az acr import --source mcr.microsoft.com/oss/azure/aad-pod-identity/nmi:v$(helm show chart aad-pod-identity/aad-pod-identity | grep appVersion | awk -F" " '{print $2}') \
-n $ACR_NAME --force
az acr import --source mcr.microsoft.com/oss/azure/aad-pod-identity/mic:v$(helm show chart aad-pod-identity/aad-pod-identity | grep appVersion | awk -F" " '{print $2}') \
-n $ACR_NAME --force
# CREATE HELMRELEASE MANIFEST FOR AAD-POD-IDENTITY
mkdir -p $GITHUB_REPO/infrastructure/base/aad-pod-identity
cat > $GITHUB_REPO/infrastructure/base/aad-pod-identity/release.yaml << ENDOFFILE
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
name: aad-pod-identity
spec:
releaseName: aad-pod-identity
chart:
spec:
chart: aad-pod-identity
sourceRef:
kind: HelmRepository
name: acr-private
namespace: flux-system
version: "${AADPODIDENTITY_CHART_VERSION}"
interval: 1h0m0s
install:
remediation:
retries: 3
# Default values
# https://github.com/Azure/aad-pod-identity/blob/master/charts/aad-pod-identity/values.yaml
values:
image:
repository: ${ACR_NAME}.azurecr.io/oss/azure/aad-pod-identity
ENDOFFILE
# KUSTOMIZATION MANIFEST FOR AAD-POD-IDENTITY
cat > $GITHUB_REPO/infrastructure/base/aad-pod-identity/kustomization.yaml << ENDOFFILE
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: baseline
resources:
- release.yaml
ENDOFFILE
# COMMIT AND PUSH THE CHANGES
cd $GITHUB_REPO
git add *
git commit -m "aad-pod-identity installation"
git push
cd ..