Pourquoi les processus dans les conteneurs Docker ne doivent pas s'exécuter en tant que root
Publié: 2022-08-19 Les processus dans un conteneur Docker ne doivent pas être exécutés en tant que root. Il est plus sûr d'exécuter vos applications en tant qu'utilisateur non root que vous spécifiez dans le cadre de votre Dockerfile ou lors de l'utilisation docker run
. Cela minimise les risques en présentant une surface d'attaque réduite à toutes les menaces dans votre conteneur.
Dans cet article, vous découvrirez les dangers liés à l'exécution d'applications conteneurisées en tant que root. Vous verrez également comment créer un utilisateur non root et configurer l'espacement des noms dans les situations où cela n'est pas possible.
Pourquoi courir en tant que root est-il dangereux ?
Les conteneurs sont exécutés en tant que root par défaut. Le démon Docker s'exécute en tant que root sur votre hôte et les conteneurs en cours d'exécution seront également root.
Bien qu'il puisse sembler que root à l'intérieur du conteneur soit un utilisateur indépendant, il s'agit en fait du même compte root sur votre hôte. La séparation est uniquement assurée par les mécanismes d'isolation des conteneurs de Docker. Il n'y a pas de frontière physique forte; votre conteneur est un autre processus exécuté par l'utilisateur root sur le noyau de votre hôte. Cela signifie qu'une vulnérabilité dans votre application, l'environnement d'exécution Docker ou le noyau Linux pourrait permettre à des attaquants de sortir du conteneur et d'effectuer des opérations avec privilèges root sur votre machine.
Certaines protections intégrées réduisent le risque que cela se produise. La racine à l'intérieur du conteneur n'est pas privilégiée et a des capacités restreintes. Cela empêche le conteneur d'utiliser les commandes d'administration système, sauf si vous ajoutez manuellement des fonctionnalités ou utilisez le mode privilégié lorsque vous démarrez vos conteneurs.
Malgré cette atténuation, permettre aux applications de s'exécuter en tant que root reste un danger. Tout comme vous limiteriez l'utilisation de root dans un environnement traditionnel, il n'est pas judicieux de l'utiliser inutilement dans vos conteneurs. Vous fournissez un environnement surprivilégié qui donne aux attaquants une meilleure prise en main en cas de violation.
Exécution d'applications conteneurisées en tant qu'utilisateur non root
Il est recommandé que les applications conteneurisées s'exécutent en tant qu'utilisateur standard. La plupart des logiciels n'ont pas besoin d'un accès root, donc le changement d'utilisateur fournit une couche de défense immédiate contre l'évasion du conteneur.
Vous devez créer un nouveau compte utilisateur comme l'une des dernières étapes de votre Dockerfile. Vous pouvez y parvenir avec l'instruction USER
:
FROM image de base : la plus récente RUN apt install démo-package USER demo-user:demo-group ENTRYPOINT ["démo-binaire"]
Les conteneurs démarrés à partir de cette image s'exécuteront en tant que demo-user
. L'utilisateur sera membre du groupe demo-group
group. Vous pouvez omettre le nom du groupe si vous n'avez pas besoin que l'utilisateur fasse partie d'un groupe :
USER démo-utilisateur
Vous pouvez spécifier un ID utilisateur (UID) et un ID de groupe (GID) au lieu de noms :
UTILISATEUR 950:950
L'attribution d'un UID et d'un GID connus est généralement la manière la plus sûre de procéder. Il empêche l'utilisateur du conteneur d'être mappé sur un compte hôte disposant de trop de privilèges.
USER
est souvent spécifié comme l'avant-dernière étape d'un Dockerfile. Cela signifie que vous pouvez toujours exécuter des opérations nécessitant root plus tôt dans la construction de l'image. L'instruction d' apt install
dans l'exemple ci-dessus a un besoin légitime de root. Si l'instruction USER
était placée au-dessus, apt
serait exécuté en tant demo-user
qui n'aurait pas les autorisations nécessaires. Comme les instructions Dockerfile ne s'appliquent qu'aux versions d'image, et non aux conteneurs en cours d'exécution, il est prudent de laisser le changement d'utilisateur plus tard dans votre Dockerfile.
La modification de l'utilisateur sous lequel votre conteneur s'exécute peut nécessiter la mise à jour des autorisations sur les fichiers et dossiers auxquels il accède. Définissez la propriété sur tous les chemins qui seront utilisés par votre application :
COPIER initial-config.yaml /data/config.yaml USER demo-user:demo-group RUN chown demo-user:demo-group /data
Dans cet exemple, le répertoire /data
doit appartenir à demo-user
afin que l'application puisse apporter des modifications à son fichier de configuration. L'instruction COPY
précédente aura copié le fichier en tant que root. Un raccourci est disponible en utilisant le drapeau --chown
avec copy
:
COPIER --chown=demo-user:demo-group initial-config.yaml /data/config.yaml
Modification de l'utilisateur lors du démarrage d'un conteneur
Bien que vous puissiez facilement changer l'utilisateur dans vos propres Dockerfiles, de nombreuses applications tierces continuent de s'exécuter en tant que root. Vous pouvez réduire le risque associé à leur utilisation en définissant l'indicateur --user
chaque fois que vous appelez docker run
. Cela remplace l'utilisateur défini dans le Dockerfile de l'image.
$ docker run -d --user demo-user:demo-group demo-image:latest $ docker run -d --user demo-user demo-image:latest $ docker run -d --user 950:950 demo-image:latest
L'indicateur --user
exécute le processus du conteneur en tant qu'utilisateur spécifié. C'est moins sûr que l'instruction Dockerfile USER
car vous devez l'appliquer individuellement à chaque commande docker run
. Une meilleure option pour les images régulièrement utilisées consiste à créer votre propre image dérivée qui peut définir un nouveau compte utilisateur :
FROM image qui s'exécute en tant que root : dernière USER démo-utilisateur
$ docker build . -t image-qui-s'exécute-maintenant-en-tant-que-non-root : la plus récente
Changer l'utilisateur d'une image tierce peut causer des problèmes : si le conteneur s'attend à être exécuté en tant que root ou doit accéder aux chemins du système de fichiers appartenant à root, vous verrez des erreurs lorsque vous utiliserez l'application. Vous pouvez essayer de modifier manuellement les autorisations sur les chemins qui causent des problèmes. Vous pouvez également vérifier si le fournisseur dispose d'une méthode prise en charge pour exécuter l'application avec un compte d'utilisateur non privilégié.
Gestion des applications qui doivent s'exécuter en tant que root
L'espacement des noms d'utilisateurs est une technique permettant de traiter les applications nécessitant certains privilèges root. Il vous permet de mapper root à l'intérieur d'un conteneur vers un utilisateur non root sur votre hôte. La racine simulée à l'intérieur du conteneur dispose des privilèges dont elle a besoin, mais une évasion ne fournira pas d'accès racine à l'hôte.
Le remappage de l'espace de noms est activé en ajoutant un userns-remap
à votre fichier /etc/docker/daemon.json
:
{ "userns-remap": "par défaut" }
L'utilisation de default
comme valeur pour userns-remap
indique à Docker de créer automatiquement un nouvel utilisateur sur votre hôte appelé dockremap
. La racine dans les conteneurs sera mappée vers dockremap
sur votre hôte. Vous pouvez éventuellement spécifier un utilisateur et un groupe existants à la place, en utilisant une combinaison UID/GID ou nom d'utilisateur/nom de groupe :
{ "userns-remap": "demo-user" }
Redémarrez le démon Docker après avoir appliqué votre modification :
$ redémarrage du menu fixe du service sudo
Si vous utilisez nsuser-remap: default
, l'utilisateur dockremap
devrait maintenant exister sur votre hôte :
$ id dockremap uid=140(dockremap) gid=119(dockremap) groupes=119(dockremap)
L'utilisateur doit également apparaître dans les fichiers d'ID subordonnés /etc/subuid
et /etc/subgid
:
$ dockremap:231500:65535
L'utilisateur s'est vu attribuer une plage de 65 535 ID subordonnés à partir de 231500. Dans l'espace de noms d'utilisateur, l'ID 231500
est mappé sur 0
, ce qui en fait l'utilisateur racine dans vos conteneurs. Étant un UID à numéro élevé, 231500 n'a aucun privilège sur l'hôte, de sorte que les attaques par évasion de conteneurs ne pourront pas infliger autant de dégâts.
Tous les conteneurs que vous démarrez s'exécuteront à l'aide de l'espace de noms d'utilisateur remappé, sauf si vous vous désabonnez avec docker run --userns=host
. Le mécanisme fonctionne en créant des répertoires avec espace de noms dans /var/lib/docker
qui appartiennent à l'UID et au GID subordonnés de l'utilisateur avec espace de noms :
$ sudo ls -l /var/lib/docker/231500.231500 total 14 drwx------ 5 231500 231500 13 Juil 22 19:00 aufs drwx------ 3 231500 231500 13 juillet 22 19:00 conteneurs ...
L'espacement des noms d'utilisateurs est un moyen efficace d'augmenter l'isolement des conteneurs, d'éviter les interruptions et de préserver la compatibilité avec les applications nécessitant des privilèges root. Il y a cependant quelques compromis : la fonctionnalité fonctionne mieux sur une nouvelle instance Docker, les volumes montés à partir de l'hôte doivent avoir leurs autorisations ajustées et certains pilotes de stockage externes ne prennent pas du tout en charge le mappage des utilisateurs. Vous devez consulter la documentation avant d'adopter cette option.
Sommaire
L'exécution d'applications conteneurisées en tant que root représente un risque pour la sécurité. Bien qu'il soit facile de l'oublier, l'isolement fourni par les conteneurs n'est pas assez fort pour séparer complètement les utilisateurs du noyau des utilisateurs du conteneur. La racine dans le conteneur est la même que la racine sur votre hôte, donc un compromis réussi pourrait fournir le contrôle de votre machine.
En tant qu'auteur d'image, vous devez inclure l'instruction USER
dans votre Dockerfile afin que votre application s'exécute sans root. Les utilisateurs d'images peuvent remplacer cela avec docker run --user
pour attribuer un UID et un GID spécifiques. Cela permet d'atténuer les cas où l'image utilise normalement la racine.
Vous pouvez encore renforcer la sécurité en supprimant toutes les fonctionnalités du conteneur à l'aide --cap-drop=ALL
, puis en mettant sur liste blanche celles qui sont requises avec --cap-add
. La combinaison de ces techniques exécutera votre application en tant qu'utilisateur non root avec l'ensemble minimum de privilèges dont elle a besoin, améliorant ainsi votre posture de sécurité.