AKS with AAD Integration and Kubernetes RBAC- Access kubernetes using service principal and kubeconfig

AKS with AAD Integration and Kubernetes RBAC- Access kubernetes using service principal and kubeconfig

Azure Kubernetes Service (AKS) provides below three options for Authentication and Authorization-

  • Azure AD Authentication with Kubernetes RBAC

  • Azure AD Authentication with Azure RBAC

  • Local Accounts with Kubernetes RBAC

In this blog, we will see how Azure AD Authentication with Kubernetes RBAC works for Azure AD service principal. With this configuration, user accounts/app registrations are maintained in the Azure active directory whereas their roles/permissions are managed within Kubernetes RBAC.

Follow below steps to setup a Azure AD service principal with Kubernetes RBAC and access Kubernetes resources using Python.

  1. Install required packages with the below command

    pip install kubernetes

    pip install azure-identity

    pip install azure-mgmt-containerservice

  2. Install Azure CLI. Then install kubelogin by running below command on Azure CLI terminal-

     az aks install-cli
    
  3. Create a service principal (app registration) in Azure AD. Get the client id and client secret for the same.

  4. Assign below Azure AD role to this service principal on AKS resource.

    Azure Kubernetes Service Cluster User

    This role is required in order to read kubeconfig file.

  5. Set up required Kubernetes RBAC role and role binding.

    • Create script for RBAC role with below code as- ClusterRole.yaml

        apiVersion: rbac.authorization.k8s.io/v1
        kind: ClusterRole
        metadata:
          name: aad-namespace-reader
        rules:
        - apiGroups: [""]
          resources: ["namespaces"]
          verbs: ["*"]
      

      Run below kubectl command on AKS cluster-

      kubectl apply -f clusterRole.yaml

    • Create script for cluster role binding using below code as- clusterRoleBinding.yaml

        apiVersion: rbac.authorization.k8s.io/v1
        kind: ClusterRoleBinding
        metadata:
          name: aad-namespace-reader-binding
        roleRef:
          apiGroup: rbac.authorization.k8s.io
          kind: ClusterRole
          name: aad-namespace-reader
        subjects:
        - apiGroup: rbac.authorization.k8s.io
          kind: User
          name: <service-principal-enterprise-app-object-id>
      

      In the above YAML file, we need to update <service-principal-enterprise-app-object-id> with the service principal enterprise app object id.

      To get this object id: Go to Azure active directory-- > Enterprise applications -- > Search for specific service principal -- > Copy object id

    • Run below kubectl command on AKS cluster-

      kubectl apply -f clusterRoleBinding.yaml

  6. Run below Python script to access AKS resources using a service principal. Steps followed in the script are as follows

    • Get ContainerServiceClient object for your AKS cluster using service principal credentials (client id-secret)

    • Using this client, get access profile for user account.

    • Extract the kubeconfig file content from the access profile. Note that this access profile belongs to local kubernetes user account, thus kubeconfig file also contains login details for local user account.

    • To convert this kubeconfig file with service principal login details, we need to run convert-kubeconfig command on kubelogin tool. To run this, we used a subprocess in Python script.

    • Once converted, use this kubeconfig file to create k8 client object from Kubernetes package. This object is created for service principal user with Azure AD authentication and Kubernetes RBAC role authorizations.

    • With this k8 object, you can fetch required resources which is namespace in this case.

    from azure.identity import ClientSecretCredential
    from azure.mgmt.containerservice import ContainerServiceClient
    from kubernetes import client, config
    import yaml
    import json
    import os

    # make sure your service principal has role assigned on AKS as- Azure Kubernetes Service Cluster User
    # this role is required in order to read kubeconfig 

    credentials = ClientSecretCredential(
            tenant_id="",       # set your azure tenant id 
            client_id="",       # set client id value
            client_secret=""    # set client secret value
        )

        # Instantiate the AKS client
    aks_client = ContainerServiceClient(credentials, "<azure subscription id>") # specify azure subscription id

    # get access profile
    access_profile = aks_client.managed_clusters.list_cluster_user_credentials(
        "<Azure resource group name>", # set azure resource group name where AKS is created
        "<AKS resource name>"  # set name of AKS resource
    )

    # # Extract the kubeconfig file content from the access profile
    kube_config_bytes = access_profile.kubeconfigs[0].value
    kube_config_str = kube_config_bytes.decode("utf-8")
    kube_config = yaml.safe_load(kube_config_str)

    configfile = "customkubeconfig"


    with open(configfile, 'w') as file:
        json.dump(kube_config, file)

    # Get the current directory path
    current_dir = os.getcwd()

    # Construct the file path by joining the current directory path and the Filelocation
    file_path = os.path.join(current_dir, configfile) 

    # currently extracted kubeconfig file from AKS contains kubernetese local user accounts and not service principal that we want to use to access.
    # Hence, we need to modify it using kubelogin command line tool. We need to execute convert-kubeconfig command with appropiate parameters.
    # to execute, this command from python script, we have used subprocess.
    # make sure kubelogin is installed on your machine correctly.
    # reference - https://azure.github.io/kubelogin/cli/convert-kubeconfig.html#:~:text=covert%2Dkubeconfig,Exec%20plugin%20will%20be%20converted.

    # Define the command
    import subprocess
    command = [
        "kubelogin",
        "convert-kubeconfig",
        "-l",
        "spn",
        "--client-id",
        "<client id value>",
        "--client-secret",
        "<client secret value>",
        "--kubeconfig",
        file_path
    ]
    # Execute the command
    subprocess.run(command, check=True)

    # Load converted kubeconfig directly from dictionary
    config.load_kube_config(config_file=file_path)
    #        config.kube_config.load_kube_config_from_dict(kube_config)

    # After loading kubeconfig, we can delete the local file, its optional
    if os.path.exists(file_path):
        # Delete the file
        os.remove(file_path)
        print("File deleted successfully.")
    else:
        print("File does not exist.")

    # Create the API client
    k8s_client = client.CoreV1Api()

    # Get the list of namespaces for the cluster
    namespaces = k8s_client.list_namespace().items  

    # Print the list of namespaces
    for namespace in namespaces:
        print(namespace.metadata.name)

Conclusion

In this blog, we understood how Azure Kubernetes Service works with AAD and Kubernetes RBAC for service principal.

Thanks for reading.

Keep learning!

Did you find this article valuable?

Support Azure Developer's Blog by becoming a sponsor. Any amount is appreciated!