How AKS authentication integrates & works with Microsoft ID

How AKS authentication integrates & works with Microsoft ID

Introduction

In production & enterprise-grade setups an AKS cluster gets usually configured to authenticate users against Azure Entra ID and perform authorziation decisions based on the Kubernetes RBAC model.

This makes sense since Entra ID is usually the central identity provider with on-premises Active Directory, while the Kubernetes API still manages authorization decisions.

But have you ever wondered how the Azure Entra ID integration works and why the additional helper binary called kubelogin is required?

In this post, we'll look at Kubernetes and its authentication mechanism and see how it's connected and integrated into Entra ID. Let's get started!

How Kubernetes authentication works without Entra ID

As you might know, Kubernetes has no objects representing normal user accounts, unlike it has for service accounts.

Instead, any HTTP request hitting the Kubernetes API presenting a valid certificate signed by the cluster's CA is considered authenticated.

In this case, the user's identity is derived from the common name field in the subject of the certificate.

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 1234
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN = ca
        Validity
            Not Before: Jan 10 19:41:52 2024 GMT
            Not After : Jan 10 19:51:52 2026 GMT
        Subject: O = system:masters, CN = masterclient
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (4096 bit)
                Modulus: ...
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage:
                TLS Web Client Authentication
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Authority Key Identifier:
                keyid:...            
    Signature Algorithm: sha256WithRSAEncryption
         ...

A user certificate for the masterclient identity

After authentication, the Kubernetes RBAC sub-system takes care of authorization decisions and passes the request on to the admission controllers.

Kubernetes request processing

Running kubectl auth whoami, will return the following

ATTRIBUTE   VALUE
Username    masterclient
Groups      [system:masters system:authenticated]

The depicted user certificate from above has been taken and decoded from the kubeconfig file. It gets pulled and installed by Azure CLI, amongst a private key and the CA certificate, when you execute az aks get-credentials.

apiVersion: v1
clusters:
    - cluster:
        certificate-authority-data: <base64-encoded-ca-certificate>
        server: https://your-aks-cluster-dns-yd6y5kxt.hcp.switzerlandnorth.azmk8s.io:443
      name: your-aks-cluster
contexts:
    - context:
        cluster: your-aks-cluster
        user: clusterUser_your-resource-group_your-aks-cluster
      name: your-aks-cluster
current-context: your-aks-cluster
kind: Config
preferences: {}
users:
    - name: clusterUser_your-resource-group_your-aks-cluster
      user:
        client-certificate-data: <base64-encoded-user-certificate>
        client-key-data: <base64-encoded-user-key>
        token: <token>

.kube/config

The certificate-authority-data key holds the cluster certificate, while the client-certificate-data and client-key-data keys hold the user's certificate and corresponding private key.

Okay, this is all nice and swell 🧐, but how does Entra ID and kubelogin fit in here?

The thing with kubectl and kubelogin

Regarding authentication and authorization, OAuth 2.0 and OpenID Connect are the go-to protocols and defacto standards on the Internet.

However, these protocols are not natively understood by kubectl, which can only work with certificates and bearer tokens.

To be more precise, kubectl can attach a bearer token to its requests towards the Kubernetes API, but it can't fetch or refresh any bearer tokens.

⚠️ Please be aware that there are three projects on GitHub with the name kubelogin 🤯 This article refers to Microsoft implementation found here.

So this is why the kubelogin binary is required to execute any OAuth flows and pass the retrieved tokens on to kubectl. The following diagram gives a high-level overview of the process.

After executing az aks get-credentials on an Entra ID integrated cluster, your kubeconfig will carry a key called exec in a user object. This entry defines which client-go credential plugin to call and which arguments should be used.

apiVersion: v1
clusters: ...
contexts: ...
current-context: your-aks-cluster
kind: Config
preferences: {}
users:
    - name: clusterUser_your-resource-group_your-aks-cluster
      user:
        exec:
            apiVersion: client.authentication.k8s.io/v1beta1
            args:
                - get-token
                - --environment
                - AzurePublicCloud
                - --server-id
                - 6dae42f8-4368-4678-94ff-3960e28e3630
                - --client-id
                - 80faf920-1908-4b52-b5ef-a8e7bedfc67a
                - --tenant-id
                - <your-tenant-id>
                - --login
                - interactive
            command: kubelogin
            env: null
            installHint: ...
            provideClusterInfo: false

