Blog

Mastering Azure Kubernetes Services deel 4 Secure Identity & Access Management

Inleiding

Identity en Access Management (IAM) tot een digitaal landschap is vandaag de dag  misschien wel het belangrijkste aandachtspunt voor organisaties vanwege de vele cyberaanvallen. Deze aanvallen beginnen vaak met een door een aanvaller bemachtigde credential. Hackers richten zich niet alleen meer op het bemachtigen van interactieve credentials (credentials gebruikt door beheerders of gebruikers) maar ook niet-interactieve credentials (credentials gebruikt tussen systemen/services) zijn vandaag de dag populair bij hackers. Nieuwe methodes voor authenticatie en autorisatie, in een poging aanvallers buiten de deur te houden, volgen zich dan ook in rap tempo op.

Dit geldt zeker ook voor AKS, hiervoor zijn een aantal belangrijke keuzes te maken en te configureren. AKS wordt tegenwoordig vaak geconfigureerd met Entra-ID  (Azure AD) authenticatie en autorisatie. Kubernetes authenticatie en autorisatie blijven ook dan nog steeds een belangrijke rol spelen. Kubernetes service accounts met niet tijd-gebonden (long-lived) tokens, werden voor Kubernetes v1.22 opgeslagen in secrets en worden in veel AKS clusters, ook met Entra-ID authenticatie en autorisatie, nog steeds gebruikt. Long-lived tokens vormen een veiligheidsrisico en het is raadzaam deze te vervangen door tijd-gebonden (short-lived) tokens en, waar mogelijk, gebruik te maken van Entra-ID federated workload identities.

Deze blog gaat dan ook dieper in op AKS met Entra-ID authenticatie, Azure autorisatie (Azure Role Based Access Control), Entra-ID federated workload identities, Kubernetes authenticatie, Kubernetes autorisatie (Kubernetes RBAC) en Kubernetes service accounts. Voor de leesbaarheid en navigatie is deze blog opgesplitst in 3 delen:

Inleiding

Deel 1: AKS interactieve Authenticatie

Deel 2: AKS niet-interactieve Authenticatie

Deel 3: AKS Autorisatie

Conclusie

 

Toegang tot de Kubernetes API server.

AKS is een deels door Microsoft beheerde Kubernetes omgeving. Voor toegang tot het AKS cluster of eigenlijk de Kubernetes API-server, gebruikt Kubernetes vier verschillende achtereenvolgende stappen. Dit geldt voor alle soorten toegang, zoals toegang via kubectl, pods met client bibliotheken, of toegang door middel van REST aanvragen. Zowel gebruikers, Azure service principals, als Kubernetes service accounts kunnen toegang krijgen tot de Kubernetes API-server.

Onderstaande figuur toont de vier achtereenvolgende stappen voor het verkrijgen van toegang tot de Kubernetes API-Server.

Stap 1: Transport Layer Security (TLS)
De Kubernetes API-server Certificate Authority (CA) presenteert een certificaat voor het opzetten van een TLS sessie. Voor het opzetten van een vertrouwde TLS sessie naar de API-Server is de public key nodig van de API-server CA. Verderop in deze blog Entra-ID authenticatie leg ik uit hoe deze verkregen wordt.

Stap 2: Authenticatie
AKS ondersteunt verschillende Kubernetes authenticatie modules zoals client certificaten en JSON Web Tokens. Verderop in deze blog ga ik hier dieper op in. AKS interactieve authenticatie en AKS niet-interactieve authenticatie).

Stap 3: Autorisatie
Kubernetes kent een aantal autorisatie modules zoals Attribute Based Access Control (ABAC), Role Based Access Control (RBAC) en Webhook mode. AKS gebruikt Kubernetes RBAC en Azure RBAC via de Kubernetes Webhook mode (zie verderop in deze blog AKS Autorisatie).

Stap 4: Admission Control
Een admission controller bestaat uit code die de aanvraag naar de Kubernetes API-onderschept voordat deze het te benaderen Kubernetes object ‘bereikt’, maar nadat de aanvraag geauthentiseerd en geautoriseerd is. Admission control modules zijn software modules die een aanvraag kunnen valideren, aanpassen of weigeren, en hebben toegang tot de inhoud van het aan te passen of te creëren Kubernetes object. Behalve gecompileerde admission plugins kent Kubernetes ook ‘dynamic admission control’ plugins, ontwikkeld als extensies en draaiend als een webhook. Admission webhooks zijn HTTP callbacks die een request ontvangen, daar iets mee doen, en vervolgens doorsturen naar de Kubernetes API-server. AKS gebruikt een Kubernetes admission webhook voor de ondersteuning van Entra-ID federated workload identities.

Omdat het gebruik van een federated workload identity de meest veilige manier is om Azure resources te benaderen vanuit een AKS cluster – en bij dit type identity gebruik gemaakt wordt van een Kubernetes mutating admission webhook – zal ook dit onderwerp aan bod komen. (Zie verderop in deze blog Entra Federated Workload-ID met AKS).

Kubernetes IAM versus AKS IAM

Kubernetes kent twee soorten accounts, service accounts beheerd door de Kubernetes API-server en ‘gewone’ gebruikers (AKS noemt dit ‘local accounts’). Kubernetes kent, afgezien van een aantal standaard aanwezige gebruikers zoals clusterAdmin, verder geen ‘gewone’ gebruikers. Kubernetes gaat er van uit dat een Kubernetes onafhankelijke service, bijvoorbeeld Entra-ID, het gebruikersbeheer voor haar rekening neemt. Ondanks het feit dat Kubernetes geen ‘gewone’ gebruikers beheermodule heeft, zal elke gebruiker die een geldig client certificaat (signed door de CA van het cluster), aan de API-server presenteert, geauthentiseerd worden. Overigens is het ook mogelijk je eigen CA te koppelen aan een Kubernetes cluster, eventueel zelfs aparte CA’s voor TLS en Client Certificaten.

