Dans un précédent article, j’avais tenté de décrire la logique d’authentification auprès d’un cluster Kubernetes et de donner quelques informations autour du fonctionnement de la gestion des droits associés.
J’étais parti sur l’usage par défaut d’une clef privée et d’un certificat. Complété d'un contrôle d’accès basé sur les rôles RBAC (Role-based access control), c’est ainsi qu’on peut basiquement se connecter à un cluster Kubernetes et réaliser un certain nombre d’actions fonctions des droits qu’on nous aura donnés.
Ce mécanisme de clef/certificat n’est pas forcément des plus pratiques à utiliser, notamment en entreprise.
En milieu professionnel, il n’est pas rare d’avoir un annuaire d’entreprise, chargé de stocker les comptes et groupes d’utilisateurs pour assurer un service d’authentification centralisé.
On retrouve souvent par exemple, Microsoft Active Directory, ou son extension cloud Microsoft Entra ID (précédemment connu sous le nom de Azure Active Directory).
De plus en plus, les entreprises tentent de fédérer leur authentification sur ce type de produit. Cela permet d’avoir une gestion centralisée de ses accès, d’imposer des conditions particulières d’authentification et de suivre plus facilement l’activité associée.
Dans cet article, j’ai choisi de présenter la configuration de Kubernetes afin de pouvoir utiliser Microsoft Entra ID comme source d’authentification.
J’aurais pu choisir d’autres solutions, mais Microsoft Entra ID reste très utilisée, et surtout la mécanique d’intégration avec Kubernetes pourra être facilement adaptée à d’autres produits.
L’objectif est d’utiliser le standard OIDC (OpenID Connect) qui lui-même utilise le protocole OAuth 2.0 pour faire communiquer Kubernetes avec Microsoft Entra ID.
OIDC/OAuth 2.0 ne sont pas spécifiques à Microsoft Entra ID. Il est possible d’utiliser également d’autres solutions d'IAM (Identity and Access Management) comme Okta ou Google Cloud Identity en se basant sur les mêmes protocoles avec une configuration de Kubernetes très proche de ce que nous allons voir maintenant.
Comme à mon habitude avant de rentrer dans le vif du sujet, je vais résumer l’objectif de l’article dans un schéma global.
Cliquez sur l'image pour l'agrandir.
Dans la vue ci-dessus, je pars du principe que le compte de l’utilisateur existe dans un environnement Microsoft Active Directory On Prem. Ce compte est répliqué dans Microsoft Entra ID via un outil dédié Microsoft Entra Connect Sync. Cela reste facultatif, on pourrait très bien être dans un environnement où le compte n’existe que dans Microsoft Entra ID et est natif à Azure. L’important est que ce compte existe dans Microsoft Entra ID.
La logique est la suivante:
Comme évoqué précédemment, pour que la démonstration fonctionne, il faut qu’un compte utilisateur existe dans Microsoft Entra ID. Ce compte peut être créé nativement dans la solution d’annuaire cloud ou être répliqué d’un annuaire interne. Dans mon cas, j’exécute un service Microsoft Entra Connect Sync pour synchroniser mon Active Directory avec mon tenant Microsoft Entra ID pour mon domaine coolcorp.fr. Je ne vais pas rentrer dans le détail à ce niveau, libre à chacun d’avoir la mécanique de son choix.
Cliquez sur l'image pour l'agrandir.
Cliquez sur l'image pour l'agrandir.
La suite des instructions va concerner des manipulations sous Microsoft Entra ID, il est préférable d’avoir quelques notions du produit pour bien comprendre la suite, mais je vais essayer d’être le plus explicite possible pour ceux qui disposeraient d’autres solutions puissent trouver une logique de comparaison.
Il faut se connecter sur son tenant Microsoft Entra ID avec un compte disposant des droits nécessaires pour créer ce que Microsoft appelle une Enterprise Application. Cela correspond à une configuration propre à une application externe qui va permettre à cette dernière de communiquer avec Microsoft Entra ID.
Cliquez sur l'image pour l'agrandir.
Cliquez sur l'image pour l'agrandir.
Dans mon exemple, elle va autoriser mon application externe, soit mon Cluster Kubernetes et mon client kubectl, à interagir avec l’annuaire cloud.
Microsoft dispose d’un référentiel d’application déjà paramétrée pour simplifier l’intégration d’outils connus ou d’entreprises partenaires. Elles figurent dans une gallery, mais qu’on ne va pas utiliser, préférant déclarer une application « custom » nommée arbitrairement private-app-k8s.
Cliquez sur l'image pour l'agrandir.
Cliquez sur l'image pour l'agrandir.
Une fois l’application créée, il faut retourner dans le menu de base de Microsoft Entra ID pour cette fois-ci retenir l’option « App Registrations » dans laquelle on va définir les conditions d’utilisation de l’app qu’on vient de déclarer.
Cliquez sur l'image pour l'agrandir.
En cliquant sur « All Application », on retrouve l’apps en question qu’on peut sélectionner pour poursuivre la configuration.
Cliquez sur l'image pour l'agrandir.
De là on choisit l’option « Authentification », pour y ajouter une « plateform ». Une platform correspond au type d’application externe qui va interagir avec Microsoft Entra ID via notre Enterprise Application private-app-k8s.
Cliquez sur l'image pour l'agrandir.
Cliquez sur l'image pour l'agrandir.
On va choisir « Web » car Kubernetes/kubectl vont s’authentifier comme s’il s’agissait d’une application web.
Cliquez sur l'image pour l'agrandir.
Il faut ensuite configurer l’adresse de redirection vers laquelle on sera renvoyé une fois l’authentification réussie. Cela correspond à l'application externe qui a fait la demande d’authentification.
Dans notre exemple, c’est kubectl qui fait la demande de jeton, c’est donc vers lui qu’on doit être renvoyé une fois la phase d’authentification réalisée du côté de Microsoft Entra ID.
En l’occurrence, kubectl attendra la réponse sur le port 18000, et comme kubectl est local à l’équipement retenu pour lancer la commande (ex : votre poste de travail), on utilise http://localhost:18000.
Cliquez sur l'image pour l'agrandir.
Il est nécessaire d’ajouter également un autre port, le 8000.
Cliquez sur l'image pour l'agrandir.
Cliquez sur l'image pour l'agrandir.
On peut passer maintenant au menu « API permissions ». En effet, une authentification réussie n’implique pas que vous puissiez récupérer auprès de Microsoft Entra ID, des informations sur l’utilisateur lui-même.
Cliquez sur l'image pour l'agrandir.
Pour ça il faut ajouter une permission sur l’API Microsoft Graph.
Cliquez sur l'image pour l'agrandir.
Cliquez sur l'image pour l'agrandir.
C’est l’API la plus utilisée dans Microsoft Entra ID puisqu’elle permet de récupérer tout un ensemble d’informations sur l’utilisateur authentifié.
On demande à ce qu’on obtienne une délégation sur l’usage de cette API.
Cliquez sur l'image pour l'agrandir.
On cherche à pouvoir lire le profil de l’utilisateur via les deux points d’entrée API User: User.Read et User.Read.All.
Cliquez sur l'image pour l'agrandir.
C’est grâce à cela que notre jeton d’authentification va pouvoir contenir le login de l’utilisateur, ses groupes, son adresse mails….
Une fois la demande de délégation faite, il faut l’autoriser via l’appui sur le bouton « Grant Admin consent for Default Directory ».
Cliquez sur l'image pour l'agrandir.
Cliquez sur l'image pour l'agrandir.
C’est une mesure de sécurité complémentaire. En effet en entreprise, il n’est pas rare que les créateurs d’Enterprise Application ne soient pas pleinement administrateurs du tenant Microsoft Entra ID. Dans ce cas, il créer leur configuration et soumettent leur besoin d’accès à l’API Microsoft Entra ID aux administrateurs du tenant, qui fonction de la criticité des informations demandées ou de l’interaction associés, peuvent refuser ou non la délégation.
On poursuit par le menu « Certificates & Secrets ».
Cliquez sur l'image pour l'agrandir.
Cliquez sur l'image pour l'agrandir.
Kubectl va devoir communiquer avec Microsoft Entra ID avant même que l’utilisateur ne s’authentifie. C’est l’application kubectl elle-même qui va se présenter, il faut donc qu’elle puisse prouver sa légitimité. C’est à ça que va servir le secret (attention, ça n'a rien à voir avec l'objet Secret de l'API K8S). Bien entendu, sa valeur est confidentielle et ne doit pas être partagée.
On donne une validité au secret, la bonne pratique est de ne pas dépasser 6 mois.
Cliquez sur l'image pour l'agrandir.
Cliquez sur l'image pour l'agrandir.
On passe ensuite sur la partie « Token configuration ». Par défaut le token émis contient peu de données sur l’utilisateur. Or on n’a demandé via l’API Graph à pouvoir accéder au profil complet de ce dernier. Il faut donc indiquer à Microsoft Entra ID qu’on souhaite que le token contienne des informations complémentaires sur l’utilisateur, qu’on pourra ensuite utiliser au niveau de Kubernetes.
Cliquez sur l'image pour l'agrandir.
Par exemple, on souhaite avoir la liste des groupes auxquels appartient le user, ainsi il sera possible au niveau de K8S de ne pas créer de rôles par utilisateurs, mais par groupes d’utilisateurs.
Cliquez sur l'image pour l'agrandir.
Cliquez sur l'image pour l'agrandir.
On veut aussi ajouter l’UPN (User Principal Name), qui est un id unique par utilisateur et qui peut servir comme référence de login.
Cliquez sur l'image pour l'agrandir.
Cliquez sur l'image pour l'agrandir.
Cliquez sur l'image pour l'agrandir.
Il faut maintenant revenir en arrière, pour aller dans le menu « Enterprise Application » et cliquer sur son apps.
Cliquez sur l'image pour l'agrandir.
On va dans « User and Groups », puis on va ajouter notre utilisateur. En effet, vous pouvez avoir tous vos users et groups dans Microsoft Entra ID, ce n’est pas pour autant qu’ils sont autorisés au niveau de votre application d’entreprises. C’est à vous de renseigner les personnes ou les groupes pouvant faire appel à cette application d’entreprises, en d’autres mots ceux qui seront autorisés à déléguer leur authentification via kubectl à Microsoft Entra ID.
Cliquez sur l'image pour l'agrandir.
Pour mon exemple, je dispose d’une version gratuite de Microsoft Entra ID dans laquelle je ne peux pas donner de droits pour un groupe. Je vais donc choisir mon user directement.
Cliquez sur l'image pour l'agrandir.
Cliquez sur l'image pour l'agrandir.
Cliquez sur l'image pour l'agrandir.
On arrive à la fin du côté de Microsoft Entra ID. Avant de s’orienter vers Kubernetes, il nous faut récupérer plusieurs informations:
Cliquez sur l'image pour l'agrandir.
Cliquez sur l'image pour l'agrandir.
Ces trois références vont nous être utiles pour la suite.
On commence par le paramétrage de Kubernetes, et plus particulièrement de l’API Server puisque c’est ce composant qui est en charge de recevoir toutes les demandes externes d’interactions avec le cluster.
Pour l’exemple, je me base sur mon cluster K8S vanilla déployé dans le cadre de mon cookbook avec kubeadm.
Tous les composants de base d’un cluster Kubernetes sont rattachés à des fichiers yamls: des manifests.
Ces manifests sont inscrits dans un dossier spécifique, normalement /etc/kubernetes/manifests/ sur chaque nœud du cluster.
Ce dossier est surveillé par l’agent Kubelet installé sur les serveurs du cluster. Toutes modifications de l’un de ses fichiers vont être détectées par l’agent et va provoquer le redémarrage du composant associé. C’est comme ça que fonctionne le « bootstrap » d’un cluster.
Dans notre cas, il va falloir modifier le manifest kube-apiserver.yaml présent sur chaque contrôle-plane dans /etc/kubernetes/manifests/.
La première chose à faire est de réaliser une sauvegarde du fichier d’origine. Attention, surtout ne stocker rien dans /etc/kubernetes/manifests/. L’agent Kubelet surveille tout ce qui s’y trouve, même avec une autre extension que yaml…Croyez-moi ça peut être problématique. Copier donc votre sauvegarde dans un répertoire tiers.
Cliquez sur l'image pour l'agrandir.
Puis on édite le yaml kube-apiserver.yaml:
On vient lui ajouter dans la section spec, au niveau des instructions de command, les lignes suivantes:
#Azure conf
- --oidc-client-id=id_entreprise_application
- --oidc-issuer-url=https://login.microsoftonline.com/id_tenant_entra/v2.0
- --oidc-username-claim=preferred_username
- --oidc-groups-claim=groups
- --oidc-username-prefix=-
#fin Azure conf
Cliquez sur l'image pour l'agrandir.
Lorsqu’on va enregistrer le fichier, cela va provoquer le redémarrage du composant API (et des dépendances) du cluster pour le control plane qu’on vient de modifier.
Cliquez sur l'image pour l'agrandir.
Il faut donc procéder control plane par control plane en s’assurant à chaque fois que les composants redémarrent (kubectl get pod -n kube-system
) pour éviter toute interruption de services.
Tous ces paramètres ne sont pas propres à Microsoft Entra ID. Ce sont les options rattachées au support de OIDC par l’API Serveur de Kubernetes.
Si vous êtes amené à utiliser une autre solution d’annuaire externe, vous devrez procéder aux mêmes changements. Par contre, les valeurs de ces options peuvent changer, tout dépend de la configuration de votre token qui est délivré au format jwt et aux URLs devant être appelées.
Attention, lors d’un update de votre cluster tel que décrit ici, il est fort probable que votre configuration OIDC soit perdue. Il faudra la rétablir à chaque mise à jour du composant API du cluster. Pour cela, n’hésitez pas à exploiter un outil comme Ansible afin de rétablir automatiquement la configuration.
On peut passer à kubectl. Il est conseillé de prendre connaissance de mon précédent article sur le sujet qui vous donne les bases du fonctionnement de kubectl, et plus spécifiquement de l’usage du fichier config associé.
Par défaut, kubectl ne prend pas en charge OIDC, il faut lui adjoindre un plugin appelé kubelogin.
Celui-ci se récupère à l’URL suivante et prend la forme d’un simple binaire. A vous de choisir le format qui correspond à votre OS.
Cliquez sur l'image pour l'agrandir.
Une fois récupéré, il est important de renommer le binaire en kubectl-oidc_login, et de le positionner dans un emplacement renseigné dans votre path système. De cette manière, il sera implicitement appelé par kubectl.
Cliquez sur l'image pour l'agrandir.
La suite consiste à modifier son fichier de config lu par kubectl. (Plus d’informations ici)
Cliquez sur l'image pour l'agrandir.
Il faut créer un nouveau user dans la section users. Dans mon exemple, je l’appel auth-entra.
- name: auth-entra
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- oidc-login
- get-token
- --oidc-issuer-url=https://login.microsoftonline.com/id_tenant_entra/v2.0
- --oidc-client-id=id_entreprise_application
- --oidc-client-secret=secret_application
- --oidc-extra-scope=profile
command: kubectl
env: null
interactiveMode: IfAvailable
provideClusterInfo: false
Cliquez sur l'image pour l'agrandir.
Les paramètres qu’on n’y trouve sont en faite ceux qui vont être traités par l’addon kubelogin qu’on vient de récupérer.
Les autres éléments sont nécessaires au bon fonctionnement du plugin.
Puis on créer un nouveau contexte, associant l’utilisateur auth-entra au cluster.
- context:
cluster: kub
user: auth-entra
name: auth-entra@kub
Essayons maintenant de basculer dans ce nouveau contexte avec la commande:
kubectl config use-context auth-entra@kub
Cliquez sur l'image pour l'agrandir.
Lorsque je vais taper une commande d’interaction avec le cluster, comme par exemple kubectl get node
.
Mon navigateur va s’ouvrir et je vais avoir une mire d’authentification dans laquelle, je vais pouvoir renseigner mon login et mon password.
Cliquez sur l'image pour l'agrandir.
Si les informations sont correctes, l’authentification est acceptée et je peux revenir à ma CLI.
Cliquez sur l'image pour l'agrandir.
J’ai néanmoin un retour négatif. Ce qui est normal puisqu’en l’état, je n’ai pas donné de rôle à mon utilisateur.
Cliquez sur l'image pour l'agrandir.
C’est important à comprendre. Lorsque j’utilise OIDC avec Kubernetes, je délègue l’opération d’authentification à une solution tiers, en l’occurrence ici Microsoft Entra ID, mais la logique RBAC demeure et je conserve la gestion des rôles au niveau du cluster.
Il faut donc que j’utilise un objet de type ClusterRoleBinding pour associer mon utilisateur vburgun@coolcorp.fr à un rôle sur le cluster. Pour l'exemple, je vais utiliser le rôle par défaut cluster-admin (ce n’est pas conseillé, mais c’est pour simplifier la démonstration).
Voici le contenu du yaml clusterrolebinding-vburgun-entra-to-clusteradmin.yml:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: clusterrolebinding-vburgun-entra-to-clusteradmin
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: User
name: "vburgun@coolcorp.priv"
apiGroup: rbac.authorization.k8s.io
Je dois rebasculer avec mon authentification locale par défault: kubectl config use-context kubernetes-admin@kub
.
J’applique mon objet ClusterRoleBinding: kubectl apply -f clusterrolebinding-vburgun-entra-to-clusteradmin.yml
.
Cliquez sur l'image pour l'agrandir.
Je reviens avec mon contexte auth-entra@kub: kubectl config use-context auth-entra@kub
.
Cliquez sur l'image pour l'agrandir.
Je relance ma commande kubectl get node
et cette fois ci j’ai bien un retour positif.
Cliquez sur l'image pour l'agrandir.
Veuillez noter que je n’ai pas eu à retaper mes identifiants. La raison est que j’ai déjà précédemment récupéré un token. Même si je n’avais pas les droits sur le cluster, l’authentification, elle, a fonctionné, et j’ai toujours le token associé.
Celui-ci se trouve dans le répertoire de cache associé à l’addon kubelogin. Généralement le dossier est dans votre profil de configuration kub: User_profil\.
kube\cache\oidc-login
.
Cliquez sur l'image pour l'agrandir.
Pour les curieux vous pouvez ouvrir ce token et utiliser un site comme https://jwt.io/ pour le décoder.
Cliquez sur l'image pour l'agrandir.
Vous y retrouverez toutes les informations propres à l’utilisateur.
Dans notre exemple, on retrouve les IDs des groups Microsoft Entra ID, puisqu’on n’a demandé explicitement à les obtenir dans le cadre de la configuration de notre Enterprise Application.
Prenons par exemple, l’ID group 7acf3d08-ac27-423e-9f01-20aebd9ca880.
Si on consulte Microsoft Entra ID, on s’aperçoit que cet ID correspond au groupe GRP-APP-K8S-ADM, dans lequel mon utilisateur est renseigné.
Cliquez sur l'image pour l'agrandir.
Cliquez sur l'image pour l'agrandir.
Je vais donc refaire mon objet ClusterRoleBinding, en utilisant non plus le login de l’utilisateur, mais l’ID du groupe.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: clusterrolebinding-vburgun-entra-to-clusteradmin
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: Group
name: "7acf3d08-ac27-423e-9f01-20aebd9ca880"
apiGroup: rbac.authorization.k8s.io
Je réapplique l'objet avec la commande kubectl apply -f clusterrolebinding-vburgun-entra-to-clusteradmin.yml
.
Cliquez sur l'image pour l'agrandir.
Si je reviens dans mon contexte kubectl auth-entra et que je relance ma commande, kubectl get node
j’ai toujours une réponse correcte.
Cliquez sur l'image pour l'agrandir.
Simplement, au niveau de Kubernetes, mon accès n’est plus fonction de mon login, mais de mon appartenance à un groupe.
Dernier petit point concernant la sécurité. Comme on l’a vu, le token qu’on récupère est stocké dans le cache de kubelogin. S’il venait à être récupéré par une personne malveillante, cette personne pourrait exploiter le token pour se faire passer pour vous et accéder au cluster. Normalement ce risque est faible, car le token a une durée de vie limité qu’il vous est possible de paramétrer, de plus cela sous-entend que vous ayez rendu accessible votre répertoire de cache.
D’autres mécanismes peuvent être mis en place, comme les règles d’accès conditionnelles dans Microsoft Entra ID (disponible uniquement en version payante) qui peuvent également imposer des sources spécifiques d’authentification (matériel et/ou géographique).
Enfin, le fichier config associé à kubectl contient le secret de l’Enterprise Application. Même si cette information est confidentielle, elle n’est pas suffisante pour permettre à un attaquant de se faire passer pour vous. Ce secret permet de solliciter Microsoft Entra ID pour s’authentifier, mais il est encore nécessaire ensuite d’avoir des identifiants corrects pour obtenir un jeton.
Enfin, même en cas d’indisponibilité de Microsoft Entra ID, il vous reste toujours la possibilité de vous connecter au cluster via la mécanique d’authentification plus traditionnelle.
Après avoir vu les bases autour de l’authentification et de la gestion des rôles sous Kubernetes, il me semblait intéressant de montrer la capacité de K8S à pouvoir exploiter des mécanismes standard comme OIDC et OAuth 2.0 pour déléguer l’authentification à des solutions spécialisées. Ceci même avec un cluster vanilla monté avec Kubeadm.
Encore une fois, il est important de bien comprendre le fonctionnement et la logique d’authentification sous Kubernetes.
Dans un contexte, ou l’on crie à la Cybersécurité tous les 2 posts X (twitter), il me parait essentiel de s’intéresser à ce que l’on fait et pourquoi on le fait...et pas seulement suivre la sortie du prompt de ChatGPT....
Pour ceux qui souhaiteraient mettre en place ce genre de configuration, faites attention aux points suivants: