Passer au contenu

Sécuriser AKS au déploiement #1

Cet article est le premier d’une série de trois articles, où nous jetterons un œil sur Azure Kubernetes Service (AKS), comment le déployer et comment sécuriser son usage.

Dans ce premier article, nous allons rappeler quelques basiques et revoir les prérequis pour intégrer l’authentification Azure Active Directory et la gestion RBAC sur un cluster AKS.

Cet article fait partie d’une série :

Sécuriser AKS au déploiement

AKS – Rappel et déploiement basique

Nous ne passerons pas trop de temps sur la présentation de AKS, le service jouissant d’une grande popularité depuis ces derniers mois.
Pour résumer, AKS est le nouveau service d’orchestration de container managé de Microsoft. Il remplace progressivement Azure Container service et se focalise uniquement sur le moteur d’orchestration de la Cloud Native Computing foundation (CNCF) Kubernetes. Devant la montée en puissance de cette solution d’orchestration, Microsoft, comme tous les Cloud provider et beaucoup d’éditeur, propose sa version managée de Kubernetes dans la plateforme Azure. La documentation Azure explique très bien comment déployer un cluster AKS avec quelques commandes az cli :

az group create --name myResourceGroup --location eastus

az aks create \

--resource-group myResourceGroup \
--name myAKSCluster \
--node-count 1 \
--enable-addons monitoring \
--generate-ssh-keys

Bien que très utile, cette commande dissimule pas mal de choses qui s’opèrent en arrière-plan.
On ne sait pas en effet où le cluster est déployé, ni comment le réseau est configuré. Par défaut, le plugin réseau utilisé est kubenet, ce qui est bien pour du test mais ne nous permet pas de tester toutes les possibilités de AKS.

Dans cet article, prenons quelques hypothèses :

  • Le réseau Azure sous-jacent est déjà en place
  • Nous utiliserons le Azure CNI pour notre cluster AKS

Disposer d’un VNet est une étape essentielle avant le déploiement du cluster AKS. La raison principale étant étroitement liée au choix du CNI. Avec le CNI Azure, les nodes Kubernetes mais également les pods s’appuient sur les adresses IP privées du VNet et plus particulièrement du subnet cible pour le déploiement.

De fait, pour déployer un cluster AKS en mesure d’héberger un nombre approprié de workloads (i.e. pods), le design du réseau devrait être réalisé méticuleusement afin de fournir un espace d’adressage IP suffisant pour le cluster AKS. La documentation Azure, encore une fois très complète, nous donne la formule suivante pour calculer la taille minimale du subnet cible pour un cluster AKS, en fonction du nombre de workloads :

(number of nodes + 1) + ((number of nodes + 1) * maximum pods per node that you configure)

Ce qui donne, pour un cluster de 50 nodes :

(51) + (51 * 30 (default)) = 1,581

A noter, que nous avons un nombre maximum de pods par nodes configuré à 30 par défaut avec Azure CNI. Fort heureusement, il s’agit d’une soft limit qui peut être modifiée à 110 pods par node, soit au moment du déploiement, soit en action après déploiement avec az cli par exemple.

Avec le Azure CNI, nous pouvons également utiliser les Network Policies dans Kubernetes. Dans la mesure où nous souhaitons sécuriser le cluster AKS lors de son déploiement, les network policies sont requises.

Intégration de AKS avec Azure AD

Prérequis à l’intégration

Dans la mesure où AKS est un service géré par Microsoft, il fournit quelques fonctionnalités intéréssantes comme l’intégration avec Azure Active Directory.
Pour une société utilisant déjà Azure AD comme source d’identité, soit à partir d’une synchronisation avec un LDAP on premise, soit en mode Cloud Native, la possibilité d’utiliser directement Azure AD pour authentifier les utilisateurs AKS est un gros avantage.
De plus, de par les capacités d’Azure AD de forcer ou non une authentification en plusieurs étapes (Multi Factor Authentication ou MFA), un utilisateur avec MFA activé sera contraint d’utiliser son device d’authentification pour accéder à AKS. Bien qu’un peu plus contraignant, l’utilisation de MFA est à prendre comme une bonne pratique.

Création des éléments requis

1) Revue des prérequis

L’authentification AAD sur un cluster AKS s’appuie sur OpenID Connect. Sans rentrer trop dans les détails ici, résumons comment cela fonctionne :

Une application enregistrée sur AAD est requise. Cette application, qualifiée d’application serveur, est associée au cluster AKS et est utilisée pour récupérer les appartenances de groupes des utilisateurs AAD.

Pour pouvoir réaliser cette fonction, l’application a besoin de disposer de droits sur l’API Microsoft Graph :
-Application access: Read directory data
-Delegated permissions: Sign in and read user profile and Read Directory data

