## Introduction Les contrôleurs d'admission (admission controllers) Kubernetes se situent entre l'authentification/l'autorisation et la persistance des objets, interceptant chaque requête API pour valider, muter ou rejeter des ressources. La mise à niveau ou la migration de ces contrôleurs est une opération courante mais à haut risque — que vous passiez d'API obsolètes comme admissionregistration.k8s.io/v1beta1 vers v1 , que vous remplaciez PodSecurityPolicy par le contrôleur Pod Security Admission, ou que vous déployiez une mise à jour de webhook personnalisé. Un changement mal exécuté peut bloquer la création de charges de travail à l'échelle du cluster, tandis qu'une migration bien planifiée progresse par étapes contrôlées avec une capacité de restauration instantanée. Ce guide fournit un flux de travail reproductible, testé en production : inventaire de l'état actuel, exécution d'un pilote ciblé avec des valeurs par défaut sûres, vérification du comportement avec des cas de test concrets, et maintien de procédures de récupération documentées. Chaque étape inclut les commandes kubectl exactes, les manifestes YAML et la sortie attendue pour que vous puissiez exécuter en toute confiance sur vos propres clusters. ## Inventaire des Versions et de l'Environnement Avant de toucher à toute configuration, établissez une base de référence précise. Exécutez ces commandes depuis une machine avec un accès cluster-admin : kubectl version --short kubectl get --raw /version Exemple de sortie : Client Version: v1.28.4 Kustomize Version: v5.0.4-0.20230601165947-6ce0bf390ce3 Server Version: v1.27.6 Confirmez quelles versions de l'API admissionregistration le cluster dessert. L'API v1 est stable depuis Kubernetes 1.16 ; si votre control plane est plus ancien, planifiez d'abord la mise à niveau du control plane. kubectl api-versions | grep admissionregistration Sortie attendue sur un cluster moderne : admissionregistration.k8s.io/v1 Listez toutes les configurations de webhook actuellement installées : kubectl get validatingwebhookconfigurations,mutatingwebhookconfigurations Exemple de sortie : NAME WEBHOOKS AGE pod-policy.example.com 1 45d quota-enforcer.example.com 1 12d Pour chaque webhook, inspectez la ressource complète pour capturer apiVersion , admissionReviewVersions , failurePolicy , sideEffects et namespaceSelector : kubectl get validatingwebhookconfiguration pod-policy.example.com -o yaml Extrait clé à enregistrer : apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: name: pod-policy.example.com webhooks: - name: pod-policy.example.com admissionReviewVersions: ["v1"] sideEffects: None failurePolicy: Fail Pour les plugins d'admission intégrés, examinez le manifeste de pod statique kube-apiserver. Sur les clusters kubeadm : grep -A2 'enable-admission-plugins' /etc/kubernetes/manifests/kube-apiserver.yaml Exemple de sortie : - --enable-admission-plugins=NodeRestriction,NamespaceLifecycle,LimitRanger,ServiceAccount,PodSecurity - --disable-admission-plugins= Documentez toutes les découvertes dans un journal de modifications. Prenez un instantané etcd avant de procéder, et exportez chaque configuration de webhook en YAML pour une restauration immédiate : mkdir -p /root/admission-backup/$(date +%F) kubectl get validatingwebhookconfigurations,mutatingwebhookconfigurations -o yaml > /root/admission-backup/$(date +%F)/webhooks-backup.yaml ## Chemin de Configuration Sûr : Pilote Ciblé N'appliquez jamais un changement à l'échelle du cluster en une seule étape. Utilisez un pilote ciblé qui limite la nouvelle configuration à un seul namespace de test, et commencez avec failurePolicy: Ignore pour qu'un webhook défectueux ne puisse pas bloquer des charges de travail légitimes. ### Pilote de Migration de Webhook Créez une ValidatingWebhookConfiguration migrée ciblant l'API v1 avec un sélecteur de namespace : # validatingwebhook-scoped.yaml apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: name: pod-policy.example.com webhooks: - name: pod-policy.example.com clientConfig: service: name: webhook-service namespace: webhook-ns path: /validate port: 443 caBundle: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t... # remplacer par le CA réel rules: - operations: ["CREATE", "UPDATE"] apiGroups: ["*"] apiVersions: ["*"] resources: ["pods"] scope: Namespaced namespaceSelector: matchLabels: admission-pilot: enabled admissionReviewVersions: ["v1"] sideEffects: None failurePolicy: Ignore timeoutSeconds: 5 Appliquez-le : kubectl apply -f validatingwebhook-scoped.yaml Sortie attendue : validatingwebhookconfiguration.admissionregistration.k8s.io/pod-policy.example.com configured Créez et étiquetez le namespace pilote : kubectl create namespace pilot-ns kubectl label namespace pilot-ns admission-pilot=enabled Sortie attendue : namespace/pilot-ns created namespace/pilot-ns labeled Seules les ressources créées dans pilot-ns atteindront désormais ce webhook. Tous les autres namespaces continueront d'utiliser la configuration précédente (ou aucun webhook s'il s'agit d'un nouveau déploiement). ### Migration du Contrôleur Intégré : PodSecurityPolicy vers Pod Security Admission Pour la migration intégrée, modifiez le manifeste kube-apiserver pour activer le plugin PodSecurity tout en conservant tous les plugins requis existants : # Sauvegarde d'abord cp /etc/kubernetes/manifests/kube-apiserver.yaml /root/admission-backup/$(date +%F)/kube-apiserver.yaml.backup # Modifier le manifeste vim /etc/kubernetes/manifests/kube-apiserver.yaml Localisez le drapeau --enable-admission-plugins et assurez-vous que PodSecurity est inclus aux côtés des plugins obligatoires : - --enable-admission-plugins=NodeRestriction,NamespaceLifecycle,LimitRanger,ServiceAccount,PodSecurity Ne supprimez pas NodeRestriction , NamespaceLifecycle , LimitRanger ou ServiceAccount à moins d'avoir validé leur suppression dans un cluster de test. Le kubelet redémarrera automatiquement l'API server lorsque le manifeste change. Maintenant, étiquetez le namespace pilote avec les modes Pod Security Admission. Utilisez baseline pour l'application (bloque les élévations de privilèges connues) et restricted pour les avertissements (alerte sur les charges de travail hautement privilégiées) : kubectl label namespace pilot-ns pod-security.kubernetes.io/enforce=baseline kubectl label namespace pilot-ns pod-security.kubernetes.io/warn=restricted kubectl label namespace pilot-ns pod-security.kubernetes.io/audit=restricted Sortie attendue : namespace/pilot-ns labeled namespace/pilot-ns labeled namespace/pilot-ns labeled Cette application ciblée signifie que seules les charges de travail de pilot-ns sont évaluées selon les nouvelles politiques. Le reste du cluster reste sur l'ancien PodSecurityPolicy (ou sans restriction) jusqu'à ce que vous étendiez les étiquettes. ## Vérification et Diagnostics La vérification a deux objectifs : confirmer que le contrôleur est actif, et observer les décisions exactes d'acceptation/refus pour des charges de travail représentatives. ### Vérification du Webhook Créez un Pod de test dans le namespace pilote qui devrait être refusé par votre politique (par exemple, un conteneur s'exécutant en root alors que la politique exige runAsNonRoot: true ) : # test-deny.yaml apiVersion: v1 kind: Pod metadata: name: test-deny namespace: pilot-ns spec: containers: - name: nginx image: nginx:1.25 securityContext: runAsNonRoot: false Appliquez-le : kubectl apply -f test-deny.yaml Sortie de refus attendue : Error from server (Forbidden): error when creating "test-deny.yaml": admission webhook "pod-policy.example.com" denied the request: Pod does not meet security policy: runAsNonRoot must be true Créez maintenant un Pod conforme qui devrait être admis : # test-allow.yaml apiVersion: v1 kind: Pod metadata: name: test-allow namespace: pilot-ns spec: containers: - name: nginx image: nginx:1.25 securityContext: runAsNonRoot: true runAsUser: 1000 kubectl apply -f test-allow.yaml Sortie de succès attendue : pod/test-allow created Si le webhook ne se déclenche pas du tout, dépannez dans cet ordre : - Correspondance du sélecteur de namespace : kubectl get namespace pilot-ns --show-labels — confirmez que admission-pilot=enabled est présent. - Accessibilité du service : kubectl get endpoints -n webhook-ns webhook-service — vérifiez que l'IP de l'endpoint correspond à un pod webhook en cours d'exécution. - Validité du bundle CA : Le caBundle dans la configuration du webhook doit correspondre au certificat de service. Décodez et inspectez : echo "" | base64 -d | openssl x509 -text -noout . - Logs du webhook : kubectl logs -n webhook-ns -l app=webhook-service — recherchez les requêtes /validate entrantes et les erreurs de handshake TLS. Vérifiez les événements récents dans le namespace pilote pour les entrées liées à l'admission : kubectl get events -n pilot-ns --sort-by=.lastTimestamp Exemple de sortie : LAST SEEN TYPE REASON OBJECT MESSAGE 12s Warning FailedCreate pod/test-deny admission webhook "pod-policy.example.com" denied the request: Pod does not meet security policy: runAsNonRoot must be true ### Vérification de Pod Security Admission Testez le contrôleur intégré avec un Pod privilégié qui viole le profil baseline : # test-privileged.yaml apiVersion: v1 kind: Pod metadata: name: test-privileged namespace: pilot-ns spec: containers: - name: test image: busybox:1.36 command: ["sleep", "3600"] securityContext: privileged: true kubectl apply -f test-privileged.yaml -n pilot-ns Refus attendu (application baseline) : Error from server (Forbidden): pods "test-privileged" is forbidden: violates PodSecurity "baseline: v1.27": privileged (container "test" must not set securityContext.privileged=true) Un Pod conforme (pas de privileged, pas de hostPath, pas de hostNetwork, etc.) devrait être créé avec succès. ### Confirmation par les Journaux d'Audit Si la journalisation d'audit de l'API server est activée, interrogez pour les décisions d'admission : grep 'admission webhook' /var/log/kubernetes/audit.log | tail -n 5 Exemple d'entrée d'audit (tronquée) : {"kind":"Event","apiVersion":"audit.k8s.io/v1","level":"RequestResponse","verb":"create","requestURI":"/api/v1/namespaces/pilot-ns/pods","user":{"username":"admin"},"responseStatus":{"code":403},"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":"RBAC: allowed by ClusterRoleBinding"}} Un responseStatus.code de 200 ou 201 signifie admis ; 403 signifie refusé par un contrôleur d'admission. Confirmez la politique d'échec en direct pendant le pilote : kubectl get --raw /apis/admissionregistration.k8s.io/v1/validatingwebhookconfigurations/pod-policy.example.com | jq .webhooks[0].failurePolicy Sortie attendue pendant le pilote : "Ignore" ## Modes d'Échec et Récupération Les changements de contrôleurs d'admission échouent de manière prévisible. Le tableau ci-dessous associe chaque mode d'échec à son symptôme observable et à la commande de récupération exacte.
Mode d'ÉchecSymptômeAction de Récupération
Endpoint webhook en panne avec failurePolicy: FailToutes les créations de pods correspondantes bloquent puis échouent par timeoutDéfinir failurePolicy: Ignore ou supprimer le webhook
caBundle invalide ou mauvais nom de serviceErreurs "connection refused" ou TLS dans les logs de l'API serverCorriger caBundle ou supprimer le webhook
Champs v1 requis manquants (sideEffects, admissionReviewVersions)kubectl apply retourne une erreur de validation de champAjouter les champs manquants et réappliquer
Erreur de frappe dans --enable-admission-pluginskube-apiserver ne démarre pas, crashloopRestaurer le manifeste depuis la sauvegarde
Sélecteur de namespace trop large (ex: absent ou matchExpressions trop large)Le webhook rejette des ressources dans les namespaces de productionResserrer matchLabels et réappliquer
### Commandes de Récupération 1. Échec de l'endpoint webhook — basculer immédiatement sur Ignore : kubectl patch validatingwebhookconfiguration pod-policy.example.com \ --type merge \ -p '{"webhooks":[{"name":"pod-policy.example.com","failurePolicy":"Ignore"}]}' Sortie attendue : validatingwebhookconfiguration.admissionregistration.k8s.io/pod-policy.example.com patched 2. kube-apiserver ne démarre pas à cause d'une mauvaise configuration de plugin — restaurer le manifeste : cp /etc/kubernetes/manifests/kube-apiserver.yaml /tmp/kube-apiserver.yaml.broken cp /root/admission-backup/$(date +%F)/kube-apiserver.yaml.backup /etc/kubernetes/manifests/kube-apiserver.yaml Le kubelet détecte le changement de fichier et redémarre automatiquement l'API server. Évitez systemctl restart kubelet sauf si le kubelet lui-même est défaillant. 3. Annuler une migration de webhook — supprimer la nouvelle config, appliquer la sauvegarde : kubectl delete validatingwebhookconfiguration pod-policy.example.com kubectl apply -f /root/admission-backup/$(date +%F)/webhooks-backup.yaml 4. Désactiver l'application Pod Security Admission sur un namespace : kubectl label namespace pilot-ns pod-security.kubernetes.io/enforce- kubectl label namespace pilot-ns pod-security.kubernetes.io/warn- kubectl label namespace pilot-ns pod-security.kubernetes.io/audit- Sortie attendue : namespace/pilot-ns unlabeled namespace/pilot-ns unlabeled namespace/pilot-ns unlabeled 5. Vérification de santé post-récupération : kubectl get nodes kubectl get pods -A Confirmez que tous les nœuds sont Ready et qu'aucun pod système n'est en CrashLoopBackOff ou Pending à cause de échecs d'admission. ## Liste de Contrôle Opérationnelle Utilisez cette liste de contrôle pour chaque mise à niveau ou migration de contrôleur d'admission. Cochez chaque élément et enregistrez la sortie dans votre journal de modifications. - [ ] Confirmer la version du cluster ( kubectl version --short ) et la version de l'API admissionregistration ( kubectl api-versions | grep admissionregistration ). - [ ] Exporter tous les YAML ValidatingWebhookConfiguration et MutatingWebhookConfiguration vers un répertoire de sauvegarde horodaté. - [ ] Prendre un instantané etcd ( etcdctl snapshot save /backup/etcd-$(date +%F).db ) et vérifier qu'il est restaurable sur un cluster de test. - [ ] Identifier le webhook ou le plugin intégré exact à modifier, son périmètre actuel et son failurePolicy . - [ ] Créer un namespace pilote avec une étiquette unique (ex: admission-pilot=enabled ). - [ ] Appliquer la nouvelle configuration avec failurePolicy: Ignore (webhooks) ou des étiquettes de namespace ciblées (Pod Security Admission). - [ ] Exécuter un test positif (ressource conforme → admise) et un test négatif (ressource non conforme → refusée avec le message attendu) dans le namespace pilote. - [ ] Capturer la sortie exacte de refus/admission et la coller dans l'enregistrement de modification. - [ ] Vérifier les journaux d'audit de l'API server ou les logs du webhook pour les entrées de décision d'admission. - [ ] Une fois le pilote validé, étendre progressivement le namespaceSelector (ajouter des étiquettes à plus de namespaces) ou basculer failurePolicy sur Fail . - [ ] Documenter l'emplacement de l'artefact de restauration et la commande exacte pour restaurer (ex: kubectl apply -f /root/admission-backup/2024-01-15/webhooks-backup.yaml ). - [ ] Planifier une révision 24 à 48 heures après le déploiement complet pour rechercher les échecs silencieux. Commande de révision post-déploiement : kubectl get events -A --field-selector reason=FailedCreate | grep -i admission Si cela retourne des événements, investiguez la logique du webhook ou de la politique avant de considérer la migration comme terminée. ## Conclusion Une mise à niveau ou migration sécurisée d'un contrôleur d'admission suit une séquence disciplinée : inventorier l'état exact du cluster et la configuration actuelle, isoler le changement derrière un namespace pilote étiqueté avec failurePolicy: Ignore , vérifier chaque chemin d'acceptation et de refus avec des cas de test concrets et une sortie capturée, et maintenir des procédures de restauration documentées en une seule commande. Commencez par collecter la version du cluster, tous les YAML de webhook et la liste des plugins kube-apiserver. Appliquez les changements uniquement au namespace pilote — que ce soit via un namespaceSelector de webhook ou des étiquettes Pod Security Admission — et validez avec des pods de test à la fois négatifs et positifs. Gardez les manifestes de sauvegarde et les instantanés etcd accessibles pour pouvoir restaurer en quelques minutes si l'API server ne démarre pas ou si un webhook se comporte mal. Répétez la liste de contrôle opérationnelle pour chaque changement subséquent, et auditez les événements après le déploiement complet pour détecter les régressions silencieuses. Ces étapes transforment un changement risqué à l'échelle du cluster en une migration contrôlée et reproductible qui élimine les temps d'arrêt non planifiés et réduit le retravail.