Passer

Voyage vers un AKS sécurisé : Les Pod Identities

Dans cet article, nous allons explorer comment implémenter et utiliser la solution basée sur le projet Open Source Pod Identity.
Le projet, comme tout bon projet OSS, dispose de son propre site de documentation. Augmenté d’un certain nombre de publications, il y a beaucoup de matière pour commencer.
D’un autre côté, la compréhension des prérequis et la mise en œuvre sont assez complexes malgré tout ;
et cet article constitue mon propre guide pour le déploiement de la fonctionnalité dans un cluster AKS.
J’espère que cela vous sera utile.

Partir du besoin

Avant de plonger dans le cœur du sujet, apportons un peu de contexte.
Prenons une application, hébergée sur un serveur, qui doit accéder à une base de données par exemple.
Habituellement, nous aurions une connexion string qui s’appuie sur un compte de service pour l’accès à cette base de
données. Et c’est là que commence les difficultés. Ce compte de service doit être déclaré sur le serveur, avec son mot de passe associé. Il y a donc 2 choses importantes ici :

  • Est-ce que le mot de passe est stocké de manière sécurisée ?
  • Comment effectuons-nous la rotation du mot de passe ?

Front Layer au Data LayerSans entrer dès maintenant dans les détails, nous allons avoir exactement le même problème dans un environnement containerisé.
Pour le moment, disons simplement que les Cloud Platforms comme Azure viennent avec une proposition pour résoudre ce challenge technique. Dans le cas d’Azure, la solution apportée s’appelle « Managed Identity ».

Concepts de base de Azure Managed Identity

Nous partons donc à présent du postulat que l’ensemble applicatif qui nous intéresse vit entièrement dans Azure. Nous avons également défini que notre front-end applicatif vit sur une machine virtuelle Azure et qu’il doit être en mesure de joindre un service de base de données de type PaaS. Comme chacun sait, IAM dans Azure dépend de Azure Active Directory. Lorsque l’on doit accéder à une ressource,nous configurons une assignation RBAC à une identité Azure AD, celle-ci étant possiblement un utilisateur, un groupe ou encore une Application Registration. Le groupe mis à part, ces entités utilisent un secret pour l’authentification. De fait, la migration dans le Cloud n’a pas l’air de changer les choses pour le moment.

C’est ici que nous introduisons le concept des identités managées. Une identité managée, comme son
nom l’indique, est gérée par Azure. Ce qui signifie que nous ne nous occupons plus de cette identité, d’un
point de vue gestion du secret. Ce point est pris en charge par la plateforme Azure en backend. Il ne
reste que la partie RBAC et l’assignation du niveau d’autorisation approprié.
Dans notre cas, notre frontend, qui vit sur une Virtual Machine Azure, recevrait donc une identité
managée associée à la VM qui, à son tour, reçoit des autorisations d’accès au service Azure Database à
travers une assignation RBAC. En un mot, génial !

A présent, pour finaliser la définition des concepts, les identités managées apparaissent sous 2 types :

  • La System Assigned Identity (SAI) qui est directement associée avec les services Azure comme les VMs ou les fonctions ;
  • La User Assigned Identity (UAI) qui est créée au préalable dans Azure et associée à un service éligible dans un second temps.

Une seule SAI peut être associée à un service alors que plusieurs UAI peuvent être associées à un même service (ou différents services le cas échéant).

Une seule SAI peut être associée à un servicePlusieurs UAI peuvent être associées à un même service

Regardons, à présent, comment cela se retranscrit d’un point de vue containerisé.

Là où les containers n’aident pas

Commençons par un disclaimer : il est vrai que les containers peuvent aider énormément dans un cycle de vie applicatif. Cependant, d’un point de vue technique, la propriété d’un container à isoler un runtime engendre un effet blackbox pour système hôte. Encore une fois, cela apporte un certain nombre d’avantages, le premier étant la décorrélation de l’application avec le système d’exploitation.

Container main property

Toutefois, dans notre cas, en isolant le runtime applicatif, nous l’isolons également du reste de la
plateforme Azure, ce qui implique une impossibilité d’utiliser les identités managées.
Il y a bien entendu des exceptions, par exemple, les Azure Container Instances peuvent nativement être
associées à des identités managées. Cela étant, il s’agit d’un service de container « natif » d’Azure donc
un cas différent d’un « Bring Your Own Container » qui, lui, ne peut s’intégrer directement avec les
identités managées.

Et nous sommes (enfin) à la fin de notre mise en contexte. Sachant qu’aujourd’hui, la solution préférée
en termes d’hébergement de micro-services est Kubernetes, nous allons regarder spécifiquement
comment nous pouvons bénéficier des identités managées avec des Pods dans Azure Kubernetes
Services.