A travers cette application et le service principal associé, le cluster AKS devient capable de vérifier l’identité de l’utilisateur s’authentifiant. A ce stade, cependant, aucun accès n’est donné à l’utilisateur sur le cluster AKS. Une seconde application, qualifiée cliente, accédant à la première application est requise. Cette application est de type « Native App » et accède à la première application.

Pour créer les applications, nous pouvons utiliser soit le portail Azure, soit l’az cli ou PowerShell, soit partiellement le provider Terraform Azure AD. Jetons un œil aux détails.

2) Réunion des prérequis

Comme nous l’avons dit, la création des applications et des service principals peut être effectué à travers le portail. Un fait marquant cependant, bien que la documentation Azure soit très détaillée, elle fait référence à la section du portail Azure AD « Legacy » qui devrait être abandonnée à fin mai. Avec le portail « Legacy », il est relativement facile de discerner une application native d’une application https, mais un peu moins avec le nouveau portail. D’un autre côté, cela reste très simple avec az cli. Notre choix est donc fait.Prenons l’hypothèse que nous voulons appeler l’application serveur K8SSRV et l’application client K8SCli :

PS C:\Users\David> $serverApplicationId=$(az ad app create --display-name K8SSRV --
identifier-uris "https://K8SSRV.teknews.cloud" --query appId -o tsv)

L’application serveur doit pouvoir assumer l’identité de l’utilisateur Azure AD, nous devons donc modifier le manifest de l’application comme affiché ci-dessous :

