Nous avons vu dans un premier article les basiques et les prérequis pour intégrer l’authentification Azure Active Directory et la gestion RBAC sur un cluster AKS. Dans cet article, nous continurons notre exploration de l’utilisation d’Azure Active Directory (AAD) pour sécuriser AKS. Nous détaillerons les étapes de déploiement avec Terraform et les providers Azure et Kubernetes.
Cet article fait partie d’une série :
Déploiement d’un cluster AKS intégré à Azure AD
Maintenant que les prérequis sont en place au niveau Azure AD, nous pouvons déployer le cluster AKS, à
l’aide d’une config Terraform. Pour la ressource AKS, nous utilisons azurerm_kubernetes_cluster.
########################################################## #This module allows the creation of an AKS Cluster ################################################################
#Creating the AKS Cluster with RBAC Enabled and AAD integration resource "azurerm_kubernetes_cluster" "TerraAKSwithRBAC" { name = "${var.AKSClusName}" location bb = "${var.AKSLocation}" resource_group_name = "${var.AKSRGName}" agent_pool_profile { name = "${lower(var.AKSAgentPoolName)}" count = "${var.AKSNodeCount}" vm_size = "${var.AKSNodeInstanceType}" os_type = "${var.AKSNodeOSType}" os_disk_size_gb = "${var.AKSNodeOSDiskSize}" vnet_subnet_id = "${var.AKSSubnetId}" max_pods = "${var.AKSMaxPods}" } dns_prefix = "${lower(var.AKSprefix)}" service_principal { client_id = "${var.K8SSPId}" client_secret = "${var.K8SSPSecret}" } addon_profile { http_application_routing { enabled = "${var.IshttproutingEnabled}" } oms_agent { enabled = true log_analytics_workspace_id = "${lower(var.AKSLAWId)}" } } # kubernetes_version = "${var.KubeVersion}" linux_profile { admin_username = "${var.AKSAdminName}" ssh_key { key_data = "${var.PublicSSHKey}" } } network_profile { network_plugin = "azure" network_policy = "calico" dns_service_ip = "${cidrhost(var.AKSSVCCIDR, var.AKSDNSSVCIPModfier)}" docker_bridge_cidr = "${var.AKSDockerBridgeCIDR}" service_cidr = "${var.AKSSVCCIDR}" } role_based_access_control { enabled = true azure_active_directory { client_app_id = "${var.AADCliAppId}" server_app_id = "${var.AADServerAppId}" server_app_secret = "${var.AADServerAppSecret}" tenant_id = "${var.AADTenantId}" } } tags { Environment = "${var.EnvironmentTag}" Usage = "${var.EnvironmentUsageTag}" Owner = "${var.OwnerTag}" ProvisioningDate = "${var.ProvisioningDateTag}" } }
La partie importante pour l’intégration AAD est dans le bloc role_based_access_control. Evidemment, RBAC doit être activé, donc le paramètre enabled doit avoir la valeur true. En second lieu, nous devons référencer les applications préparées dans les sections précédentes, avec le secret pour l’application server, le app id pour les deux applications ainsi que le tenant Id Azure Active Directory.
Coder en dur ces informations n’est pas une bonne pratique, nous utilisons donc un Key Vault pour la valeur de ces variables à l’appel du module, comme affiché ci-dessous.
Pour sécuriser correctement l’accès au Key Vault, il convient bien entendu de définir une access policy qui ne donne à Terraform qu’un accès en lecture à l’application associée pour le déploiement.
module "AKSClus" { #Module Location source = "github.com/dfrappart/Terra-AZModuletest//Modules//44-2 AKS ClusterwithRBAC/" #Module variable AKSRGName = "${data.azurerm_resource_group.AKSRG.name}" AKSClusName = "${var.AKSClus}" AKSprefix = "${module.AKSClusterRandomPrefix.Result}" AKSLocation = "${var.AzureRegion}" AKSSubnetId = "${data.azurerm_subnet.AKSwithRBACSubnet.id}" K8SSPId = "${data.azurerm_key_vault_secret.AKSSP_AppId.value}" K8SSPSecret = "${data.azurerm_key_vault_secret.AKSSP_AppSecret.value}" AKSLAWId = "${data.azurerm_log_analytics_workspace.AKSLabWS.id}" AADTenantId = "${var.AzureTenantID}" AADServerAppSecret = "${data.azurerm_key_vault_secret.AKS_AADServer_AppSecret.value}" AADServerAppId = "${data.azurerm_key_vault_secret.AKS_AADServer_AppID.value}" AADCliAppId = "${data.azurerm_key_vault_secret.AKS_AADClient_AppId.value}" PublicSSHKey = "${data.azurerm_key_vault_secret.SSHPublicKey.value}" EnvironmentTag = "${var.EnvironmentTag}" EnvironmentUsageTag = "${var.EnvironmentUsageTag}" OwnerTag = "${var.OwnerTag}" ProvisioningDateTag = "${var.ProvisioningDateTag}" #the following parameters are optional because defined with default values AKSSVCCIDR = "172.19.0.0/16" AKSDockerBridgeCIDR = "172.17.0.1/16" }
Dans cet appel de module, nous pouvons également observer que le service principal, associé au cluster AKS pour lui permettre de provisionner des ressources de type public IP, Load Balancer Rules, etc., est également récupéré dans un Key Vault.
Test de l’authentification AAD
Récupération des identifiants administrateur et configuration des roles binding manuellement
Dans le cas d’un cluster AKS sans RBAC, il est possible de récupérer les identifiants AKS avec la commande az cli az aks get-credentials:
az aks get-credentials --resource-group myResourceGroup --name myAKSCluster
A la condition bien sûr que le client kubectl soit présent sur la station ou que la commande ait été exécutée, les informations d’authentification sont enregistrées dans le répertoire./kube, dans le fichier config :
PS C:\Users\David> ls .\.kube\ Répertoire : C:\Users\David\.kube Mode LastWriteTime Length Name ---- ------------- ------ ---- d----- 29/04/2019 15:04 cache d----- 29/04/2019 15:04 http-cache -a---- 14/05/2019 23:28 5546 config
Pour un cluster AKS avec RBAC activé, il faut ajouter le paramètre –admin pour créer le fichier config.
L’étape suivante est d’associer les identités AAD sur le cluster Kubernetes, à l’aide des objets Kubernetes Roles, ClusterRoles clusterRoleBinding ou RoleBinding. Dans notre cas, nous utilisons des rôles déjà existants :
- Le cluster-role cluster-admin, qui comme son nom l’indique donne des droits étendus sur l’ensemble du cluster,
- Le cluster-role admin qui donne des droits étendu mais qui s’associe à un namespace.
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: my-cluster-admins roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-admin subjects: - apiGroup: rbac.authorization.k8s.io kind: User name: "david@teknews.cloud
Pour effectuer l’attachement d’un utilisateur, nous pouvons utiliser son UPN, alors que pour un groupe, nous utilisons son object id:
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: my-cluster-admins roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-admin subjects: - apiGroup: rbac.authorization.k8s.io kind: Group name: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx"
Ceci étant, comme l’implique le chapitre, c’est une façon de faire, mais ce n’est pas celle que je préfère utiliser.
Récupération et des identifiants administrateur et configuration des roles binding
Nous allons profiter du provider Terraform pour Kubernetes pour créer les roles binding. Dans un premier temps, l’authentification sur un cluster AKS peut se faire en récupérant les outputs de la ressource azurerm_kubernetes_cluster, dans notre cas, ce sont les outputs du module AKS :
provider "kubernetes" { host = "${module.AKSClus.KubeAdminCFG_HostName}" client_certificate = "${base64decode(module.AKSClus.KubeAdminCFG_ClientCertificate)}" client_key = "${base64decode(module.AKSClus.KubeAdminCFG_ClientKey)}" cluster_ca_certificate = "${base64decode(module.AKSClus.KubeAdminCFG_ClusCACert)}" }
Ensuite nous utilisons la ressource kubernetes_cluster_role_binding pour attacher l’identité AAD désirée :
############################################################ # associate user & groups to cluster admin role resource "kubernetes_cluster_role_binding" "Terra_builtin_clubsteradmin_binding_user" { metadata { name = "terracreated-clusteradminrole-binding-user" } role_ref { api_group = "rbac.authorization.k8s.io" kind = "ClusterRole" name = "cluster-admin" } subject { api_group = "rbac.authorization.k8s.io" kind = "User" name = "${var.AKSClusterAdminUSer}" } }
Ensuite, pour réaliser une ségrégation des accès, nous allons donner à un groupe l’accès administrateur sur un namespace. Créons donc d’abord ce namespace :
############################################################ # Create namespace test resource "kubernetes_namespace" "terra_test_namespace" { metadata { annotations { name = "terra_test_namespace" usage = "for_test_namespace" } labels { namespacelabel = "testnamespace_label" } name = "terra-test-namespace" } }
Attachons ensuite ce groupe AAD au ClusterRole Admin associé au namespace créé :
############################################################ # bind namespace full admin role to AAD Group resource "kubernetes_role_binding" "terraadminnamspace" { metadata { name = "terransadminrolebinding" namespace = "${kubernetes_namespace.terra_test_namespace.metadata.0.name}" } role_ref { api_group = "rbac.authorization.k8s.io" kind = "ClusterRole" name = "admin" } subject { kind = "Group" name = "${var.AKSClusterAdminGroup}" api_group = "rbac.authorization.k8s.io" } }
Avec cette configuration Terraform, le groupe correspondant à la variable AKSClusterAdminGroup reçoit le rôle administrateur dans le namespace terra-test-namespace, qui est référencé avec la valeur ${kubernetes_namespace.terra_test_namespace.metadata.0.name}. Cette interpolation est effectivement plus longue que d’écrire simplement le nom du namespace, mais évite d’écrire en dur celui-ci, ce qui ne serait pas une bonne pratique.
Test d’authentification
Nous sommes maintenant prêts pour tester l’authentification sur le cluster AKS. La première étape est de récupérer les identifiants. Pour cela, nous utilisons la commande az aks get-credentials, après avoir effectué l’authentification sur az cli. Cela implique en l’état, que la personne a accès à la souscription dans laquelle est situé le cluster AKS. En revanche, une fois le fichier config récupéré, seul le client kubectl est requis.
Après avoir exécuté cette première commande, dans un premier temps avec un compte lié au cluster-role cluster-admin, nous exécutons ensuite la commande kubectl get nodes, qui requiert des droits au niveau du cluster, ce qui est bien le cas avec le compte présent :
PS C:\Users\David> az aks get-credentials -n AKSLabClusterwithRBAC -g RG_AKSManagedCluster --overwrite Merged "AKSLabClusterwithRBAC" as current context in C:\Users\David\.kube\config PS C:\Users\David> kubectl get nodes To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code CW48SF6FL to authenticate.
Ici, nous obtenons une url pour authentifier l’utilisateur sur AAD, avec un code à entrer sur la page d’authentification.
Après avoir entré le code, nous sélectionnons le compte AAD correspondant :
Puis le mot de passe :
La page suivante indique que l’authentification est effectuée pour l’application AKSAADClient, qui dispose elle-même des droits d’authentification sur l’application serveur :
Une fois l’authentification terminée, nous obtenons le résultat suivant pour la commande kubectl :
NAME STATUS ROLES AGE VERSION aks-terraaksap-18551064-0 NotReady agent 10d v1.12.7 aks-terraaksap-18551064-1 NotReady agent 10d v1.12.7 aks-terraaksap-18551064-2 NotReady agent 10d v1.12.7
A présent, testons une connexion avec un autre utilisateur, cette fois ci membre du groupe associé au rôle admin sur le namespace terra-test-namespace. Cette fois ci, nous utilisons le compte de Penny, sur lequel MFA dans AAD est activé :
PS C:\Users\David> kubectl get pods --namespace terra-test-namespace To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code CTU8ZT3BK to authenticate.
NAME READY STATUS RESTARTS AGE testnginxdeployment-6975459585-4stpg 1/1 Running 0 4h48m testnginxdeployment-6975459585-794gb 1/1 Running 0 4h48m testnginxdeployment-6975459585-n6h6r 1/1 Running 0 4h48m testnginxpod 1/1 Running 0 4h48m PS C:\Users\David> kubectl get nodes Error from server (Forbidden): nodes is forbidden: User "penny@teknews.cloud" cannot list resource "nodes" in API group "" at the cluster scope PS C:\Users\David> kubectl get services --namespace terra-test-namespace NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE testnginxsvc LoadBalancer 172.19.83.79 13.80.159.235 8080:32434/TCP 4h48m testnginxsvc2 LoadBalancer 172.19.137.138 13.94.205.224 8080:32601/TCP 4h49m PS C:\Users\David> kubectl get services Error from server (Forbidden): services is forbidden: User "penny@teknews.cloud" cannot list resource "services" in API group "" in the namespace "default" PS C:\Users\David>
Nous pouvons voir que la commande kubectl get pods, lancée avant la chaine d’authentification et scopée sur le namespace nous renvoie le résultat attendu. En revanche, nous avons une erreur lorsque la commande kubectl n’est pas scopée sur le namespace.
Pour conclure
Dans cet article, nous avons déployé un cluster AKS intégré à AAD pour mettre en œuvre un RBAC, puis nous avons testé avec succès l’authentification avec des utilisateurs AAD, n’ayant pas nécessairement des droits directs sur la ressource dans Azure. Dans un prochain article, nous mettrons en place la sécurisation au niveau du cluster AKS à l’aide des Network Policies.