Kubernetes bepaalt de gebruikersnaam aan de hand van het common name veld in het subject van het client certificaat (bijvoorbeeld CN=”Willy-Jan”). Daarna bepaalt het Kubernetes RBAC subsysteem, via een Kubernetes rol en rolbinding (zie verderop in deze blog Kubernetes RBAC) of de gebruiker geautoriseerd is om de aangevraagde operatie uit te voeren. Deze manier van werken, waarbij een AKS beheerder client certificaten aan gebruikers moet toekennen en beheren is omslachtig en daarom geniet het de voorkeur om Microsoft Entra-ID Identities te gebruiken voor authenticatie en autorisatie naar je AKS Cluster. Kubernetes service accounts maken dan overigens nog steeds gebruik van Kubernetes authenticatie en autorisatie. Door gebruik te maken van Entra Federated Workload-ID met AKS wordt het gebruik van Kubernetes authenticatie en autorisatie tot een minimum beperkt.

Deel 1: AKS interactieve Authenticatie

Naast certificaat gebaseerde authenticatie ondersteunt Kubernetes, met verschillende type authenticatie plugins, op tokens gebaseerde authenticatie zoals bijvoorbeeld JWT tokens gegenereerd door Entra-ID. AKS ondersteunt zowel ‘local accounts’ (client certificaat) gebaseerde authenticatie als Entra-ID gebaseerde authenticatie. Met Entra-ID integratie maak je gebruik van het Open ID Connect (OIDC) protocol voor authenticatie.

Het ligt voor de hand om Entra-ID gebaseerde authenticatie te gebruiken, maar wat te doen met local accounts? Om te voorkomen dat clusterAdmin credentials verkregen worden via een client certificaat is het raadzaam om local accounts uit te schakelen in AKS. Verderop in deze blog ga ik hier dieper op in, zie Local Account Authenticatie.

ENTRA-ID (Azure AD) authenticatie

Wanneer je als gebruiker of administrator interactief met je cluster wilt communiceren via de Kubernetes API-server, gebruik je de kubectl tool. Om deze tool te kunnen gebruiken voor AKS is een Azure command-line interface (CLI) sessie nodig. Nadat je ingelogd bent met “az login” haal je de benodigde credentials op met:

az aks get-credentials --resource-group <myResourceGroup> --name <myAKSCluster>

Hiermee wordt een kubeconfig file (%homepath%\.kube\config) gegeneerd voor de connectie met je AKS cluster. Voor het kunnen uitvoeren van het “az aks get-credentials” commando zijn specifieke Azure rechten nodig op de Azure AKS resource die een gebruiker krijgt via de “Azure Kubernetes Service Cluster User” Rol.

De config file bevat informatie over hoe te authentiseren naar het cluster. Bovenin bevindt zich het base64 encoded certificaat met de public key van de CA van het cluster:

Onder de “certificate-authority-data” zie je het API-Server http endpoint en verderop de methode voor authenticatie:

Door een kubectl commando uit te voeren, bijvoorbeeld “kubectl get pods”, wordt default de OAuth 2.0 device authorization grant flow uitgevoerd (https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-device-code).

Het verkregen access token wordt vervolgens opgeslagen in de %homepath%\.kube\cache\kubelogin file.

