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 ?
Sans 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).
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.
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.
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 :
Source : 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 :
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_
|
User Assigned Identity forkubelet resource Id |
Après le déploiement nous obtenons les informations désirées :
Avec cela, nous devons réaliser l’assignations des rôles, pourquoi pas à travers une simple ressource
terraform :
Un module est disponible ici pour les plus curieux. L’appel du module ressemble à ceci :
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 :
Lorsque le fichier yaml est modifié correctement, une commande kubectl permet le déploiement (ou une
commande helm d’ailleurs) :
Pour vérifier l’installation, un check des pods dans le namespace default :
Puis un check des logs sur les pods en question :
A présent nous pouvons déployer les CRD pour Azure Identity. Pour cela nous avons les définitions yaml
pour 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 :
Ensuite, l’utilisation de la ressource local_file nous permet de créer les fichiers dans le répertoire cible :
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 :
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 :
Dans notre use case, nous allons créer un secret store pointant vers une Key Vault créé au préalable :
Et ensuite un pod utilisant ce même secret store :
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 :
Nous devrions normalement obtenir la même valeur que celle du secret dans le 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 !