Skip to main content

Kubernetes tips & tricks : Le Node Authorization mode de l'API-Server

22 oct. 2019

Aujourd'hui, pendant la mise à jour d'un cluster j'ai appris à mes dépens l’existence du Node Authorization Mode de l'API server du kube. Debriefons ça ensemble...

Il s'agissait de réaliser une banale mise à jour de Kubernetes en version 1.14.7 vers la 1.15.4. La particularité de ce cluster est qu'il n'utilise pas kube-proxy. Historiquement, nous avions installé ce cluster en utilisant kube-router comme service proxy car celui-ci supporte IPVS, ce qui n'était pas le cas pour kube-proxy à l'époque. L'inconvénient est que cette configuration n'est pas vraiment supportée par kubeadm qui réinstalle kube-proxy via un DaemonSet à chaque mise à jour. Cette dépendance de kubeadm à kube-proxy va même un peu plus loin comme nous allons le voir aujourd'hui.

Très grossièrement, la mise à jour du cluster comprenait les étapes suivantes :

  • Mettre à jour kubeadm sur les master nodes
  • Exécuter l'upgrade du contrôle plane avec kubeadm
  • Supprimer le DaemonSet de kube-proxy qui a été réinstallé automatiquement par kubeadm
  • Mettre à jour kubeadm et kubelet sur tous les nodes
  • Mettre à jour les configuration de kubelet avec kubeadm
  • Relancer les kubelets

C'est à l'étape 5 que notre plan minutieusement préparé... échoua.

La mise à jour de la configuration de kubelet consiste à exécuter la commande suivante sur les noeuds :

kubeadm upgrade node phase kubelet-config --kubelet-version 1.15.4

En pratique, cette commande télécharge la configuration du kubelet mis à jour par kubeadm qui se trouve dans la ConfigMap kubelet-config-{version}. Le problème, c'est que cette commande exécute un bout de code générique qui télécharge également la configuration des autres composants du cluster, notamment celle de notre bien aimé kube-proxy. C'est là que le drame survient, la commande me retourne l'erreur suivante :

unable to fetch the kubeadm-config ConfigMap: failed to get component configs: configmaps "kube-proxy" is forbidden: User "system:node:n001" cannot get resource "configmaps" in API group "" in the namespace "kube-system": no relationship found between node "n001" and this object

Je me lance alors dans une alléchante séance de troubleshooting :

Je commence par vérifier que la ConfigMap "kube-proxy" existe bien. Après tout, je n'ai supprimé que le DaemonSet kube-proxy. La ConfigMap devrait encore exister, et c'est bien le cas.

Je continue en vérifiant les droits sur la ConfigMap avec la commande kubectl can-i et en comparant le résultat avec un cluster qui utilise kube-proxy :

Sans kube-proxy :

kubectl --kubeconfig=/etc/kubernetes/kubelet.conf -n kube-system auth can-i get configmap/kube-proxy no - no relationship found between node "n001" and this object

Avec kube-proxy :

kubectl --kubeconfig=/etc/kubernetes/kubelet.conf -n kube-system auth can-i get configmap/kube-proxy yes

Je compare alors les droits entre les deux clusters : ils sont bien identiques. Mais je découvre également qu'aucun ClusterRoleBinding n'associe le ClusterRole system:node aux noeuds. Je me demande alors comment les kubelets du cluster avec kube-proxy peuvent bien accéder à la ConfigMap kube-proxy. Y-aurait-il de la magie là dessous ?!

Abracadabra !

Au bout de quelques minutes de recherche, j'apprend finalement l’existence du Node Authorization mode activé sur l'API Server en parallèle du RBAC mode.

Ce mode permet à l'API-Server d'autoriser les Kubelets à n'accéder qu'aux objets liés aux Pods exécutés par ceux-ci. Un Kubelet n'a en effet aucune raison de pouvoir accéder à tous les Secrets d'un cluster, pourtant, si un Pod exécuté sur celui-ci monte un Secret, il faudra que celui-ci soit en mesure de le récupérer auprès de l'API-Server. Ce dispositif a été introduit dans Kubernetes 1.7 afin d'en améliorer la sécurité. Auparavant, un ClusterRole était bindé sur les utilisateurs des Kubelets pour les laisser accéder à tous les objets dont les Pods peuvent dépendre (Secrets, ConfigMap, etc).

C'est cette fonctionnalité qui a empêché Kubeadm de récupérer la ConfigMap kube-proxy puisque le DaemonSet de celui-ci supprimé, plus aucun Pod ne dépendait de cette ConfigMap et donc plus aucun Kubelet n'avait le droit de la récupérer. Le message d'erreur initial "no relationship found between node "n001" and this object" était d'ailleurs tout à fait explicite et contenait déjà la réponse à notre interrogation. Ce message d'erreur n'est pas courant et est produit directement par le Node Authorization mode.

Afin de contourner ce point et pour pouvoir poursuivre notre mise à jour, la solution la plus simple que j'ai trouvé fût de créer un DaemonSet exécutant un pause container et montant le ConfigMap de kube-proxy.

A noter également, l'existence du Node Restriction Admission Controller, activé par défaut sur une installation kubeadm récente. Il permet d'aller encore plus loin et de limiter (en partie, plus de détails) les modifications (ou suppressions!) de son propre objet Node et des Pods schédulés sur lui-même.