Pourquoi les processus dans les conteneurs Docker ne doivent pas s’exécuter en tant que racine
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.
Sommaire
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 aux 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 le USER
instruction:
FROM base-image:latest RUN apt install demo-package USER demo-user:demo-group ENTRYPOINT ["demo-binary"]
Les conteneurs démarrés à partir de cette image s’exécuteront en tant que demo-user
. L’utilisateur sera membre du demo-group
groupe. Vous pouvez omettre le nom du groupe si vous n’avez pas besoin que l’utilisateur fasse partie d’un groupe :
USER demo-user
Vous pouvez spécifier un ID utilisateur (UID) et un ID de groupe (GID) au lieu de noms :
USER 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. La apt install
l’instruction dans l’exemple ci-dessus a un besoin légitime de root. Si la USER
l’instruction était placée au-dessus, apt
serait exécuté comme 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 :
COPY initial-config.yaml /data/config.yaml USER demo-user:demo-group RUN chown demo-user:demo-group /data
Dans cet exemple le /data
le répertoire doit appartenir à demo-user
afin que l’application puisse apporter des modifications à son fichier de configuration. Le plus tôt COPY
instruction aura copié le fichier en tant que root. Un raccourci est disponible en utilisant le --chown
drapeau avec copy
:
COPY --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 le --user
signaler 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
La --user
flag exécute le processus du conteneur en tant qu’utilisateur spécifié. C’est moins sûr que le Dockerfile USER
instruction parce que vous devez l’appliquer individuellement à chaque docker run
commande. 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-that-runs-as-root:latest USER demo-user
$ docker build . -t image-that-now-runs-as-non-root:latest
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
terrain à votre /etc/docker/daemon.json
dossier:
{ "userns-remap": "default" }
Utilisant default
comme valeur pour userns-remap
demande à 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ébergeur. 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 :
$ sudo service docker restart
Si vous utilisez nsuser-remap: default
la dockremap
l’utilisateur devrait maintenant exister sur votre hôte :
$ id dockremap uid=140(dockremap) gid=119(dockremap) groups=119(dockremap)
L’utilisateur doit également apparaître dans le /etc/subuid
et /etc/subgid
fichiers ID subordonnés :
$ dockremap:231500:65535
L’utilisateur s’est vu attribuer une plage de 65 535 ID subordonnés à partir de 231 500. Dans l’espace de noms d’utilisateur, ID 231500
est mappé à 0
, ce qui en fait l’utilisateur racine de 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 à l’intérieur /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 Jul 22 19:00 aufs drwx------ 3 231500 231500 13 Jul 22 19:00 containers ...
L’espacement des noms d’utilisateurs est un moyen efficace d’augmenter l’isolation 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’images, vous devez inclure le USER
instruction dans votre Dockerfile afin que votre application s’exécute sans racine. 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 de --cap-drop=ALL
puis en ajoutant à la liste blanche ceux qui sont requis avec --cap-add
drapeaux. 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é.