Kubernetes securityContext : les capacités Linux dans Kubernetes
26 janvier 2021
0 minutes de lectureIl y a bien longtemps, les systèmes d’exploitation Unix reposaient sur un modèle d’autorisations relativement simple : on était soit un utilisateur standard, soit root, le superutilisateur qui dispose de tous les droits.
Les utilisateurs standard pouvaient certes obtenir des autorisations élevées sur des fichiers ou des répertoires, mais presque toutes les fonctions du noyau étaient réservées à root. Si une application avait besoin d’un seul appel système du noyau pour fonctionner, il fallait lui accorder les privilèges SUID — ce qui conférait de fait les privilèges root à son processus lorsqu’il était lancé par un utilisateur standard.
Cette approche fonctionnait plutôt bien à l’époque où les machines physiques étaient utilisées par un nombre relativement restreint de personnes. Mais cette granularité grossière n’était plus adaptée au monde moderne. Pour offrir davantage de flexibilité et de sécurité, les développeurs du noyau Linux ont mis au point une solution beaucoup plus précise : regrouper les appels système du noyau en capacités et permettre d’attribuer ces capacités à chaque processus. Ainsi, si une application avait besoin d’un seul appel système du noyau, il suffisait de lui attribuer la capacité correspondante, limitant ainsi les risques pour la sécurité du système.
Gérer les privilèges dans les conteneurs Kubernetes
Alors, comment cela fonctionne-t-il dans les conteneurs ?
En réalité, un conteneur n’est qu’un processus exécuté sur le système, isolé au moyen des cgroups et des espaces de noms du noyau. Les capacités peuvent donc lui être attribuées comme à n’importe quel autre processus. C’est le runtime de conteneurs qui s’en charge lorsqu’il crée le conteneur.
En général, le runtime de conteneurs attribue un ensemble de capacités par défaut au conteneur (vous pouvez consulter ici l’ensemble fourni par Docker) et permet également d’ajouter ou de supprimer des capacités. Si vous exécutez un conteneur avec l’option --privileged, vous lui accordez toutes les capacités. Si vous l’exécutez en tant que root, cela peut entraîner un grave problème de sécurité, un sujet que j’ai abordé dans un article précédent.
Définir les capacités d’un conteneur dans le securityContext de Kubernetes
Dans Kubernetes, le plan de contrôle utilise ces paramètres du runtime de conteneurs pour définir les capacités attribuées à notre conteneur au démarrage. L’utilisateur peut configurer les capacités à l’aide de différents paramètres dans la section securityContext du fichier YAML du conteneur. Voici à quoi ressemble cette configuration :
Dans cet exemple, nous supprimerions toutes les capacités, puis ajouterions CAP_NET_ADMIN. Si la section capabilities du securityContext est vide, nous obtenons l’ensemble de capacités par défaut défini par le runtime de conteneurs. Cet ensemble est généralement assez généreux et peut largement dépasser les besoins de notre application. Déployons un pod dans Kubernetes pour voir quelles capacités nous obtenons.
Commençons par créer un fichier YAML pour déployer un pod. Dans cette configuration, nous utilisons une image disponible sur Docker Hub, basée sur Alpine et dotée de l’outil capsh, qui nous permettra de voir les capacités de notre conteneur.
Notez que nous n’avons pas défini de section securityContext pour ce conteneur : tous les paramètres seront donc ceux du système par défaut. Rappelons que si aucun utilisateur n’est spécifié dans Kubernetes, le conteneur s’exécute avec l’utilisateur par défaut indiqué dans le Dockerfile utilisé pour le créer — souvent root.
Une fois le pod opérationnel, nous pouvons ouvrir un shell à l’intérieur et exécuter capsh pour vérifier les capacités :
Supprimer des capacités dans le securityContext de Kubernetes
Nous constatons donc que, par défaut, nous nous exécutons en tant que root et disposons d’un grand nombre de capacités. Il s’agit de l’ensemble défini par défaut par le runtime de conteneurs. Essayons maintenant de supprimer l’une de ces capacités dans les paramètres de notre securityContext : nous allons retirer la possibilité de créer des nœuds de système de fichiers, c’est-à-dire CAP_MKNOD :
Par rapport au premier pod, nous constatons que celui-ci ne dispose plus de la capacité cap_mknod. Nous pouvons également supprimer toutes les capacités dans le securityContext, et pas seulement une capacité à la fois :
Sans aucune capacité, certaines fonctions système échoueront. Essayons, par exemple, d’installer bash avec apk :
Même si notre conteneur s’exécute en tant que root, l’installation du paquet bash échoue, car elle nécessite de définir des autorisations sur le système de fichiers. Or, nous avons supprimé cette capacité de notre conteneur. Si nous voulions empêcher quiconque d’installer des logiciels dans notre conteneur, la gestion des autorisations au moyen des capacités serait une solution.
capsh permet d’afficher les capacités de notre conteneur dans un format lisible, mais ce n’est pas le seul moyen de les connaître. Nous pouvons aussi accéder directement à ces informations dans le système de fichiers proc, sans installer de logiciel supplémentaire :
Dans le fichier /proc/1/status, les capacités sont représentées sous forme de bitmap. Comme aucune capacité n’est activée dans ce conteneur, les valeurs sont toutes nulles. Voyons maintenant le même fichier dans le conteneur d’origine, où toutes les capacités sont activées :
Le résultat est moins lisible que celui de capsh, mais chaque bit correspond à une capacité particulière, comme indiqué dans l’en-tête du noyau correspondant.
Découvrez comment renforcer la sécurité de Kubernetes en supprimant les capacités par défaut d’un conteneur.
Ajouter des capacités dans le securityContext de Kubernetes
En plus de supprimer des capacités, nous pouvons aussi les rétablir. Reprenons notre exemple précédent et ajoutons une seule capacité aux paramètres du securityContext : CAP_MKNOD :
Dans cet exemple, nous constatons que seule cette capacité est désormais activée.
Le principe du moindre privilège dans le securityContext de Kubernetes
Dans cet article, nous avons vu comment fonctionnent les capacités dans les conteneurs, comment les configurer dans le securityContext de Kubernetes et comment le runtime de conteneurs les gère.
Conformément au principe du moindre privilège, la meilleure pratique en matière de sécurité consiste à n’accorder à notre conteneur que les capacités dont il a réellement besoin. J’ai abordé ce sujet dans un article précédent. Il peut être surprenant de constater que la plupart des processus n’ont pas besoin de capacités du noyau : même lorsqu’ils nécessitent des autorisations élevées, celles-ci peuvent généralement être gérées au niveau des fichiers.
Commencez par supprimer toutes les capacités dans le securityContext, puis ajoutez uniquement celles dont vous avez besoin. Pour diagnostiquer les erreurs, vous pouvez consulter la sortie d’outils comme SELinux afin de repérer les capacités susceptibles d’en être la cause. N’oubliez pas non plus que les conteneurs Kubernetes peuvent s’exécuter en tant que root si vous ne spécifiez pas un autre utilisateur.
Appliquer les paramètres de capacités du securityContext dans Kubernetes
Pour garantir la définition des paramètres du securityContext, comme les capacités et l’exécution sans privilèges root, nous pouvons utiliser des contrôleurs d’admission dans notre cluster Kubernetes. Ils empêcheront le lancement de conteneurs qui ne respectent pas les paramètres de sécurité requis. Kubernetes intègre le contrôleur PodSecurityPolicy, qui permet d’appliquer les paramètres du securityContext.
Toutefois, veuillez noter que cette fonctionnalité sera obsolète à partir de la version 1.21, au profit de projets maintenus en externe, comme Open Policy Agent.
Nous devons également intégrer directement à notre processus de développement des mécanismes de visibilité et de correction pour ce type de paramètres de sécurité. Snyk peut analyser vos fichiers YAML Kubernetes, détecter les paramètres non sécurisés du securityContext concernant les capacités et d’autres configurations, et fournir des conseils de correction directement dans les workflows des développeurs. Cette fonctionnalité est disponible via la Snyk CLI et peut également être intégrée directement aux systèmes de gestion du code source et d’intégration continue :

Envie d’essayer cette fonctionnalité ? Créez votre compte gratuit dès aujourd’hui !
Lancez-vous dans les compétitions Capture The Flag
Apprenez à résoudre des défis Capture The Flag en regardant à la demande notre atelier virtuel d’initiation.