{
"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx",
"acceptMappedClaims": null,
"accessTokenAcceptedVersion": null,
"addIns": [],
"allowPublicClient": null,
"appId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx",
"appRoles": [],
"oauth2AllowUrlPathMatching": false,
"createdDateTime": "2019-05-16T10:06:34Z",
"groupMembershipClaims": "All",
"identifierUris": [
"https://K8SSRV.teknews.cloud"
],

En az cli, cela s’effectue avec la commande suivante :

PS C:\Users\David> az ad app update --id $serverApplicationId --set
groupMembershipClaims=All

Nous créons ensuite le service principal associé à l’application :

PS C:\Users\David> az ad sp create --id $serverApplicationId
{
"accountEnabled": "True",
"addIns": [],
"alternativeNames": [],
"appDisplayName": "K8SSRV",
"appId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx",
"appOwnerTenantId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx",
"appRoleAssignmentRequired": false,
"appRoles": [],
"applicationTemplateId": null,
"deletionTimestamp": null,
"displayName": "K8SSRV"
"errorUrl": null,
"homepage": null,
"informationalUrls": {
"marketing": null,
"privacy": null,
"support": null,
"termsOfService": null
},
"keyCredentials": [],
"logoutUrl": null,
"notificationEmailAddresses": [],
"oauth2Permissions": [
{
"adminConsentDescription": "Allow the application to access K8SSRV on behalf of the signed-in user.",
"adminConsentDisplayName": "Access K8SSRV",
"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx",
"isEnabled": true,
"type": "User",
"userConsentDescription": "Allow the application to access K8SSRV on your behalf.",
"userConsentDisplayName": "Access K8SSRV",
"value": "user_impersonation"
}
],
"objectId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx",
"objectType": "ServicePrincipal",
"odata.metadata": "https://graph.windows.net/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/$metadata#directoryObjects/@Element",
"odata.type": "Microsoft.DirectoryServices.ServicePrincipal",
"passwordCredentials": [],
"preferredSingleSignOnMode": null,
"preferredTokenSigningKeyEndDateTime": null,
"preferredTokenSigningKeyThumbprint": null,
"publisherName": "dfitc",
"replyUrls": [],
"samlMetadataUrl": null,
"samlSingleSignOnSettings": null,
"servicePrincipalNames": [
"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx", "https://K8SSRV.teknews.cloud"
],
"servicePrincipalType": "Application",
"signInAudience": "AzureADMyOrg",
"tags": []
"tokenEncryptionKeyId": null
}

Nous avons besoin du secret de l’application pour le cluster AKS, que nous récupérons via la commande suivante :

PS C:\Users\David> $serverApplicationSecret=$(az ad sp credential reset --name $serverApplicationId --credential-description K8SSRVPwd --query password -o tsv)

L’application requiert maintenant l’autorisation d’accès à l’API Microsoft Graph, que nous lui accordons comme suit. Afin de passer cette commande, l’az cli doit être exécutée avec un compte Azure AD qui dispose des droits admin globaux :

PS C:\Users\David> az ad app permission add --id $serverApplicationId --api 00000003-0000-0000-c000-000000000000 --api-permissions e1fe6dd8-ba31-4d61-89e7-88639da4683d=Scope 06da0dbc-49e2-44d2-8312-53f166ab848a=Scope 7ab1d382-f21e-4acd-a863-ba3e13f7da61=Role
Invoking "az ad app permission grant --id xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx --api 00000003-0000-0000-c000-000000000000" is needed to make the change effective
PS C:\Users\David> az ad app permission grant --id $serverApplicationId --api 00000003-0000-0000-c000-000000000000
{
"clientId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx",
"consentType": "AllPrincipals",
"expiryTime": "2020-05-16T10:22:24.551805",
"objectId": "KGEOWvQaWkSZ88psknWSqpsVrtuS4RhJpdS6UkfUDCU",
"odata.metadata": "https://graph.windows.net/e0c45235-95fe-4bd6-96ca-2d529f0ebde4/$metadata#oauth2PermissionGrants/@Element",
"odatatype": null,
"principalId": null,
"resourceId": "dbae159b-e192-4918-a5d4-ba5247d40c25",
"scope": "user_impersonation",
"startTime": "2019-05-16T10:22:24.551805"
}
PS C:\Users\David> az ad app permission admin-consent --id $serverApplicationId

L’application cliente est créée avec la commande suivante :

PS C:\Users\David> $clientApplicationId=$(az ad app create --display-name K8SCli --native-app --reply-urls "https://K8SCli.teknews.cloud" --query appId -o tsv)
PS C:\Users\David> az ad sp create --id $clientApplicationId
{
"accountEnabled": "True",
"addIns": [],
"alternativeNames": [],
"appDisplayName": "K8SCli",
"appId": "08bd0fed-45b6-4c91-b264-3756adfd314f",
"appOwnerTenantId": "e0c45235-95fe-4bd6-96ca-2d529f0ebde4",
"appRoleAssignmentRequired": false,
"appRoles": [],
"applicationTemplateId": null,
"deletionTimestamp": null,
"displayName": "K8SCli",
"errorUrl": null,
"homepage": null,
"informationalUrls": {
"marketing": null,
"privacy": null,
"support": null,
"termsOfService": null
},
"keyCredentials": [],
"logoutUrl": null,
"notificationEmailAddresses": [],
"oauth2Permissions": [
{
"adminConsentDescription": "Allow the application to access K8SCli on behalf of the signed-in user.",
"adminConsentDisplayName": "Access K8SCli",
"id": "5189cc1f-f58f-463c-b659-857245cb39e7",
"isEnabled": true,
"type": "User",
"userConsentDescription": "Allow the application to access K8SCli on your behalf.",
"userConsentDisplayName": "Access K8SCli",
"value": "user_impersonation"
}
],
"objectId": "f600f073-273b-46a0-9c05-325412b0f9dd",
"objectType": "ServicePrincipal",
"odata.metadata": "https://graph.windows.net/e0c45235-95fe-4bd6-96ca
2d529f0ebde4/$metadata#directoryObjects/@Element",
"odata.type": "Microsoft.DirectoryServices.ServicePrincipal",
"passwordCredentials": [],
"preferredSingleSignOnMode": null,
"preferredTokenSigningKeyEndDateTime": null,
"preferredTokenSigningKeyThumbprint": null,
"publisherName": "dfitc",
"replyUrls": [
"https://K8SCli.teknews.cloud"
],
"samlMetadataUrl": null,"samlSingleSignOnSettings": null,
"servicePrincipalNames": [
"08bd0fed-45b6-4c91-b264-3756adfd314f"
],
"servicePrincipalType": "Application",
"signInAudience": "AzureADMyOrg",
"tags": [],
"tokenEncryptionKeyId": null
}


Pour donner les autorisations requises à l’application cliente sur l’application serveur, nous utilisons les commandes suivantes :

PS C:\Users\David> $oAuthPermissionId=(az ad app show --id $serverApplicationId --query oauth2Permissions[0].id)
PS C:\Users\David> $oAuthPermissionId
"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx"
PS C:\Users\David> az ad app permission add --id $clientApplicationId --api $serverApplicationId --api-permissions $oAuthPermissionId=Scope
Invoking "az ad app permission grant --id xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx f --api xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx" is needed to make the change effective
PS C:\Users\David> az ad app permission grant --id $clientApplicationId --api $serverApplicationId
{
"clientId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx",
"consentType": "AllPrincipals",
"expiryTime": "2020-05-16T11:26:31.055843",
"objectId": "c_AA9jsnoEacBTJUErD53ShhDlr0GlpEmfPKbJJ1kqo",
"odata.metadata": "https://graph.windows.net/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/$metadata#oauth2PermissionGrants/@Element",
"odatatype": null,
"principalId": null,
"resourceId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx",
"scope": "user_impersonation",
"startTime": "2019-05-16T11:26:31.055843"
}

Pour conclure

Dans cette première partie, nous avons réuni les prérequis pour utiliser Azure AD comme source d’identité sur un cluster AKS. Dans un prochain article, nous utiliserons Terraform pour déployer un cluster AKS utilisant les services principals créés.