A propos de Pod Identity pour AKS

Initialement Pod Identity est avant tout un projet Open source, de fait sans support officiel de Microsoft.
Il est à présent possible, dans une preview, de disposer de la fonctionnalité à travers un add-on AKS.
Donc, à termes, il devrait être possible d’avoir Pod Identity ET le support de Microsoft.

En tant que projet Open Source, Pod Identity vit sur GitHub, bien entendu. Et il est possible de trouver les explications sur les concepts de la technologie sur le site associé.

Sans avoir la prétention de réécrire la documentation, prenons le temps d’expliquer quelques concepts.

L’objectif est d’attacher une identité managée à un pod et d’utiliser ensuite cette identité pour avoir accès à des ressources Azure externe au cluster AKS. Comme le pod n’est pas par design une ressource Azure, une UAI est la parfaite candidate pour notre objectif. La difficulté est de faire correspondre cette UAI du plan de contrôle Azure avec le plan de contrôle Kubernetes. Pour cela Pod Identity repose sur les objets suivants :

  • AzureIdentity qui est dans notre cas l’AUI vivant dans Azure déclarée dans le plan de contrôle Kubernetes ;
  • AzureIdentityBinding qui est l’objet de l’API Kubernetes pour lier un pod à une AzureIdentity (et donc une UAI) au moyen d’étiquettes. Nous verrons cela dans les détails par la suite ;
  • AzureAssignedIdentity qui est un objet dans Kubernetes décrivant l’état de relation entre les
    deux objets précédents.

Azure Control plane

Ces 3 objets sont ce que l’on appelle, dans la terminologie Kubernetes, des Custom Resource Definition
(CRD). Pod Identity s’appuie ensuite sur des composants de base :

Pod Identity s’appuie sur des composants de baseSource : https://azure.github.io/aad-pod-identity/docs/concepts/block-diagram-and-design/

Comme on peut le voir sur cette image prise sur la documentation de Pod Identity, il y a 2 objets dédiés à l’infrastructure :

  • Un deployment appelé MIC (Managed Identity Controller) qui déploie des pods chargés de surveiller les pods et d’attribuer des identités gérées ;
  • Un daemonset appelé NMI (Node Managed Identity), qui parle au MIC via le serveur API lorsqu’un Pod nécessite un jeton via une identité gérée.

En faisant abstraction du plan de contrôle Kubernetes nous avons un schéma comme ci-dessous :

Pod requests

Déploiement de l’infrastructure pour Pod Identity

Après la description des concepts, place à un peu de pratique. Nous commençons avec un cluster AKS déjà déployé avec :

  • Managed Azure AD integration
  • RBAC enabled

Pour déployer le cluster, en ce qui me concerne, j’ai déployé à travers Terraform. Le module est d’ailleurs disponible sur le repo GitHub suivant pour les curieux. A travers une déclaration d’outputs Terraform approprié, il est aisé de récupérer les informations requises pour la post configuration AKS (ou pré configuration Pod Identity).

La documentation Pod Identity spécifie des besoins d’assignations RBAC. Je vous recommande de lire
soigneusement ces prérequis pour éviter l’effet « trial and error » qui peut être fastidieux (« Mais qui
ferait ca ? » me diriez-vous.).

Les rôles à assigner sont les suivants :

  • Managed Identity Operator
  • Virtual Machine Contributor

La question légitime est « Qui doit recevoir ces rôles ? ». C’est ici que l’on tire avantage des output via terraform. J’ai utilisé les outputs suivants :

Output name            Value               Description                     
KubeControlPlane_SAI
azurerm_kubernetes_cluster.TerraAKSwithRBAC.identity
azurerm_kubernetes_cluster.TerraAKSwithRBAC.identity
KubeControlPlane_SAI_Princip alId
azurerm_kubernetes_cluster. TerraAKSwithRBAC.identity[0 ].principal_id
AKS Control plane Managed Identity principal Id
AKS Control plane Managed Identity principal Id
azurerm_kubernetes_cluster.TerraAKSwithRBAC.kubelet_identity
User Assigned Identity block for the Kubelet
KubeKubelet_UAI_ClientId
azurerm_kubernetes_cluster.TerraAKSwithRBAC.kubelet_identity[0].client_id
User Assigned Identity forkubelet principal I
KubeKubelet_UAI_ObjectId
azurerm_kubernetes_cluster.TerraAKSwithRBAC.kubelet_identity[0].object_id
User Assigned Identity forkubelet object Id
KubeKubelet_UAI_Id
azurerm_kubernetes_cluster.TerraAKSwithRBAC.kubelet_identity[0].user_
assigned_identity_id
User Assigned Identity forkubelet resource Id