Standaard gebruikt AKS tegenwoordig de credential plugin ‘kubelogin’ (https://azure.github.io/kubelogin/). Deze is voornamelijk bedoeld voor niet-interactieve login scenario’s, maar kun je ook gebruiken om bijvoorbeeld het access-token van je azure CLI sessie naar de API server te sturen, zodat je niet opnieuw hoeft te authentiseren nadat je met “az login” bent ingelogd bij Entra-ID. Met “kubelogin convert-kubeconfig -l azurecli” converteer je de authenticatie methode wat resulteert in een geupdate config file:

Als je nu met kubectl een commando naar de API server stuurt gebruikt kubelogin het access-token van de Azure CLI sessie.

Wanneer, zoals in bovenstaand voorbeeld, AKS geconfigureerd is met Entra-ID authenticatie gebruikt de Kubernetes API-server een webhook (http gebaseerde callback functie) om het token te valideren en het groepslidmaatschap van de gebruiker te achterhalen door middel van een query naar de MS Graph API. Hieronder zie je de authenticatie flow waarvan de stappen goed uitgelegd zijn op https://learn.microsoft.com/en-us/azure/aks/concepts-identity

Microsoft Entra-ID authenticatie voor AKS clusters is gebaseerd op OpenID Connect (OIDC). De verwijzing naar, en configuratie van de ‘Webhook token authenticatiemodule’ in de Kubernetes API server wordt geconfigureerd en beheerd door Microsoft als onderdeel van de AKS control plane.

Local Account (Kubernetes) Authenticatie

Zoals eerder beschreven wordt dringend aanbevolen local accounts uit te schakelen. Wanneer deze echter wel ingeschakeld zijn kan je met het volgende commando de Kubernetes default local account ClusterAdmin credential bemachtigen, mits je ook voldoende rechten hebt op de AKS Azure resource (zie verderop).

az aks get-credentials --resource-group <ResourceGroupNaam> --name <AKSClusterNaam> --admin

Deze admin credential bestaat uit een client certificaat, signed door de CA van de API-server en wordt toegevoegd aan de kubeconfig file.

Aan het einde van je config file zie je de details van het client certificaat dat gebruikt wordt voor authenticatie als Kubernetes cluster administrator.

De ‘client-certificate-data’ is een base64 encoded string die je via https://www.base64decode.org/ kunt decoderen. Wanneer je vervolgens de gedecodeerde data opslaat als een filenaam met extensie .crt en deze opent zie je het client certificaat met Subject ‘masterclient, system:masters’.

De gebruikersnaam is ‘masterclient’ en de groep waar de gebruiker lid van is ‘system:masters’.

Kubernetes heeft een aantal ingebouwde ‘Subjects’ met voorgedefinieerde rollen. ‘Subjects’ kunnen gebruikers, groepen of service accounts zijn. De volgende tabel toont de standaard Kubernetes gebruikers en groepen.

UsersGroups
clusterAdminsystem:masters
clusterMonitoringUsersystem:masters
system:kube-controller-managersystem:authenticated
system:kube-schedulersystem:unauthenticated
system:kube-proxy

 

De system:masters groep is standaard door middel van een Kubernetes ‘rolbinding’ gekoppeld aan de voorgedefinieerde ‘cluster-admin’ ClusterRole via de ‘cluster-admin’ ClusterRoleBinding. Zie verderop in deze blog uitleg over Kubernetes rollen en rolbindings (Kubernetes RBAC).

Let op, met deze config file authentiseer je dus met standaard Kubernetes local admin account credentials, zonder Entra-ID authenticatie, en heb je volledige administratieve rechten op het AKS cluster.

Om een signed client certificaat voor het Kubernetes local admin account (–admin) op te kunnen halen heb je, naast het feit dat local accounts ‘enabled’ moet zijn, op de AKS Azure resource een aantal rechten nodig zoals bijvoorbeeld ‘List the clusterAdmin credential of a managed cluster’. Deze rechten krijg je met de Azure rol ‘Azure Kubernetes Service Cluster Admin’ op de AKS Azure resource.

Ook Entra-ID accounts met bijvoorbeeld een owner of contributer role op de resource groep of op de subscriptie van de AKS resource, hebben via ‘inheritance’ de benodigde rechten om dit client certificaat te bemachtigen. Iedereen die in het bezit is van een kubeconfig file met het cluster-admin client certificaat en in staat is om het publieke, of in geval van een private cluster, het private AKS Cluster API-server endpoint (zie mijn blog mastering AKS deel 3) te benaderen, heeft zo administratieve toegang tot het AKS Cluster zonder enige vorm van Entra-ID authenticatie. Dit werkt zelfs wanneer local accounts zijn uitgeschakeld op het AKS cluster! (Alleen om een client certificaat te ‘bemachtigen’ met de flag “–admin” moet local accounts enabled zijn.)

Zorg er voor dat deze standaard cluster-admin credential niet voor operatie of development doeleinden gebruikt wordt! Het ‘rondzwerven’ van deze config file op diverse plaatsen vormt een veiligheidsrisico. Daarom is het raadzaam om direct na de deployment van een AKS cluster één config file met het cluster-admin certificaat te maken, deze veilig te bewaren, en daarna direct local accounts uit te schakelen. In geval van nood kan de config file gebuikt worden als een ‘break-glass’ procedure.

Deel 2: AKS niet-interactieve authenticatie

Niet-interactieve authenticatie wordt steeds meer gebruikt, zoals bijvoorbeeld met Azure DevOps pipelines of GitHub actions voor het uitrollen van Kubernetes deployments, maar bijvoorbeeld ook onderlinge authenticatie tussen pods, pods die zich moeten authenticeren bij een Kubernetes API-server of pods op een AKS cluster die zich moeten authenticeren bij Entra-ID om een Azure resource te benaderen. Dit onderdeel van deze blog gaat hier dieper op in.

Kubernetes service accounts

In tegenstelling tot ‘normale’ user accounts, die in de Kubernetes API server niet als objecten bestaan, bestaan service accounts wel als objecten in de API-server. Ook wanneer AKS is geconfigureerd met Entra-ID (Azure AD) authenticatie en Azure Role Based Access Control (RBAC) spelen Kubernetes service accounts nog steeds een belangrijke rol in een AKS cluster! Zie Entra Federated Workload-ID met AKS verderop in deze blog.

Bij het aanmaken van een Kubernetes cluster maakt Kubernetes automatisch een service account object, genaamd “default” aan, voor elke namespace in het cluster. Dit service account heeft standaard geen permissies behalve ‘default API discovery’ permissies. Deze permissies kent Kubernetes toe aan alle geauthenticeerde identities, mits Kubernetes RBAC aan staat, wat bij AKS het geval is.

Met “kubectl create serviceaccount <serviceaccountnaam> -n <namespace>” creëer je een ‘eigen’ Kubernetes service account in een specifieke namespace.

Kubernetes service accounts gebruiken API Server signed JSON Web Tokens (JWTs) die als signed bearer token in de header (Authorization: Bearer <token>) van een http request naar de API server gestuurd worden voor authenticatie.

Met “kubectl create token <serviceaccountnaam> -n <namespace>” creëer je een tijd-gebonden token voor het zelf aangemaakte service account. De output toont het deels base64 encoded token dat je via de website https://jwt.io kunt decoderen:

Op deze jwt.io website is ook uitleg te vinden over JWT tokens.

Met bijvoorbeeld “kubectl –token=<xxxxxxx> get pods -n <namespace>” of postman kun je testen of het token werkt. Om informatie over pods of andere Kubernetes objecten te kunnen opvragen heb je echter ook rechten nodig (autorisatie), dus krijg je een autorisatie error wanneer je dit kubectl commando uitvoert voordat het service account via RBAC rechten op Kubernetes objecten heeft gekregen. Zie verderop  AKS Autorisatie.

Onderstaand figuur toont een authenticatie verzoek naar de Kubernetes API server zonder autorisatie header:

Selecteer vervolgens bij “Authorization” Bearer Token. Na het invullen van het verkregen token in het token veld en het opnieuw versturen van een “GET” request wordt geen “Unauthorized” melding meer getoond:

Het antwoord verschilt voor AKS clusters geconfigureerd met Kubernetes RBAC en Azure RBAC. Bovenstaande afbeelding toont de melding voor een cluster geconfigureerd met Kubernetes RBAC.

Voor Kubernetes v1.22 werden er altijd niet tijd-gebonden (long-lived) tokens voor service accounts aangemaakt, die dan bewaard werden in een secret. Het is nog steeds mogelijk dit soort tokens/secrets aan te maken en te gebruiken maar dit wordt ten zeerste afgeraden vanwege het veiligheidsrisico. Wees je bewust van eventueel al aanwezige long-lived tokens, en vervang deze waar mogelijk door short-lived tokens.

Wanneer een pod uitgerold wordt in een cluster en er is via een pod-manifest geen service account toegewezen aan de pod, dan krijgt de pod automatisch het “default” service account van de namespace waarin de pod uitgerold wordt.

Bij het toewijzen van een ‘eigen’ service account met een pod-manifest aan een pod (spec.serviceAccountName veld in de pod sepcificatie) maakt Kubernetes, via de TokenRequest API, een short-lived, automatisch roterend token aan voor dat service account en projecteert dit token als een volume aan de pod.

Het kubelet proces, draaiend in de control plane en op elke node, zorgt, in opdracht van de pod, voor de aanvraag van het token. Plaats dit vervolgens in een configureerbaar path en zorgt voor de automatische rotatie van het token wanneer het token ouder is dan 80% van de time-to-live (TTL), of wanneer het token ouder is dan 24 uur.

De applicatie is dus verantwoordelijk voor het herladen van het token wanneer dit roteert. Dit kan bijvoorbeeld door elke 5 minuten het token te laden zonder de daadwerkelijk “expiration time” van het token bij te houden.

Deze manier van service account toekenning zorgt er ook voor dat het gerelateerde token “invalid” wordt zodra de betreffende pod verwijderd wordt.

In onderstaand voorbeeld wordt een Kubernetes service account “svctest01” aangemaakt in een “test” namespace (1). Vervolgens wordt een pod aangemaakt met het service account “svctest01” waarvan het token automatisch geprojecteerd wordt in een mounted volume aan de pod (2). Nu kan het token van het service account vanuit de pod gebruikt worden om de API-Server te benaderen (3,4).

Stap 1: Maak een service account “svctest01” in namespace “test”.

kubectl create namespace test
kubectl create serviceaccount svctest01 -n test

Stap 2: Deploy een pod-manifest met het service account gekoppeld.

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: nginxpodtst
  namespace: test
spec:
  containers:
  - image: nginx:latest
    name: nginxpodtst
  serviceAccountName: svctest01
EOF

Stap 3: Open een shell naar de pod.

kubectl exec -n test --stdin --tty  nginxpodtst -- /bin/bash

Stap 4:  Maak een request naar de API-server vanuit de pod met het token van het geprojecteerde service account.

Maak een variabele “APISERVER” aan die verwijst naar de interne API server hostname

APISERVER=https://kubernetes.default.svc

Maak een variable “SERVICEACCOUNT” aan die verwijst naar het path van het service account token

SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount

Maak een variabele aan met de inhoud van het service account bearer token

TOKEN=$(cat ${SERVICEACCOUNT}/token)

Maak een variabele aan die wijst naar de file met het publieke certificaat van de API-Server Certificate Authority (CA)

CACERT=${SERVICEACCOUNT}/ca.crt

Authenticeer bij de API server met het token

curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api

Dit resulteert in een geauthentiseerde sessie naar de API-server:

Entra-ID Workload Identities

Sinds enige tijd wordt de term workload identities gepromoot. Een workload identity is een identity die toegekend (assigned) wordt aan een software workload (een applicatie, service, script of container) en daarmee toegang krijgt tot andere services of resources. Wanneer een GitHub action bijvoorbeeld toegang nodig heeft tot de Azure Resource Manager heeft die GitHub Action een workload identity nodig.

In Microsoft Entra-ID bestaan workload identities uit: applications (app registrations), service principals (enterprise applications), en managed identities. Niets nieuws dus zou je zeggen! Dat klopt. Wat wel nieuw is, is het fenomeen workload identity federation.

Workload Identity Federation

Managed Identities zijn met name geschikt voor software workloads draaiend in Azure die toegang nodig hebben tot een Azure resource. Het Azure platform beheert de credentials (certificaten) voor managed identities. Voor software workloads draaiend buiten Azure, bijvoorbeeld actions in GitHub of pipelines op Microsoft hosted agents, zijn zelf te beheren applicatie credentials (certificaten of secrets) nodig voor toegang tot Azure resources. Deze credentials vormen een veiligheidsrisico en moeten veilig opgeslagen en regelmatig verwisseld worden. Er bestaat ook een risico op downtime wanneer de credentials verlopen zijn, zonder dat deze vernieuwd zijn.

Met workload identity federation worden deze problemen getackled. Met de configuratie van een federated workload identity (speciale configuratie van een user-assigned managed identiy of app registration), wordt een vertrouwensrelatie (trust) tussen de federated workload identity en de externe identity provider (IdP) gemaakt. De federated (Entra-ID) workload identity wordt de identity voor de externe software workload.

Entra-ID ondersteunt op dit moment federated workload identities voor een beperkt aantal scenario’s, zoals bijvoorbeeld GitHub actions die Azure resources uitrollen of aanpassen, Azure DevOps Service Connections (nog in public preview), en ook Entra Federated Workload ID met AKS waar ik hieronder in deze blog dieper op in ga.

Entra Federated Workload-ID met AKS

Workloads (pods/containers) op een AKS cluster hebben een Microsoft Entra applicatie credential of managed identity nodig om toegang te krijgen tot Azure resources, zoals bijvoorbeeld Azure Key Vault of Microsoft Graph. Kubernetes ondersteunt inmiddels de mogelijkheid voor het opzetten van een federatie met externe Identity Providers (IdPs), zoals bijvoorbeeld Entra-ID.

Microsoft Entra Workload ID gebruikt drie Kubernetes features om pods gebruik te laten maken van een Entra Federated Workload-ID. Hierbij wordt een Kubernetes service account, waarvan het token vertrouwd wordt door Entra-ID, gebruikt voor het verkrijgen van een Entra-ID token wat geïnjecteerd wordt in een mounted volume aan de pod. Het Kubernetes service account token wordt vertrouwd door Entra-ID door het opzetten van een soort OIDC “trust” tussen een Kubernetes service account en Entra-ID workload Identity.

Feature 1: Service account issuer discovery
Met deze Kubernetes feature maak je van Kubernetes een OIDC compatable Identity Provider (IdP). Dit gebeurt door het publiceren van de metadata (beschrijving) van de OIDC provider configuratie. Deze beschrijving wordt gedeeld met http in een OpenID Provider Configuration document (ook wel discovery document genoemd) op een bekende locatie (/.well-known/openid-configuration).

Deze Kubernetes feature zet je in AKS aan met “az aks update -g <resourcegroup> -n <AKSClusternaam> –enable-oidc-issuer”.

Wanneer AKS geconfigureerd is met deze Kubernetes feature kun je het discovery document raadplegen door achter de discovery URL  “/.well-known/openid-configuration” te plakken.

Voor het raadplegen van de AKS discovery URL gebruik je het volgende commando:

az aks show -n myAKScluster -g myResourceGroup --query "oidcIssuerProfile.issuerUrl" -otsv

Door achter de verkregen URL “/.well-known/openid-configuration” te plakken kun je met een browser het discovery document raadplegen:

https://westeurope.oic.prod-aks.azure.com/f398b6ff-468c-41f0-aefe-78efa2d4998d/8b19fa34-c4bb-4324-9ec9-7c504c6314da/.well-known/openid-configuration

Ook voor je eigen Entra-ID tenant kun je het discovery document raadplegen:
https://login.microsoftonline.com/<naam>.onmicrosoft.com/v2.0/.well-known/openid-configuration

Naast het discovery document wordt ook de gerelateerde JSON Web Key Set (JWKS) via http gepubliceerd op de URI met /openid/v1/jwks

Deze URI bevat een array van public signing keys die gebruikt worden door Entra-ID om de handtekeningen van de “signed” access tokens te valideren.

Voor meer achtergrondinformatie en het ‘uitlezen’ van de public key zie https://www.jhanley.com/blog/azure-openid-connect-json-web-key-set/

Het volgende diagram laat de authenticatie flow zien gebruik makend van OIDC.

Bron: https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview?tabs=dotnet

Feature 2: Service account token volume projection
Deze Kubernetes feature zorgt er voor dat tokens voor Kubernetes service accounts:

  1. Van een digitale handtekening worden voorzien (via een token request API) door de Kubernetes CA.
  2. Geprojecteerd worden in een mounted volume van een pod (door kubelet).

Zie voor meer achtergrond informatie:

https://Kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#serviceaccount-token-volume-projection

Feature 3: Mutating admission webhook
Tot slot maakt AKS gebruik van een admission webhook (zie Toegang tot de Kubernetes API server stap 4 admission control in deze blog) om pods, die een label “azure.workload.identity/use: “true”” in hun template specificatie hebben, te muteren.

Deze feature zet je in AKS aan met “az aks update -g <resourcegroup> -n <AKSClusternaam> –enable-workload-identity” en wordt meestal gelijktijdig met –enable-oidc-issuer” aangezet.

AKS voegt twee pods toe in de kube-system namespace die de admission webhook draaien.

De mutating admission webhook injecteert azure specifieke environment variabelen en het verkregen azure-idenity token in het “mounted” volume.

Zoals in bovenstaande afbeelding te zien is worden zowel het Kubernetes service account token (kube-api-access-6smkj) als het verkregen azure-identity token geïnjecteerd.

Vanuit de pod kan nu met het azure-identity token toegang verkregen worden tot een Azure resource. Zie de volgende afbeelding voor de stappen:

Bron: https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview?tabs=dotnet

Stappen:

  1. Kubelet projecteert een signed Kubernetes token, verkregen via de Kubernetes request token API, in een mounted volume van de pod.
  2. Wanneer de pod het label azure.workload.identity/use: “true” heeft, wordt de mutating webhook admission controller aangeroepen. Deze stuurt het Kubernetes service account met annotatie azure.workload.identity/client-id: <appid> en label azure.workload.identity/use: “true” met bijbehorend token naar Entra-ID.
  3. Entra-ID valideert de trust tussen de Entra-ID user assigned managed identity en het Kubernetes service account met behulp van het OIDC discovery document.
  4. Na validatie stuurt Entra-ID een Entra-ID access token terug naar de webhook admission controller. Deze projecteert Azure specifieke environment variabelen en het verkregen access token in een mounted volume van de pod.
  5. De pod kan nu het ENTRA-ID access token gebruiken om een Azure resource te benaderen.

Het volgende voorbeeld laat een deployment met een federated workload identity zien om vervolgens vanuit de pod met deze identity een Azure resource te benaderen. Hierbij worden dus geen App Registration secrets of Managed Identity certificaten gebruikt.

Voorbeeld: Gebruik van federated workload identity vanuit een pod.

Stap 1: Maak een User Assigned Managed Identity aan met federated credentials.

  • Kies voor het scenario “Configure a Kubernetes service account to get tokens…”
  • Vul bij Cluster Issuer URL de URL in verkregen met “az aks show -n myAKScluster -g myResourceGroup –query “oidcIssuerProfile.issuerUrl” -otsv” (URL eindigt op een “/” plak hier niet .well-known/openid-configuration achter).
  • Geef de Namespace op waarin het Kubernetes service account (en de pod) aangemaakt worden.
  • Geef de Kubernetes service account naam op waarvan het Kubernetes token gebruikt gaat worden om het Entra-ID token van deze User Assigned Managed Identity te verkrijgen.
  • Geef de credential een naam.
  • Laat bij Audience de default weergegeven audience claim staan.

Stap 2: Geef de aangemaakte User Assigned Managed Identity rechten door middel van Azure RBAC op een Azure Resource. Bijvoorbeeld de reader rol op een subscriptie.

Stap 3: Maak een Kubernetes service account aan met annotations voor client-id (van de User Assigned Managed Identity), tenant-id, en een label “ azure.workload.identity/use: “true”.

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
  name: fed-sa
  namespace: development
  annotations:
    azure.workload.identity/client-id: <xxxx>
    azure.workload.identity/tenant-id: <xxxx>
  labels:
    azure.workload.identity/use: "true"
EOF

Stap 4: Maak een deployment met een label “azure.workload.identity/use: “true” in het template en een verwijzing naar het zojuist aangemaakte Kubernetes service account.

cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: azcli-deployment
  namespace: development
  labels:
    app: azcli
spec:
  replicas: 1
  selector:
    matchLabels:
      app: azcli
  template:
    metadata:
      labels:
        app: azcli
        azure.workload.identity/use: "true"
    spec:
      serviceAccount: fed-sa
      containers:
        - name: azcli
          image: mcr.microsoft.com/azure-cli:latest
          command:
            - "/bin/bash"
            - "-c"
            - "sleep infinity"
EOF

De environment variabelen voor de uitgerolde container zijn:

De AZURE_FEDERATED_TOKEN_FILE variabele bevat het path naar de file die het JWT token bevat.

Dit token kan vanuit de pod gebruikt worden om een Azure resource te benaderen.

Stap 5: Start een shell naar de deployment (shell connecteert naar de eerste pod van de deployment)

kubectl exec -n development --stdin --tty  deployment/azcli-deployment -- /bin/bash

Stap 6: Login bij Entra-ID

az login --service-principal -u $AZURE_CLIENT_ID -t $AZURE_TENANT_ID --federated-token "$(cat $AZURE_FEDERATED_TOKEN_FILE)"

Stap 7: Benader een Azure resource, bijvoorbeeld vraag een overzicht op van alle AKS clusters

az aks list -o table

Omdat het Kubernetes service account ook geïnjecteerd is kun je dit gebruiken voor toegang tot de API-Server. Zie eerder in deze blog Kubernetes service accounts.

Wanneer echter Azure RBAC gebruikt wordt is het meer voor de hand liggend om het verkregen federated workload identity token te gebruiken voor toegang tot en het benaderen van Kubernetes objecten. Zie hieronder Voorbeeld GitHub Workflow met Federated Workload-ID voor Azure Resource Manager en AKS API-server voor een uitgewerkt voorbeeld van een Azure federated workload identity wat gebruikt wordt door een GitHub Action om in te loggen bij Entra-ID en toegang krijgt tot zowel Azure resources als de AKS API-Server.

Voorbeeld GitHub Workflow met Federated Workload-ID voor Azure Resource Manager en AKS API-server toegang.

Door gebruik te maken van federated workload identities in GitHub Workflows/Actions voorkom je het moeten beheren en opslaan van secrets voor Entra-ID applicatie registraties.

Stap1: Maak een user assigned managed identity aan met een federated credential.

  • Kies voor het scenario “Configure a GitHub issued token to impersonate this application and deploy to Azure”.
  • Vul je GitHub Organisatie naam en Repository naam in. Het Subject identifier veld wordt automatisch gevuld.
  • Kies een naam voor de credential en laat het Audience veld met de default waarde gevuld.
  • Selecteer voor Entity bijvoorbeeld Branch en geef je Branch op waarvoor de credential gebruikt gaat worden.

Stap2: Geef vervolgens de user assigned managed identity een Azure AKS RBAC rol, bijvoorbeeld ‘Azure Kubernetes Service RBAC Admin’ op het AKS Azure object, of een specifiek namespace met Azure CLI (zie Azure RBAC).

Stap 3: Maak in je GitHub Repo actions ‘secrets’ aan voor Client-ID, Subscription-ID en Tenent-ID, en vul deze secrets met de juiste ID’s. Client-ID is dus het Client-ID van de aangemaakte managed identity.

Stap 4: Maak een GitHub workflow aan en gebruik de azure/login@v1 GitHub Action met de betreffende secrets uit de repository.

Deze Action genereert een GitHub token dat Entra-ID ontvangt en controleert via de federated credential van de user assigned managed identity. Entra-ID genereert en geeft een Entra-ID access-token terug, dat vervolgens gebruikt kan worden door az CLI.

name: Run Azure Login with OIDC
on:
  push:
    branches:
      - main
permissions:
  id-token: write
  contents: read
jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Az CLI login
        uses: azure/login@v1
        with:
          client-id: ${69c71ee015543f17f{ secrets.AZURE_CLIENT_ID_GHA }69c71ee015543f17f}
          tenant-id: ${69c71ee015543f17f{ secrets.AZURE_TENANT_ID }69c71ee015543f17f}
          subscription-id: ${69c71ee015543f17f{ secrets.AZURE_SUBSCRIPTION_ID }69c71ee015543f17f}
      - name: KubeCTL test
        run: |
          az aks install-cli
          az account show
          az aks get-credentials --resource-group rgwjvproaks --name akswjvpro
          kubelogin convert-kubeconfig -l azurecli
          cat /home/runner/.kube/config
          kubectl get pods -n test

Met ‘az aks install-cli’ installeer je naast de Azure CLI ook kubectl en kubeconfig op de runner.  “Az aks get-credentials” maakt de Kubernetes config file aan voor de ingelogde gebruiker (user assigned managed identity) en door het converteren van de config file met ‘azurecli’, gebruikt de Kubernetes config file het verkregen Entra-ID token voor toegang tot de Kubernetes API server! Deze toegangen, tot Azure Resource Manager en Kubernetes API-Server, zijn dus gerealiseerd zonder het uitwisselen van een secret gekoppeld aan een Azure Application Registration.

In het volgende deel van deze blog geef ik uitleg over AKS autorisatie configuratie mogelijkheden. Hier ga ik dieper in op zowel Kubernetes RBAC als Azure RBAC.

Deel 3: AKS Autorisatie

Na authenticatie van een identity vindt autorisatie plaats. AKS kan geconfigureerd worden met op  Kubernetes gebaseerde Role-Based Access Control (RBAC) of op Azure gebaseerde RBAC. De meeste organisaties kiezen voor Azure RBAC, omdat men daar bekend mee is en omdat AKS een Azure service is, is dit ook zeker een logische en goede keuze. Realiseer je dat ook met Azure RBAC, Kubernetes RBAC nog steeds van belang is, met name voor Kubernetes service accounts die autorisatie nodig hebben voor de Kubernetes API server.

Wanneer Azure RBAC aan staat worden Entra-ID identities exclusief door Azure RBAC gevalideerd en Kubernetes users en service accounts exclusief door Kubernetes RBAC.

Kubernetes RBAC

Kubernetes RBAC kent de volgende vier Kubernetes objecten: Role, ClusterRole, RoleBinding and ClusterRoleBinding. Naast de default aanwezige objecten kun je met kubectl nieuwe (cluster)rollen en (cluster)rolbindings aanmaken.

Een Role of Clusterrole bevat complementaire regels die permissies vertegenwoordigen. Er bestaan geen “deny” regels.

Een RoleBinding koppelt subjects (users, groups, service accounts) aan een Role of ClusterRole. Een Role en RoleBinding zijn altijd gekoppeld aan een namespace. Een ClusterRole bestaat in het cluster en geeft permissies over het hele cluster via een ClusterRoleBinding.

Een RoleBinding kan een Kubernetes service account koppelen aan een ClusterRole, en kan zelfs een service account uit een andere namespace koppelen aan een Role binnen de namespace waar de Rolebinding is aangemaakt.

Onderstaande figuur geeft diverse mogelijke RoleBindings en ClusterRoleBindings weer.

Voor uitgebreide informatie over Kubernetes RBAC en default (Cluster)Roles, (Cluster)RoleBindings zie: Using RBAC Authorization | Kubernetes, waar ook de default ClusterRollen te vinden zijn:

Bron: https://kubernetes.io/docs/reference/access-authn-authz/rbac

Hier zie je, zoals eerder besproken, zie Local Account (Kubernetes) authenticatie, dat de system:masters groep de default ClusterRole “cluster-admin” krijgt via de “Default ClusterRoleBinding” cluster-admin. Voor een overzicht van alle ClusterRoleBindings gebruik je:

kubectl get clusterrolebinding --all-namespaces

Zoals in de bovenstaande tabel te lezen is krijgt de ClusterRole “edit” op een specifiek namespace, potentieel, via een Kubernetes service account, rechten op een andere namespace. Veelal worden namespaces gebruikt om Kubernetes resources te isoleren voor bijvoorbeeld specifieke teams of het scheiden van ontwikkel, test, acceptatie en productie omgevingen. Wanneer service accounts uit een namespace rechten hebben op resources uit een andere namespace heeft dit dus gevolgen voor het isolatiedoel. Met het volgende commando wordt bijvoorbeeld een RoleBinding ‘rb-svctest01-view’ in de default namespace aangemaakt voor serviceaccount svctest01 in de ‘test’ namespace.

kubectl create rolebinding rb-svctest01-view --clusterrole=view --serviceaccount=test:svctest01

Door het weglaten van –namespace, wordt de rolebinding in de ‘default’ namespace aangemaakt (controleer met “kubectl get rolebinding -n default”). Het service account in de ‘test’ namespace krijgt via de default ClusterRole ‘view’, lees rechten in de ‘default’ namespace. Een gebruiker met een ‘Edit’ ClusterRole in de ‘test’ namespace kan nu via dit service account ook de objecten zien van de ‘default’ namespace.

Met het volgende commando kun je nagaan of het service account svctest01 in namespace ‘test’ inderdaad ‘view’ rechten heeft in de ‘default’ namespace.

kubectl auth can-i list pods --namespace default --as system:serviceaccount:test:svctest01

In het volgende deel, Azure RBAC,  laat ik zien hoe ook een gebruiker met een AKS RBAC ‘writer’ rol in een namespace een Kubernetes service account met rechten in een andere namespace kan gebruiken om Kubernetes objecten te raadplegen buiten de namespace waarop de gebruiker de ‘writer’ rol heeft.

Azure RBAC

De meeste AKS clusters hebben tegenwoordig Azure RBAC aan staan voor autorisaties binnen het AKS cluster (Kubernetes API Server). Is dit nog niet het geval dan kun je met “az aks update -g <rerourcegroup> -n <clusternaam> –enable-azure-rbac” of via de Azure portal Azure RBAC voor een AKS cluster aanzetten.

Azure RBAC wordt gebruikt voor het uitdelen van rechten aan Entra-ID identities (user accounts, service principals, managed identities) door middel van Azure rollen en Azure role assignments voor het kunnen benaderen, aanmaken, aanpassen en verwijderen van Kubernetes objecten via de Kubernetes API-Server.

AKS gebruikt een Kubernetes authorization webhook om na authenticatie de Azure autorisatie af te handelen.

Bron: https://learn.microsoft.com/en-us/azure/aks/concepts-identity

Zoals eerder aangegeven wordt Kubernetes RBAC nog steeds gebruikt maar alleen voor de identities buiten Entra-ID zoals Kubernetes service accounts.

Azure kent een aantal standaard ingebouwde rollen die in hoge mate overeenkomen met standaard Kubernetes rollen (zieKubernetes RBAC).

Let op, verwar onderstaande rollen niet met Azure rollen voor Azure resources zoals bijvoorbeeld de AKS resource!

RolOmschrijving
Azure Kubernetes Service RBAC ReaderGeeft ‘lees’ toegang tot de meeste objecten in een namespace, maar niet voor het lezen van roles,role-bindings en secrets.
Azure Kubernetes Service RBAC WriterGeeft ‘lees en schrijf’ toegang tot de meeste objecten in een namespace, maar niet voor het lezen/schrijven van roles en role bindings. Geeft wel toegang tot secrets en het draaien van pods als een service account in de namespace. Dit betekent dat een gebruiker met deze rol API toegangsrechten kan krijgen via alle service accounts in de namespace, die ook rechten kunnen hebben buiten de namespace.
Azure Kubernetes Service RBAC AdminGeeft admin toegang met de bedoeling deze toe te kennen aan een specifieke namespace. Geeft ‘lees/schrijf’ toegang tot de meeste resources in een namespace, inclusief het aanmaken van rollen en rolbindings binnen de namespace mits toegekend aan een namespace. Geeft geen toegang tot resource quota en de namespace zelf.
Azure Kubernetes Service RBAC Cluster AdminGeeft volledige toegang tot alle resources in het cluster binnen alle namespaces.

 

Twee eerder besproken Azure rollen voor de AKS resource zijn:

  • Azure Kubernetes Service Cluster Admin
    Deze Azure rol is alleen bedoeld voor het kunnen ophalen van admin credentials om vervolgens met een client-certificaat te kunnen authenticeren bij de API server. (Zie eerder in deze blog ( Local Account Authenticatie )
  • Azure Kubernetes Service Cluster User
    Deze Azure rol is alleen bedoeld voor identities die zich met een Entra-ID account moeten kunnen authentiseren naar de Kubernetes API-Server. (Zie eerder in deze blog ENTRA-ID authenticatie)

Voor het maken van een custom Azure RBAC rol, definieer je toegestane AKS operaties in JSON formaat en creëer je de rol met “az role definition create”. Zie: https://learn.microsoft.com/en-us/azure/role-based-access-control/resource-provider-operations#microsoftcontainerservice

Het toekennen van Azure RBAC rollen aan Entra-ID identities kan met de portal, Azure CLI of Infrastructure as Code (IaC) bijvoorbeeld Bicep of Terraform. Wanneer RBAC rollen via de Azure Portal (IAM van de AKS resource) toegekend worden, geldt de rol voor alle namespaces in het cluster!

Hieronder zie je een voorbeeld voor het aanmaken van een rol assignment voor user ‘tstusr1@<domeinsuffix>’ in de namespace ‘test’.

AKS_ID=$(az aks show -g <resourcegroup> -n <clusternaam> --query id -o tsv)
az role assignment create --role "Azure Kubernetes Service RBAC Writer" --assignee tstusr1@<domeinsuffix> --scope $AKS_ID/namespaces/test

Zoals in de beschrijving van de rol “Azure Kubernetes Service RBAC Writer” te lezen is, heeft deze rol veel gelijkenis met de Kubernetes Cluster Role ‘edit’ (zie Kubernetes RBAC)

In het volgende voorbeeld zie je hoe gebruiker tstusr1@<domeinsuffix> met de Azure RBAC rol ‘Azure Kubernetes Service RBAC Writer’ in de namespace ‘test’ via het Kubernetes service account svctest01 in de namespace ‘test’, Kubernetes objecten kan bevragen in de ‘default’ namespace.

Uitgangspunt hierbij is dat het Kubernetes service account ‘svctest01’ via een Kubernetes rolebinding de ClusterRole ‘view’ heeft gekregen in de ‘default’ namespace en dat het serviceaccount gekoppeld is aan de pod ‘nginxpodtst’. Zie de gemaakte pod deployment en rolebinding uitgelegd eerder in deze blog: Kubernetes service accounts en Kubernetes RBAC.

Stap 1: Log in met tstusr1@<domeinsuffix> op de API-Server door eerst in te loggen in Azure

az logout
az login
login met  tstusr1@<domeinsuffix>

Stap 2: Haal de benodige API-Server credentials op

az aks get-credentials --resource-group <AKSResourceGroepNaam> --name <AKSNaam>

Stap 3: Converteer de config file zodat de Azure CLI credential gebruikt wordt

kubelogin convert-kubeconfig -l azurecli

Stap 4: Vraag een overzicht van pods in de default namespace

kubectl get pods

Als het goed is krijg je nu een melding dat je geen toegang hebt tot deze resource in de ‘default’ namespace. Nu gaan we via het service account gekoppeld aan de pod een overzicht opvragen van de pods in de ‘default’ namespace.

Stap 5: Open een shell naar de pod “nginxpodtst”

kubectl exec -n test --stdin --tty nginxpodtst -- /bin/bash

Stap 6: Maak environment variabelen aan

APISERVER=https://Kubernetes.default.svc
SERVICEACCOUNT=/var/run/secrets/Kubernetes.io/serviceaccount
TOKEN=$(cat ${SERVICEACCOUNT}/token)
CACERT=${SERVICEACCOUNT}/ca.crt

Stap 7: Vraag een overzicht van de pods op in de default namespace

curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api/v1/namespaces/default/pods/

Op deze manier hoop ik wat meer inzicht gegeven te hebben in de beschrijving van de Kubernetes ‘edit’ ClusterRole en Azure ‘Azure Kubernetes Service RBAC Writer’ rol.

Conclusie

AKS IAM, zelfs met Azure Entra-ID Authenticatie en Azure RBAC is complexer dan je wellicht in eerste instantie zou verwachten. Wees je bewust van de Kubernetes serviceaccounts, de system:masters Kubernetes groep, Kubernetes Roles en RoleBindings. Gebruik Entra workload Identity Federation voor het benaderen van Azure resources vanuit een AKS Cluster en beperk het gebruik van Kubernetes langlevende tokens opgeslagen in secrets. Met deze aanbevelingen configureer je voor wat identity en access management betreft een veilig AKS cluster.

Willy-Jan Verberne

Willy-Jan is één van onze Azure specialisten. Met een sterke achtergrond in solution architectuur, identity en accessmanagement praat hij je bij over alles rondom (onder andere) Entra ID en AKS.

Door: Willy-Jan Verberne

Willy-Jan is een zeer ervaren Azure solution architect met een voorliefde voor DevOps. Hij heeft een brede kennis over het neerzetten van infrastructuur oplossingen in Azure, gecombineerd met veilige identiteit en toegangsbeheer configuraties. Dit zorgt ervoor dat hij als geen ander weet hoe je landschappen neerzet waarin een team de vrijheid krijgt waar ze naar op zoek is, terwijl de organisatie gelijktijdig in controle blijft door inzicht in het azure landschap, de juiste Azure security maatregelen, en herbruikbaarheid van onderdelen.

Meer weten over onze experts? Neem gerust contact met ons op!