Introduction
Les contrôleurs d'admission Kubernetes constituent une frontière de sécurité critique qui valide et transforme les requêtes avant leur persistance. Un contrôleur d'admission mal configuré peut laisser entrer en production des conteneurs privilégiés, des secrets non sécurisés ou des charges de travail non conformes. Ce guide propose une approche structurée et pratique pour durcir les contrôleurs d'admission, depuis l'observation d'un problème jusqu'à la vérification du résultat. Il s'adresse aux développeurs, consultants DevOps et équipes techniques de startups qui doivent sécuriser leurs clusters Kubernetes sans perturber les livraisons.
Nous nous concentrons sur quatre axes de durcissement essentiels : le contrôle d'accès, la gestion des secrets, les permissions et la posture de sécurité globale. Chaque recommandation est accompagnée de commandes concrètes, de sorties attendues, de signaux d'échec et de décisions de récupération. Le principe directeur est la sûreté opérationnelle : observer avant de modifier, limiter le rayon d'impact, ne jamais exposer de secrets, vérifier chaque changement et documenter les procédures de récupération avant qu'un incident ne survienne.
Tout au long de ce guide, nous supposons un cluster Kubernetes fonctionnel (version 1.25 ou supérieure pour la plupart des exemples, bien que les commandes fonctionnent généralement sur des versions antérieures), kubectl configuré avec les accès appropriés, et un espace de noms de pré-production où vous pouvez tester en toute sécurité. Remplacez namespace: default par votre propre espace de noms si nécessaire. Exécutez toujours les commandes en lecture seule en premier pour capturer l'état actuel.
Inventaire des versions et de l'environnement
Avant tout durcissement, vous devez savoir exactement quels contrôleurs d'admission sont actifs, quelle version de Kubernetes vous utilisez, et quel système de gestion de configuration les contrôle. Cet inventaire évite les modifications accidentelles et garantit que vos commandes sont compatibles avec votre cluster.
Étape 1 : Déterminer la version de Kubernetes et les détails du serveur d'API.
kubectl version --short
Sortie attendue (exemple) :
Client Version: v1.26.1
Kustomize Version: v4.5.7
Server Version: v1.26.1
Si --short est obsolète, utilisez kubectl version et recherchez le champ Server Version.
Étape 2 : Lister les contrôleurs d'admission actuellement activés.
Les contrôleurs d'admission activés sont définis sur le serveur d'API. Vous pouvez souvent les voir en inspectant le pod du serveur d'API ou la configuration de votre cluster. Sur un cluster géré (EKS, GKE, AKS), vous n'avez peut-être pas un accès direct aux drapeaux du serveur d'API ; consultez alors la documentation de votre fournisseur. Sur un cluster autogéré (kubeadm, kops, etc.), vérifiez le manifeste du serveur d'API :
# Sur un nœud du plan de contrôle, si vous avez un accès SSH
sudo cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep enable-admission-plugins
Exemple de sortie :
- --enable-admission-plugins=NodeRestriction,PodSecurityPolicy,ServiceAccount,DefaultStorageClass,ResourceQuota
Si vous n'avez pas accès au nœud, vous pouvez déduire certains contrôleurs activés via kubectl api-versions ou en testant des comportements spécifiques. Cependant, la méthode la plus fiable est de vérifier le fichier de configuration du cluster utilisé par votre outil de provisionnement (par exemple, la ConfigMap kubeadm-config).
Étape 3 : Identifier la gestion de configuration.
Déterminez si les contrôleurs d'admission sont configurés via des manifestes de pods statiques, la configuration kubeadm, ou un fournisseur géré. Cela affecte la manière dont vous effectuerez les modifications et les retours en arrière. Par exemple, sur les clusters kubeadm, vous pouvez modifier directement /etc/kubernetes/manifests/kube-apiserver.yaml, mais les modifications sont écrasées lors des mises à niveau si vous ne mettez pas également à jour kubeadm-config. Les clusters gérés peuvent nécessiter des drapeaux CLI ou des paramètres d'API.
Étape 4 : Capturer l'état actuel et les horodatages.
Avant tout changement, enregistrez l'état actuel du serveur d'API et des ressources pertinentes. Utilisez :
kubectl get pods -n kube-system -l component=kube-apiserver -o yaml > apiserver-backup-$(date +%Y%m%d%H%M%S).yaml
Vérifiez également les événements du cluster pour tout problème récent :
kubectl get events --all-namespaces --sort-by=.metadata.creationTimestamp | tail -20
Cela établit une base de référence pour le dépannage si un changement cause des problèmes.
Étape 5 : Comprendre les prérequis et le rayon d'impact.
Chaque contrôleur d'admission peut nécessiter des ressources ou configurations supplémentaires. Par exemple, PodSecurityPolicy (obsolète en 1.21, supprimé en 1.25) nécessite de définir des politiques et des liaisons RBAC. ResourceQuota nécessite de créer des objets de quota. La modification d'un contrôleur d'admission peut affecter toutes les nouvelles charges de travail ; testez donc d'abord dans un environnement hors production.
Vérification pratique : Avant d'activer un nouveau contrôleur d'admission, assurez-vous que ses prérequis sont satisfaits. Par exemple, si vous prévoyez d'activer PodSecurity (le remplaçant de PodSecurityPolicy), vous devez créer des étiquettes de sécurité des pods sur les espaces de noms. Vérifiez avec :
kubectl get namespace my-namespace -o jsonpath='{.metadata.labels}' | jq
Si l'étiquette pod-security.kubernetes.io/enforce est absente, le contrôleur peut ne pas se comporter comme prévu.
Chemin de configuration sûr
Le chemin de configuration sûr consiste à effectuer le plus petit changement possible de manière contrôlée, avec des étapes de retour en arrière claires. Nous nous concentrons ici sur deux scénarios de durcissement courants :
- Activer un contrôleur d'admission recommandé (par exemple,
NodeRestriction) - Renforcer la sécurité des pods via le module intégré Pod Security Admission (PSA)
Exemple 1 : Activer le contrôleur d'admission NodeRestriction
NodeRestriction garantit que les kubelets ne peuvent modifier que leurs propres objets Node et Pod, limitant ainsi l'impact d'un nœud compromis. Il est activé par défaut dans de nombreuses distributions, mais vérifiez et activez-le s'il est absent.
Étape 1 : Vérifier l'état actuel.
Comme décrit dans l'inventaire, inspectez le drapeau --enable-admission-plugins. Si NodeRestriction est absent, poursuivez.
Étape 2 : Sauvegarder le manifeste du serveur d'API.
cp /etc/kubernetes/manifests/kube-apiserver.yaml /root/kube-apiserver.yaml.backup
Étape 3 : Modifier le manifeste et ajouter NodeRestriction à la liste.
À l'aide de votre éditeur préféré, ajoutez NodeRestriction au drapeau --enable-admission-plugins, séparé par des virgules. Par exemple :
- --enable-admission-plugins=NodeRestriction,ServiceAccount,DefaultStorageClass,ResourceQuota
Étape 4 : Attendre le redémarrage du serveur d'API.
Le kubelet redémarre automatiquement les pods statiques lorsque le manifeste change. Surveillez l'état du pod du serveur d'API :
kubectl get pods -n kube-system -l component=kube-apiserver -w
Attendez que le nouveau pod soit Running et que l'ancien soit terminé.
Étape 5 : Vérifier que le contrôleur est actif.
Vérifiez indirectement en testant le comportement de NodeRestriction. Par exemple, essayez de corriger un objet Node en utilisant les informations d'identification du kubelet (si vous les avez). Une vérification moins intrusive consiste à consulter les journaux du serveur d'API pour un message indiquant le chargement du plugin :
kubectl logs -n kube-system <apiserver-pod-name> | grep NodeRestriction
Ligne de journal attendue :
I0210 10:15:30.123456 1 plugins.go:158] Loaded 15 admission controller(s) successfully in the following order: ... NodeRestriction ...
Étape 6 : Retour en arrière si nécessaire.
Si le serveur d'API ne démarre pas, restaurez la sauvegarde :
cp /root/kube-apiserver.yaml.backup /etc/kubernetes/manifests/kube-apiserver.yaml
Exemple 2 : Appliquer les normes de sécurité des pods avec Pod Security Admission
Pod Security Admission (PSA) est le remplaçant moderne de PodSecurityPolicy. Il applique trois niveaux de politique : privileged, baseline et restricted.
Étape 1 : Étiqueter un espace de noms pour appliquer le niveau restreint.
kubectl label namespace my-app pod-security.kubernetes.io/enforce=restricted pod-security.kubernetes.io/enforce-version=latest
Étape 2 : Tester avec un pod conforme et non conforme.
Créez un pod simple qui respecte la politique restreinte (pas d'élévation de privilèges, pas d'espaces de noms hôte, etc.) :
# compliant-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: compliant-pod
namespace: my-app
spec:
containers:
- name: nginx
image: nginx:1.25
securityContext:
runAsNonRoot: true
runAsUser: 1000
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
# non-compliant-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: non-compliant-pod
namespace: my-app
spec:
containers:
- name: nginx
image: nginx:1.25
securityContext:
allowPrivilegeEscalation: true
Appliquez les deux et observez :
kubectl apply -f compliant-pod.yaml
kubectl apply -f non-compliant-pod.yaml
Attendu pour le pod conforme :
pod/compliant-pod created
Pour le pod non conforme, vous verrez une erreur du type :
Error from server (Forbidden): error when creating "non-compliant-pod.yaml": pods "non-compliant-pod" is forbidden: violates PodSecurity "restricted:latest": allowPrivilegeEscalation != false (container "nginx" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "nginx" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "nginx" must set securityContext.runAsNonRoot=true)
Étape 3 : Ajuster les politiques si nécessaire.
Vous pouvez définir les niveaux warn et audit avant d'appliquer enforce pour éviter de casser les charges de travail :
kubectl label namespace my-app pod-security.kubernetes.io/warn=restricted pod-security.kubernetes.io/audit=restricted
Surveillez les journaux d'audit et les avertissements avant de définir enforce.
Vérification :
Vérifiez les étiquettes de l'espace de noms :
kubectl get namespace my-app --show-labels
La sortie attendue inclut les nouvelles étiquettes.
Vérification et diagnostics
La vérification ne consiste pas seulement à confirmer que le changement a pris effet ; elle vise à garantir que la posture de sécurité souhaitée est réellement appliquée. Utilisez une combinaison de vérifications statiques et de tests comportementaux.
Vérifier la configuration du serveur d'API
Si vous avez accès aux journaux ou aux métriques du serveur d'API, recherchez la liste des plugins d'admission activés. La ligne de journal de la section précédente est définitive. De plus, vous pouvez interroger le drapeau --enable-admission-plugins du serveur d'API via la ConfigMap kubeadm-config :
kubectl get configmap kubeadm-config -n kube-system -o jsonpath='{.data.ClusterConfiguration}' | grep enable-admission-plugins
Utiliser l'API Kubernetes pour tester les décisions d'admission
Pour des contrôleurs comme ResourceQuota, vous pouvez tester en essayant de créer une ressource qui dépasse le quota. Pour LimitRanger, testez avec un pod qui dépasse les limites.
Exemple : Valider l'application de ResourceQuota
Créez un quota pour un espace de noms :
# quota.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: pod-quota
namespace: my-app
spec:
hard:
pods: "2"
kubectl apply -f quota.yaml
Essayez de créer trois pods :
kubectl run pod1 --image=nginx --namespace=my-app
kubectl run pod2 --image=nginx --namespace=my-app
kubectl run pod3 --image=nginx --namespace=my-app
La création du troisième pod devrait échouer avec :
Error from server (Forbidden): pods "pod3" is forbidden: exceeded quota: pod-quota, requested: pods=1, used: pods=2, limited: pods=2
Inspecter les journaux d'audit pour les décisions d'admission
Si la journalisation d'audit est activée, vous pouvez voir les décisions des contrôleurs d'admission. Le journal d'audit enregistre chaque requête, y compris la phase d'admission. Pour trouver les requêtes refusées :
# Sur un nœud avec accès au journal d'audit
grep -A5 '"annotations".*admission' /var/log/kubernetes/audit/audit.log | grep '"allowed":false'
Cela aide à identifier les charges de travail bloquées et à ajuster les politiques en conséquence.
Dépannage des problèmes courants
- Le contrôleur d'admission ne prend pas effet : Vérifiez que le pod du serveur d'API a redémarré et qu'il exécute la nouvelle version. Vérifiez les plugins activés dans la spécification du pod ou les journaux.
- Les requêtes échouent après l'activation d'un contrôleur : Vérifiez le message d'erreur exact. Il indique souvent quelle politique est violée. Ajustez la charge de travail ou la politique.
- Permissions RBAC manquantes : Certains contrôleurs d'admission nécessitent des rôles RBAC supplémentaires. Par exemple, le contrôleur
ImagePolicyWebhooknécessite que le serveur d'API ait les permissions d'appeler le service webhook. Assurez-vous que le compte de service utilisé par le serveur d'API possède les rôles nécessaires.
Utilisez kubectl describe et kubectl logs pour creuser :
kubectl describe pod <apiserver-pod> -n kube-system
kubectl logs <apiserver-pod> -n kube-system --previous
Modes de défaillance et récupération
Même avec une planification minutieuse, les modifications des contrôleurs d'admission peuvent provoquer des pannes. Voici les modes de défaillance courants et les étapes de récupération.
Le serveur d'API ne démarre pas
Symptôme : Après avoir modifié le manifeste du serveur d'API, le pod du serveur d'API est en CrashLoopBackOff ou ne démarre pas.
Diagnostic : Consultez les journaux du pod du serveur d'API :
kubectl logs -n kube-system <apiserver-pod> --previous
Recherchez des erreurs telles que « invalid admission plugin » ou des erreurs de syntaxe dans le manifeste.
Récupération : Restaurez la sauvegarde du manifeste d'origine :
cp /root/kube-apiserver.yaml.backup /etc/kubernetes/manifests/kube-apiserver.yaml
Attendez ensuite que le serveur d'API récupère. Vérifiez avec :
kubectl get pods -n kube-system -l component=kube-apiserver
Les charges de travail sont rejetées de manière inattendue
Symptôme : Les nouveaux déploiements ou pods échouent avec des erreurs Forbidden après l'activation d'un contrôleur d'admission (par exemple, Pod Security Admission, ResourceQuota).
Diagnostic : Lisez attentivement le message d'erreur. Il indique quelle politique est violée. Vérifiez les étiquettes de l'espace de noms et les quotas/limites de ressources.
Récupération : Selon la gravité :
- Temporaire : Assouplissez le niveau d'application. Pour PSA, définissez
enforcesur un niveau moins restrictif ou supprimez l'étiquette :
kubectl label namespace my-app pod-security.kubernetes.io/enforce-
- Permanent : Corrigez la charge de travail pour qu'elle soit conforme à la politique. Par exemple, pour le niveau restreint de PSA, ajoutez les champs de contexte de sécurité requis. Réappliquez ensuite l'étiquette
enforce.
Défaillance du contrôleur d'admission webhook
Symptôme : Si vous utilisez un contrôleur d'admission webhook (par exemple, ValidatingWebhookConfiguration) et que le service webhook est indisponible, toutes les requêtes correspondantes peuvent échouer.
Diagnostic : Vérifiez la configuration du webhook et les points de terminaison du service.
kubectl get validatingwebhookconfigurations my-webhook -o yaml
kubectl get endpoints -n webhook-namespace webhook-service
Recherchez failurePolicy dans la configuration du webhook. S'il est défini sur Fail, toute erreur du webhook bloque la requête. S'il est défini sur Ignore, les erreurs sont ignorées (moins sécurisé).
Récupération :
- Immédiate : Si le webhook est critique et défectueux, supprimez temporairement la configuration du webhook pour débloquer les requêtes :
kubectl delete validatingwebhookconfigurations my-webhook
- Permanente : Corrigez le service webhook, puis recréez la configuration avec une
failurePolicyappropriée (de préférenceFailpour la sécurité, mais assurez une haute disponibilité) et des délais d'attente.
NodeRestriction interfère avec les mises à jour des nœuds
Symptôme : Le kubelet ne peut pas mettre à jour son propre statut de nœud ou les pods ne sont pas planifiés correctement.
Diagnostic : Vérifiez les conditions des nœuds et les journaux du kubelet. Si le kubelet ne possède pas les informations d'identification appropriées, il peut être refusé par NodeRestriction.
Récupération : Vérifiez que le kubelet utilise les bons certificats clients et que l'autorisation de nœud est activée en même temps que NodeRestriction. Si vous devez désactiver temporairement NodeRestriction, retirez-le de la liste d'activation et redémarrez le serveur d'API conformément à la procédure de retour en arrière.
Bonne pratique : Testez toujours les modifications des contrôleurs d'admission dans un environnement de préproduction qui reflète la production. Ayez un plan de retour en arrière documenté avant d'appliquer. Utilisez des espaces de noms ou des clusters canaris pour déployer progressivement les modifications.
Liste de contrôle des opérations
Utilisez cette liste de contrôle pour vous assurer que le durcissement de vos contrôleurs d'admission est systématique et sûr.
Liste de contrôle avant modification
- [ ] Identifier la version actuelle de Kubernetes :
kubectl version --short - [ ] Lister les contrôleurs d'admission activés :
grep enable-admission-plugins /etc/kubernetes/manifests/kube-apiserver.yaml(ou méthode spécifique au fournisseur) - [ ] Documenter la configuration actuelle dans le contrôle de version.
- [ ] Sauvegarder le manifeste du serveur d'API :
cp /etc/kubernetes/manifests/kube-apiserver.yaml /root/kube-apiserver.yaml.backup - [ ] S'assurer d'avoir accès à un cluster ou espace de noms de test hors production.
- [ ] Confirmer les permissions RBAC pour effectuer des modifications (cluster-admin ou équivalent).
- [ ] Examiner les ressources prérequises (par exemple, les étiquettes de sécurité des pods, les définitions de ResourceQuota).
Liste de contrôle pendant la modification
- [ ] Appliquer le plus petit changement possible, un contrôleur d'admission à la fois.
- [ ] Utiliser un espace de noms canari pour tester le comportement avant une application à l'échelle du cluster.
- [ ] Surveiller la santé du pod du serveur d'API :
kubectl get pods -n kube-system -l component=kube-apiserver -w - [ ] Vérifier les journaux du serveur d'API pour le chargement réussi des plugins.
- [ ] Exécuter un ensemble de charges de travail de test (conformes et non conformes) pour vérifier l'application.
- [ ] Enregistrer les erreurs et leurs résolutions.
Liste de contrôle de vérification après modification
- [ ] Vérifier que le contrôleur d'admission est actif (journaux, carte de configuration, test de comportement).
- [ ] Confirmer que les charges de travail existantes fonctionnent toujours (ou ont été mises à jour).
- [ ] Vérifier les journaux d'audit pour les refus inattendus.
- [ ] Mettre à jour la documentation et les procédures avec le nouvel état.
- [ ] Informer l'équipe du changement et de son impact potentiel.
Liste de contrôle de préparation à la récupération
- [ ] Plan de retour en arrière documenté : restaurer le manifeste de sauvegarde ou supprimer les étiquettes/configuration.
- [ ] Tester le retour en arrière en préproduction.
- [ ] Définir des métriques ou alertes pour les défaillances des contrôleurs d'admission (par exemple, augmentation des erreurs 4xx/5xx du serveur d'API).
- [ ] Attribuer un propriétaire pour la réponse aux incidents liés aux contrôleurs d'admission.
Conclusion
Les contrôleurs d'admission Kubernetes sont de puissants gardiens qui peuvent améliorer considérablement la sécurité du cluster. Cependant, ils doivent être configurés avec soin. En suivant l'approche structurée de ce guide — inventaire, configuration sûre, vérification et récupération — vous pouvez durcir votre cluster sans provoquer de temps d'arrêt ni bloquer les charges de travail légitimes.
Rappelez-vous ces principes fondamentaux :
- Observer avant de modifier : Capturez toujours l'état actuel et comprenez l'impact.
- Plus petit changement : Modifiez un contrôleur à la fois et testez de manière incrémentale.
- Vérifier avec des tests concrets : Utilisez de vrais pods et requêtes pour confirmer l'application.
- Avoir un plan de retour en arrière : Sachez comment revenir rapidement en arrière si quelque chose se casse.
- Protéger les secrets : N'exposez jamais d'informations d'identification dans les manifestes ou les commandes.
Comme prochaine étape, choisissez une vérification à faible risque de ce guide — par exemple, activer Pod Security Admission en mode audit sur un espace de noms de test. Enregistrez l'état actuel, appliquez le changement, exécutez les tests de vérification et documentez les résultats. Déployez ensuite progressivement vers les espaces de noms de production, en surveillant de près.
Un flux de travail technique fiable rend les échecs visibles, protège les valeurs sensibles, limite les modifications à la ressource prévue et définit la vérification de récupération avant qu'un incident ne force la décision. Grâce à ces pratiques, vous pouvez sécuriser en toute confiance vos clusters Kubernetes avec les contrôleurs d'admission.