From the example above, the entire kubelogin , including its arguments, looks as follows.

kubelogin get-token \
  --environment AzurePublicCloud \
  --server-id 6dae42f8-4368-4678-94ff-3960e28e3630 \
  --client-id 80faf920-1908-4b52-b5ef-a8e7bedfc67a \
  --tenant-id <your-tenant-id> \
  --login interactive

These two Entra ID app registrations, client and server app, are managed by the AKS resource provider for you and configured when you execute:

az aks create -g <group> -n <cluster> --enable-aad --aad-admin-group-object-ids <id> [--aad-tenant-id <id>]

Okay, so kubelogin covers the client-side functionality of the authentication process. But how do things work on the Kubernetes API side, and what does the big picture look like?

Kubelogin, Entra ID & OpenID Connect

Before we get to the big picture, let's have a look the following diagram, that depicts the relation between the components and how they relate to each other in OAuth 2.0 terminology.

AKS & Azure Entra ID OAuth 2.0 relationship

The user (or resource owner) delegates the rights to access it's identifying data to the client, kubectl, and kubelogin.

Further, the client must be registered with Entra ID (the authorization server), which authorizes the token requests.

Last but not least, AKS, the resource server, has a trust relation to Azure Entra ID. This is required so that AKS can validate the received bearer tokens against Entra ID.

The entire picture

Now that we better understand the components, we can merge the diagrams and add more details.

The entire picture

Let's walk through each of the steps.

Steps 1, 2

The user executes kubectl which in turn invokes kubelogin with the server-id, client-id and tenant-id.

Steps 3 - 7

The browser opens, and the user is asked to authenticate. The credentials are verified, and an authorization code is requested by communicating with the Entra IDs authorization endpoint, which is then passed back to kubelogin.

Steps 8, 9, 10

Kubelogin exchanges the received authorization code for a bearer token by communicating with the token endpoint. Then, the token returns to kubectl, which attaches it to the users' HTTP request to the Kubernetes API.

GET /api/v1/namespaces/test/pods
Authorization: Bearer eyJ0eXAiOiJKV7QiLCJhbGciOiJSUzI1N...

Step 11

At this point, the Kubernetes API must check that the token signature is valid.

Therefore, it discovers the JWK URI endpoint by using a well-known URL and fetches the public key published at the jwks_uri key.

https://sts.windows.net/<tenant-id>/.well-known/openid-configuration

Then, it decodes the token and validates if...

  • ... the intended recipient match (aud audience claim)
  • ... the token time window is valid (exp expiration time, nbf not before)
  • ... the client ID matches (appid)

Step 12

After all authentication conditions are met, the user is considered authenticated, and the request is passed on to the kube-api's authorization decision...

Conclusion

Let's wrap up 📑

  • Kubernetes has no user objects, and they can't be created by the API.
  • The authentication mechanism uses OpenID Connect.
  • kubectl does not implement any OAuth flows.
  • A client-go exec plugin called kubelogin is required, which implements OAuth.
  • There are three different kubelogin projects on GitHub; don't get confused.
  • The client and server IDs are static and reused across multiple (all?) AKS deployments that are Entra ID integrated.
  • The client and server app registrations in Entra ID are managed for you by Microsoft.

That's it for today. I hope you enjoyed reading this article. Always happy to receive feedback! Happy hacking 👨🏽‍💻🤓

Further reading

Enable managed identity authentication on Azure Kubernetes Service - Azure Kubernetes Service
Learn how to enable Microsoft Entra ID on Azure Kubernetes Service with kubelogin and authenticate Azure users with credentials or managed roles.
GitHub - kubernetes/client-go: Go client for Kubernetes.
Go client for Kubernetes. Contribute to kubernetes/client-go development by creating an account on GitHub.
Introduction - Azure Kubelogin
A Kubernetes credential (exec) plugin implementing azure authentication
Authenticating
This page provides an overview of authentication. Users in Kubernetes All Kubernetes clusters have two categories of users: service accounts managed by Kubernetes, and normal users. It is assumed that a cluster-independent service manages normal users in the following ways: an administrator distribu…
OpenID Connect (OIDC) on the Microsoft identity platform - Microsoft identity platform
Sign in Microsoft Entra users by using the Microsoft identity platform’s implementation of the OpenID Connect extension to OAuth 2.0.