Introduction
Les plugins de périphériques Kubernetes sont un point d'extension critique pour exposer du matériel spécialisé tel que les GPU, FPGA, adaptateurs InfiniBand et autres accélérateurs aux charges de travail conteneurisées. Cependant, leur nature privilégiée et leur interaction directe avec la kubelet en font une cible privilégiée pour les erreurs de configuration de sécurité pouvant conduire à la compromission du nœud, à l'épuisement des ressources ou à un accès non autorisé au matériel. Cet article fournit un guide pratique étape par étape pour renforcer la sécurité des plugins de périphériques Kubernetes, en mettant l'accent sur le versionnage, le contrôle d'accès, la gestion des secrets et le resserrement des permissions. Contrairement aux listes de contrôle génériques, nous utiliserons des commandes concrètes, des sorties attendues et des signaux d'échec pour garantir que chaque recommandation est observable et récupérable.
Le public cible comprend les ingénieurs DevOps, les administrateurs de plateforme et les équipes techniques de startups qui gèrent des clusters Kubernetes en production et doivent sécuriser les plugins de périphériques sans casser le fonctionnement des charges de travail. Nous supposons une familiarité avec les concepts de base de Kubernetes tels que les pods, les nœuds, la kubelet et le RBAC (Role-Based Access Control, contrôle d'accès basé sur les rôles), mais nous expliquerons en détail les spécificités des plugins de périphériques. L'objectif est la sécurité opérationnelle : observer avant de modifier, limiter le rayon d'impact, utiliser des espaces réservés au lieu de secrets, vérifier les résultats et documenter les procédures de récupération.
Avant d'apporter des modifications, il est essentiel de comprendre l'état actuel de votre cluster. Cela inclut l'identification de la version de Kubernetes installée, de la topologie de déploiement du plugin de périphériques et du composant exact inspecté. Nous commencerons par un inventaire des versions et de l'environnement, puis passerons aux chemins de configuration sûrs, à la vérification et aux diagnostics, aux modes de défaillance et à la récupération, et enfin à une liste de contrôle opérationnelle qui relie le tout.
Inventaire des versions et de l'environnement
Avant de durcir les plugins de périphériques Kubernetes, vous devez savoir exactement ce que vous exécutez. L'API du plugin de périphériques de la kubelet a évolué au fil des versions de Kubernetes, et les fonctionnalités de sécurité telles que la porte de fonctionnalité DevicePlugins ou l'API PodResources peuvent différer. Par exemple, Kubernetes 1.26 a introduit la porte de fonctionnalité DevicePluginCDIDevices (alpha) pour la prise en charge de la Container Device Interface (CDI), tandis que les versions antérieures reposent uniquement sur l'API du plugin de périphériques. Par conséquent, toutes les recommandations de cet article sont versionnées ; vérifiez toujours par rapport à la version de votre cluster.
Commandes d'inventaire
Commencez par recueillir des informations sur le cluster et les nœuds à l'aide de commandes en lecture seule. Capturez la version du serveur Kubernetes :
kubectl version --short
Sortie attendue (exemple) :
Client Version: v1.28.2
Kustomize Version: v5.0.4-0.20230601165947-6ce0bf390ce3
Server Version: v1.28.3
Ensuite, listez tous les nœuds et leurs versions de kubelet :
kubectl get nodes -o wide
Faites attention à la colonne VERSION ; la version de la kubelet peut différer légèrement de la version du serveur API. La compatibilité du plugin de périphériques dépend souvent de la version de l'API du plugin de périphériques de la kubelet, qui est négociée au moment de l'enregistrement.
Pour voir tous les pods liés aux plugins de périphériques, utilisez :
kubectl get pods -A -o wide | grep -E 'device-plugin|nvidia|intel|fpga|sriov'
Les plugins de périphériques courants incluent l'opérateur GPU NVIDIA (qui déploie nvidia-device-plugin-daemonset), l'opérateur Intel Device Plugins, le plugin de périphérique réseau SR-IOV et d'autres. Identifiez le daemonset ou le déploiement responsable de votre matériel.
Vérification de l'enregistrement du plugin de périphériques
Les plugins de périphériques s'enregistrent auprès de la kubelet via un socket Unix sous /var/lib/kubelet/device-plugins/. Vous pouvez inspecter les plugins enregistrés en examinant les journaux de la kubelet ou en utilisant l'API kubelet (si activée). Une méthode plus simple consiste à vérifier les ressources allouables du nœud :
kubectl describe node <node-name> | grep -A5 'Allocatable'
Si un plugin de périphérique GPU fonctionne, vous devriez voir quelque chose comme nvidia.com/gpu: 2 sous allouable. Sinon, le plugin peut avoir échoué à s'enregistrer.
Pour inspecter les journaux du pod du plugin de périphériques :
kubectl logs -n kube-system <device-plugin-pod-name> --tail=50
Recherchez des messages de succès d'enregistrement tels que Device plugin registered ou des erreurs comme Failed to start device plugin.
Prérequis de l'environnement
Assurez-vous que les prérequis suivants sont remplis avant le durcissement :
- La kubelet doit avoir la porte de fonctionnalité
DevicePluginsactivée (vrai par défaut depuis Kubernetes 1.10). - Les montages hôtes requis et l'accès privilégié du plugin de périphériques doivent être compris et minimisés.
- Les normes de sécurité des pods (Pod Security Standards, PSS) ou les politiques de sécurité des pods (Pod Security Policies, dépréciées) du cluster peuvent affecter les déploiements de plugins de périphériques.
- Le binaire du plugin de périphériques et l'image du conteneur doivent provenir d'une source fiable et être épinglés à une version.
Observation vs intervention
À ce stade, effectuez uniquement des observations en lecture seule. Ne modifiez aucune ressource. Enregistrez l'état actuel, y compris les horodatages, afin de pouvoir comparer avant et après les modifications. Par exemple, enregistrez la sortie de kubectl get pods -A -o yaml > before-device-plugins.yaml dans un emplacement sécurisé.
Chemin de configuration sûr
Maintenant que vous avez un inventaire, vous pouvez planifier les modifications de configuration. Le principe du moindre privilège s'applique : n'accordez au plugin de périphériques que les permissions dont il a besoin pour fonctionner, et rien de plus. Les plugins de périphériques s'exécutent généralement en tant que conteneurs privilégiés ou avec hostPID et hostNetwork, mais nous pouvons restreindre certaines capacités et utiliser des contextes de sécurité.
Comprendre les permissions des plugins de périphériques
Les plugins de périphériques nécessitent ce qui suit pour fonctionner :
- Accès au socket du plugin de périphériques de la kubelet : généralement monté depuis
/var/lib/kubelet/device-plugins/. - Accès aux nœuds de périphériques matériels, par exemple
/dev/nvidia0,/dev/dri/renderD128. - Éventuellement des informations système hôtes via
/sysou/proc. - Le mode privilégié peut être nécessaire pour accéder à certains matériels, mais il peut souvent être remplacé par des capacités spécifiques.
Exemple : durcissement du plugin de périphériques NVIDIA
Prenons le plugin de périphériques NVIDIA comme exemple. Le déploiement par défaut de NVIDIA utilise un conteneur privilégié avec de nombreux montages hôtes. Nous pouvons le durcir en :
- Supprimant les montages hôtes inutiles.
- Ajoutant un utilisateur non root (si pris en charge).
- Abandonnant toutes les capacités et en ajoutant uniquement celles nécessaires.
- Utilisant
readOnlyRootFilesystemlorsque c'est possible.
Voici un extrait avant et après de la spécification de pod du DaemonSet :
Avant (non sécurisé) :
containers:
- name: nvidia-device-plugin
image: nvcr.io/nvidia/k8s-device-plugin:v0.14.3
securityContext:
privileged: true
volumeMounts:
- name: device-plugin
mountPath: /var/lib/kubelet/device-plugins
- name: dev
mountPath: /dev
Après (durci) :
containers:
- name: nvidia-device-plugin
image: nvcr.io/nvidia/k8s-device-plugin:v0.14.3
securityContext:
privileged: false
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
add: ["SYS_ADMIN"] # nécessaire pour l'accès au pilote NVIDIA ; ajuster selon les exigences du plugin
readOnlyRootFilesystem: true
volumeMounts:
- name: device-plugin
mountPath: /var/lib/kubelet/device-plugins
- name: nvidia-devices
mountPath: /dev/nvidia0
readOnly: true
- name: nvidiactl
mountPath: /dev/nvidiactl
readOnly: true
Remarque : les capacités exactes requises dépendent de la version du plugin et du matériel. Testez soigneusement.
Contrôle d'accès pour les ressources des plugins de périphériques
Les plugins de périphériques étendent l'API Kubernetes avec des ressources personnalisées (par exemple, nvidia.com/gpu). Pour contrôler quels utilisateurs ou comptes de service peuvent demander ces ressources, utilisez le RBAC (Role-Based Access Control, contrôle d'accès basé sur les rôles) et les quotas de ressources. Par exemple :
apiVersion: v1
kind: ResourceQuota
metadata:
name: gpu-quota
namespace: team-a
spec:
hard:
requests.nvidia.com/gpu: "2"
Limitez le nombre de GPU qu'un espace de noms peut demander via LimitRange :
apiVersion: v1
kind: LimitRange
metadata:
name: gpu-limit
namespace: team-a
spec:
limits:
- max:
nvidia.com/gpu: "1"
defaultRequest:
nvidia.com/gpu: "1"
type: Container
Gestion des secrets
Les plugins de périphériques peuvent nécessiter des informations d'identification pour les licences ou les pilotes propriétaires. Évitez de coder en dur les secrets dans les spécifications de pod. Utilisez les secrets Kubernetes et montez-les en tant que volumes ou variables d'environnement. Par exemple :
kubectl create secret generic nvidia-license --from-file=license.jwt=./license.jwt
Ensuite, référencez-le dans le DaemonSet :
volumeMounts:
- name: license
mountPath: /etc/nvidia/license
readOnly: true
volumes:
- name: license
secret:
secretName: nvidia-license
Ne stockez jamais de secrets dans des ConfigMaps ou dans des images de conteneurs.
Application des modifications en toute sécurité
Avant d'appliquer toute modification de configuration, appliquez-la à un nœud ou un espace de noms de test. Utilisez une stratégie de déploiement canari : modifiez le DaemonSet avec kubectl patch ou modifiez et appliquez à un sous-ensemble en utilisant des sélecteurs de nœuds.
Par exemple, pour mettre à jour uniquement les nœuds étiquetés test=true :
nodeSelector:
test: "true"
Surveillez le déploiement :
kubectl rollout status daemonset/nvidia-device-plugin -n kube-system
Si le déploiement échoue, annulez immédiatement.
Vérification et diagnostics
Après avoir appliqué les modifications de durcissement, vous devez vérifier que le plugin de périphériques fonctionne toujours correctement. Cela implique de vérifier l'enregistrement, de tester l'allocation et d'exécuter des charges de travail réelles.
Vérification de l'enregistrement
Tout d'abord, assurez-vous que le plugin s'est réenregistré auprès de la kubelet. Consultez les journaux de la kubelet ou utilisez kubectl describe node pour voir si la ressource est toujours allouable :
kubectl describe node <node-name> | grep -A2 'Allocatable'
La sortie attendue doit inclure le nombre de ressources du périphérique.
Test de l'allocation avec un pod
Créez un pod de test qui demande la ressource du périphérique :
apiVersion: v1
kind: Pod
metadata:
name: gpu-test
spec:
restartPolicy: Never
containers:
- name: cuda-vector-add
image: nvcr.io/nvidia/k8s/cuda-sample:vectoradd-cuda11.7.1-ubuntu20.04
resources:
limits:
nvidia.com/gpu: 1
Appliquez et surveillez le pod :
kubectl apply -f gpu-test.yaml
kubectl get pods gpu-test
Si le pod s'exécute et se termine avec le code de sortie 0, le plugin de périphériques fonctionne. Vérifiez les journaux :
kubectl logs gpu-test
Pour les plugins non GPU, utilisez des conteneurs de test équivalents.
Diagnostic des échecs
Si le pod reste en Pending, exécutez :
kubectl describe pod gpu-test
Recherchez des événements comme Insufficient nvidia.com/gpu. Cela pourrait indiquer que le plugin ne s'est pas enregistré après le durcissement.
Vérifiez les journaux du pod du plugin :
kubectl logs -n kube-system <device-plugin-pod> --previous
Si le pod est en boucle de crash, inspectez la raison.
Vérification de la sécurité
Vérifiez que les modifications du contexte de sécurité ont pris effet. Pour un pod de plugin de périphériques en cours d'exécution, exécutez :
kubectl exec -n kube-system <device-plugin-pod> -- cat /proc/1/status | grep Cap
La sortie attendue doit montrer des capacités limitées (par exemple, CapEff: 00000000a80425fb). Comparez avec la valeur précédente.
Vérifiez également que le conteneur ne s'exécute pas en tant que root si ce n'est pas prévu :
kubectl exec -n kube-system <device-plugin-pod> -- id
Si l'utilisateur est root, demandez-vous si c'est nécessaire et sinon, ajoutez runAsNonRoot et runAsUser au contexte de sécurité.
Modes de défaillance et récupération
Même avec une planification minutieuse, le durcissement peut casser la fonctionnalité du plugin de périphériques. Cette section couvre les modes de défaillance courants et les procédures de récupération.
Modes de défaillance courants
- Le plugin ne s'enregistre pas : généralement dû à des permissions de socket manquantes ou à des chemins de montage incorrects. La kubelet ne signalera pas la ressource et les pods qui la demandent seront non planifiables.
- Le plugin plante en raison de la réduction des privilèges : si les capacités sont abandonnées de manière trop agressive, le plugin peut ne pas accéder au matériel ou aux fichiers système.
- Mauvaise configuration des secrets : si un secret de licence n'est pas monté correctement, le plugin peut ne pas démarrer.
- Inadéquation du sélecteur de nœuds : après les modifications, le plugin peut ne pas se déployer sur tous les nœuds prévus, laissant certains sans prise en charge du périphérique.
Étapes de récupération
Ayez toujours un plan de restauration. Pour les DaemonSets, vous pouvez revenir à la révision précédente :
kubectl rollout undo daemonset/nvidia-device-plugin -n kube-system
Si la restauration n'est pas possible, restaurez à partir de votre sauvegarde YAML enregistrée :
kubectl apply -f before-device-plugins.yaml
Pour les configurations avec état, utilisez kubectl rollout history pour voir les révisions :
kubectl rollout history daemonset/nvidia-device-plugin -n kube-system
Exemple de scénario de récupération
Supposons qu'après le durcissement, le pod de test GPU échoue avec Insufficient nvidia.com/gpu. Étapes :
- Vérifiez les ressources allouables du nœud :
kubectl describe node <node> | grep nvidia- si absent, le plugin ne s'est pas enregistré. - Vérifiez l'état du pod du plugin :
kubectl get pods -n kube-system | grep nvidia. - Consultez les journaux :
kubectl logs -n kube-system <pod> --previous. - Si les journaux montrent des erreurs de permission refusée, ajustez les capacités ou les montages.
- Si tout échoue, restaurez et enquêtez dans un environnement de préproduction.
Testez toujours la récupération dans un environnement non production d'abord pour vous assurer que la procédure fonctionne.
Liste de contrôle opérationnelle
Utilisez cette liste de contrôle pour durcir systématiquement les plugins de périphériques Kubernetes dans votre cluster tout en minimisant les risques.
Liste de contrôle avant modification
- [ ] Enregistrez la version actuelle de Kubernetes et la version de la kubelet.
- [ ] Identifiez tous les déploiements de plugins de périphériques (DaemonSets, Deployments) et leurs espaces de noms.
- [ ] Notez les contextes de sécurité actuels, les montages de volumes et les secrets utilisés.
- [ ] Sauvegardez les manifestes YAML actuels :
kubectl get daemonset -n kube-system <name> -o yaml > backup.yaml. - [ ] Créez un plan de test : quels nœuds tester, quelles charges de travail exécuter.
- [ ] Assurez-vous d'avoir les permissions RBAC (Role-Based Access Control, contrôle d'accès basé sur les rôles) cluster-admin ou suffisantes pour apporter des modifications.
Liste de contrôle de mise en œuvre des modifications
- [ ] Modifiez le contexte de sécurité : définissez
privileged: false, abandonnez les capacités, ajoutez uniquement celles requises. - [ ] Supprimez les montages hôtes inutiles, restreignez les montages
/devà des nœuds de périphériques spécifiques. - [ ] Utilisez
readOnlyRootFilesystemlorsque c'est possible. - [ ] Si le plugin prend en charge le non-root, définissez
runAsNonRoot: trueet unrunAsUserapproprié. - [ ] Utilisez les secrets Kubernetes pour toutes les informations d'identification ou licences.
- [ ] Appliquez les modifications à un sous-ensemble de test en utilisant des sélecteurs de nœuds.
- [ ] Surveillez le déploiement :
kubectl rollout status daemonset/<name> -n kube-system. - [ ] Si le déploiement échoue, annulez immédiatement.
Liste de contrôle de vérification après modification
- [ ] Confirmez que les ressources allouables du nœud montrent les ressources de périphériques attendues.
- [ ] Exécutez un pod de test demandant le périphérique et vérifiez qu'il réussit.
- [ ] Vérifiez le contexte de sécurité du pod du plugin de périphériques :
kubectl get pod -n kube-system <pod> -o jsonpath='{.spec.containers[0].securityContext}'. - [ ] Vérifiez que les secrets sont montés correctement sans être exposés dans les journaux.
- [ ] Surveillez les événements du cluster pour détecter toute anomalie :
kubectl get events -A --sort-by='.lastTimestamp'. - [ ] Mettez à jour la documentation et les runbooks avec les nouveaux paramètres de sécurité.
Surveillance continue
- [ ] Configurez des alertes pour les échecs de pod du plugin de périphériques ou l'indisponibilité des ressources.
- [ ] Examinez périodiquement les permissions des plugins de périphériques et respectez le principe du moindre privilège.
- [ ] Surveillez les nouvelles versions de Kubernetes et les versions des plugins de périphériques pour les correctifs de sécurité.
Conclusion
Le durcissement des plugins de périphériques Kubernetes est essentiel pour maintenir la sécurité du cluster et garantir que le matériel spécialisé est utilisé en toute sécurité. En suivant l'approche structurée de cet article - en commençant par un inventaire approfondi, en appliquant des modifications de configuration sûres, en vérifiant la fonctionnalité et en se préparant à la récupération - vous pouvez réduire la surface d'attaque tout en maintenant la fiabilité opérationnelle.
N'oubliez pas que chaque recommandation doit être versionnée, observable et réversible. Copier une commande sans vérifier les prérequis et la sortie attendue n'est pas une procédure opérationnelle. Comme prochaine étape, choisissez une vérification à faible risque pour votre plugin de périphériques, enregistrez l'état actuel, exécutez la vérification documentée et comparez le résultat avec le signal attendu. Passez en revue les dépendances telles que Node, Kubelet et Topology Manager car elles affectent la compatibilité et la sécurité.
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 la récupération avant qu'un incident ne force la décision. Avec ces pratiques, vous pouvez sécuriser en toute confiance vos plugins de périphériques Kubernetes et maintenir une infrastructure robuste.