Après le déploiement nous obtenons les informations désirées :

Code : Déploiement de l’infrastructure pour Pod IdentityAvec cela, nous devons réaliser l’assignations des rôles, pourquoi pas à travers une simple ressource
terraform :

Code pour assignations des rôles

Un module est disponible ici pour les plus curieux. L’appel du module ressemble à ceci :

Code pour un appel à module Code pour un appel à module Code pour un appel à module Code pour un appel à module

Les attentifs auront remarqué le scope d’application des rôles. En fonction de l’emplacement des ressources, notamment les UAI qui seront créées par la suite, il est possible (ou non) de faire un scope plus restreint sur des Resource Groups. La préparation est terminée.

Passons à présent à l’installation de Pod Identity dans le contexte Kubernetes.

Déploiement de Pod Identity

Pour installer Pod Identity sur un cluster AKS, nous nous référons naturellement à la documentation du projet.
Pour commencer, il est intéressant de noter que l’installation par défaut sur un cluster avec kubenet n’est pas possible. La raison en étant que kubenet, en tant que CNI, est moins sécurisé qu’un CNI tel que Azure CNI ou Calico. C’est une histoire pour un autre jour / article cependant.

Heureusement, il y a un moyen de forcer l’installation en spécifiant un switch sur les containers associés au nmi :

Déploiement de Pod IdentityLorsque le fichier yaml est modifié correctement, une commande kubectl permet le déploiement (ou une
commande helm d’ailleurs) :

Déploiement de Pod IdentityPour vérifier l’installation, un check des pods dans le namespace default :

Déploiement de Pod IdentityPuis un check des logs sur les pods en question :

Déploiement de Pod Identity

A présent nous pouvons déployer les CRD pour Azure Identity. Pour cela nous avons les définitions yaml
pour AzureIdentity et AzureIdentityBinding :

AzureIdentity et AzureIdentityBinding AzureIdentity et AzureIdentityBinding

A l’aide d’un petit module utilisant l’interpolation terraform et les templates, nous pouvons envoyer en
output les fichiers customisés de la manière suivante :

AzureIdentity et AzureIdentityBinding

Ensuite, l’utilisation de la ressource local_file nous permet de créer les fichiers dans le répertoire cible :

AzureIdentity et AzureIdentityBinding

Nous avons à présent tout ce qu’il nous faut pour utiliser Pod Identity, à part quelque chose pour l’utiliser effectivement. Avant cela, il est important de comprendre comment un pod est attaché à une AzureIdentity. Le CRD AzureIdentityBinding est l’objet qui fait « la colle » entre le pod et le CRD AzureIdentity, à travers un label spécifique que nous précisons ensuite au niveau du fichier de définition du pod :

AzureIdentity et AzureIdentityBinding

Pod Identity en action

Un exemple simple est de donner accès à une Azure Key Vault, à travers le CSI Driver pour secret store et spécifiquement le plugin pour Azure Key Vault. Comme nous nous concentrons ici sur Pod Identity, nous allons juste lister quelques liens :

Pod Identity en action

Dans notre use case, nous allons créer un secret store pointant vers une Key Vault créé au préalable :

Pod Identity en action

Et ensuite un pod utilisant ce même secret store :

Pod Identity en action

Une fois le pod déployé, ce qui prend parfois quelques secondes, nous pouvons atteindre le Key Vault et
récupérer la valeur du secret que nous avons spécifié dans le secret store :

Pod Identity en actionNous devrions normalement obtenir la même valeur que celle du secret dans le Key Vault :

Key Vault

La démonstration est terminée. Il est à présent temps de conclure.

Conclusion

Après ces quelques pages un peu technique, nous pouvons conclure que la technologie est intéressante car elle répond à un réel besoin de sécurisation des patterns micro-services.
Nous pouvons également nous avouer qu’il reste encore beaucoup de questions et de parties non adressées autour de l’usage de cette technologie. Retenons aussi qu’il s’agit d’un projet Open Source mais que le support Microsoft arrive bientôt avec une simplification de la mise en œuvre à l’aide d’un add-on AKS.
D’un point de vue plus pratique, je sais que la solution Velero permet d’user de Pod Identity. Je pense
que de plus en plus de solution vont se diriger dans cette direction.
Enfin, j’ai créé un repo GitHub dédié à cet article, qui est lui-même issue d’une présentation meetup.
Pour ceux qui veulent tester…

A bientôt !