diff --git a/python-clusters/attach-aks-cluster/cluster.py b/python-clusters/attach-aks-cluster/cluster.py index f483452..454bc5c 100644 --- a/python-clusters/attach-aks-cluster/cluster.py +++ b/python-clusters/attach-aks-cluster/cluster.py @@ -6,7 +6,7 @@ from dku_utils.cluster import make_overrides, get_subscription_id from dku_azure.auth import get_credentials_from_connection_info, get_credentials_from_connection_infoV2 from dku_azure.utils import run_and_process_cloud_error -from dku_azure.utils import run_and_process_cloud_error, get_instance_metadata, get_subscription_id +from dku_azure.utils import run_and_process_cloud_error, get_instance_metadata, get_subscription_id, patch_kube_config_with_aad class MyCluster(Cluster): def __init__(self, cluster_id, cluster_name, config, plugin_config): @@ -30,6 +30,13 @@ def _get_credentials(self): def start(self): credentials, subscription_id = self._get_credentials() + connection_info = self.config.get("connectionInfoV2",{"identityType":"default"}) + identity_type = connection_info.get("identityType", "default") + identity_label = None + if identity_type == 'user-assigned': + identity_label = connection_info.get("userManagedIdentityId", "") + elif identity_type == 'service-principal': + identity_label = connection_info.get("clientId", "") # Cluster name cluster_name = self.config.get("cluster", None) @@ -46,16 +53,28 @@ def start(self): clusters_client = ContainerServiceClient(credentials, subscription_id) + # Retrieve the cluster to check whether or not the Azure AD with Azure RBAC is activated + def do_get(): + return clusters_client.managed_clusters.get(resource_group, cluster_name) + get_cluster_result = run_and_process_cloud_error(do_get) + print("Amandine - managed: {}".format(get_cluster_result.aad_profile.managed)) + patch_user_in_kubeconfig = get_cluster_result.aad_profile.managed + # Get kubeconfig logging.info("Fetching kubeconfig for cluster %s in %s", cluster_name, resource_group) def do_fetch(): return clusters_client.managed_clusters.list_cluster_admin_credentials(resource_group, cluster_name) get_credentials_result = run_and_process_cloud_error(do_fetch) + kube_config_content = get_credentials_result.kubeconfigs[0].value.decode('utf8') kube_config_path = os.path.join(os.getcwd(), 'kube_config') + if patch_user_in_kubeconfig: + kube_config_yaml = patch_kube_config_with_aad(kube_config_content, identity_type, identity_label) + else: + kube_config_yaml = yaml.safe_load(kube_config_content) with open(kube_config_path, 'w') as f: - f.write(kube_config_content) - overrides = make_overrides(self.config, yaml.safe_load(kube_config_content), kube_config_path) + yaml.dump(kube_config_yaml, f) + overrides = make_overrides(self.config, kube_config_yaml, kube_config_path) # Get other cluster infos def do_inspect(): diff --git a/python-clusters/create-aks-cluster/cluster.py b/python-clusters/create-aks-cluster/cluster.py index e13542b..c3b0ecb 100644 --- a/python-clusters/create-aks-cluster/cluster.py +++ b/python-clusters/create-aks-cluster/cluster.py @@ -14,7 +14,7 @@ from dku_kube.nvidia_utils import add_gpu_driver_if_needed from dku_azure.auth import get_credentials_from_connection_info, get_credentials_from_connection_infoV2 from dku_azure.clusters import ClusterBuilder -from dku_azure.utils import run_and_process_cloud_error, get_subnet_id, get_instance_metadata, get_subscription_id +from dku_azure.utils import run_and_process_cloud_error, get_subnet_id, get_instance_metadata, get_subscription_id, patch_kube_config_with_aad from dku_azure.auth import AzureIdentityCredentialAdapter class MyCluster(Cluster): @@ -43,6 +43,13 @@ def start(self): Build the create cluster request. """ credentials, subscription_id, managed_identity_id = self._get_credentials() + connection_info = self.config.get("connectionInfoV2",{"identityType":"default"}) + identity_type = connection_info.get("identityType", "default") + identity_label = None + if identity_type == 'user-assigned': + identity_label = connection_info.get("userManagedIdentityId", "") + elif identity_type == 'service-principal': + identity_label = connection_info.get("clientId", "") # Fetch metadata about the instance metadata = get_instance_metadata() @@ -385,12 +392,13 @@ def do_fetch(): kube_config_content = get_credentials_result.kubeconfigs[0].value.decode("utf8") logging.info("Writing kubeconfig file...") kube_config_path = os.path.join(os.getcwd(), "kube_config") + kube_config_yaml = patch_kube_config_with_aad(kube_config_content, identity_type, identity_label) with open(kube_config_path, 'w') as f: - f.write(kube_config_content) + yaml.dump(kube_config_yaml, f) overrides = make_overrides( self.config, - yaml.safe_load(kube_config_content), + kube_config_yaml, kube_config_path, acr_name = None if _is_none_or_blank(acr_attachment) else acr_attachment["name"], ) diff --git a/python-lib/dku_azure/clusters.py b/python-lib/dku_azure/clusters.py index 71faf77..7106736 100644 --- a/python-lib/dku_azure/clusters.py +++ b/python-lib/dku_azure/clusters.py @@ -1,6 +1,6 @@ from dku_azure.utils import get_instance_metadata, get_vm_resource_id, get_host_network, get_subnet_id from azure.mgmt.containerservice.models import ManagedClusterAgentPoolProfile, ManagedClusterAPIServerAccessProfile, ManagedClusterServicePrincipalProfile -from azure.mgmt.containerservice.models import ContainerServiceNetworkProfile, ManagedCluster, ManagedClusterIdentity +from azure.mgmt.containerservice.models import ContainerServiceNetworkProfile, ManagedCluster, ManagedClusterAADProfile from dku_utils.access import _default_if_blank, _merge_objects, _print_as_json import logging, copy, json @@ -147,6 +147,7 @@ def build(self): cluster_params["node_resource_group"] = self.node_resource_group cluster_params["service_principal_profile"] = self.cluster_sp cluster_params["identity"] = self.identity + cluster_params["aad_profile"] = ManagedClusterAADProfile(managed=True, enable_azure_rbac=True) cluster_params["identity_profile"] = self.identity_profile cluster_params["kubernetes_version"] = self.cluster_version cluster_params["agent_pool_profiles"] = self.node_pools diff --git a/python-lib/dku_azure/utils.py b/python-lib/dku_azure/utils.py index 6c63412..2b9083b 100644 --- a/python-lib/dku_azure/utils.py +++ b/python-lib/dku_azure/utils.py @@ -1,5 +1,5 @@ import requests -import json +import yaml import logging from msrestazure.azure_exceptions import CloudError @@ -88,3 +88,45 @@ def get_host_network(credentials=None, resource_group=None, connection_info=None logging.info("VNET: {}".format(vnet)) logging.info("SUBNET ID: {}".format(subnet_id)) return vnet, subnet_id + +def patch_kube_config_with_aad(kube_config_string, authentication_mode, identity_label): + kube_config = yaml.safe_load(kube_config_string) + + kubelogin_command_options = ['get-token', '--login', 'msi', '--server-id', '6dae42f8-4368-4678-94ff-3960e28e3630'] + + additional_options = [] + if authentication_mode == 'service-principal': + additional_options = ['--identity-resource-id', identity_label] + elif authentication_mode == 'user-assigned': + if identity_label.startswith("/"): + additional_options = ['--identity-resource-id', identity_label] + else: + additional_options = ['--client-id', identity_label] + + kubelogin_command_options = kubelogin_command_options + additional_options + + kube_config_user = { + 'name': kube_config['contexts'][0]['context']['user'], + 'user': { + 'exec': { + 'apiVersion': 'client.authentication.k8s.io/v1beta1', + 'command': 'kubelogin', + 'args': kubelogin_command_options + } + } + } + + kube_config['users'] = [kube_config_user] + + return kube_config + + # determine which is the authentication mode + # depending on the authentication mode, the derivation command kubelogin options will be different + # + # SERVICE-PRINCIPAL + # kubelogin get-token -l msi --server-id 6dae42f8-4368-4678-94ff-3960e28e3630 --identity-resource-id /subscriptions/8c59bf15-b4a9-4398-a354-d2a4e7d60e2a/resourcegroups/jcasoli-fm/providers/Microsoft.ManagedIdentity/userAssignedIdentities/dss-id-jcasoli + # USER-ASSIGNED + # kubelogin get-token -l msi --server-id 6dae42f8-4368-4678-94ff-3960e28e3630 --client-id de4cab2c-359c-4970-8032-89bee12a7d31 + # DEFAULT + # kubelogin get-token -l msi --server-id 6dae42f8-4368-4678-94ff-3960e28e3630 +