Contactez-nous

Ne pas exécuter en tant que root dans le conteneur si possible

Comprenez les risques de sécurité liés à l'exécution en tant que root dans les conteneurs Docker et apprenez à configurer vos images pour utiliser un utilisateur non privilégié.

Le privilège root dans les conteneurs : une facilité dangereuse

Par défaut, de nombreuses images Docker et les instructions exécutées à l'intérieur d'un conteneur (`RUN`, `CMD`, `ENTRYPOINT`) le sont avec l'utilisateur `root` (identifiant utilisateur UID 0). Cette configuration par défaut offre une grande flexibilité lors de la construction de l'image (installation de paquets, modification de fichiers système), mais elle représente un risque de sécurité non négligeable pour l'exécution de l'application elle-même.

Le principe fondamental de sécurité dit du "moindre privilège" stipule qu'un processus ne devrait disposer que des permissions strictement nécessaires à l'accomplissement de sa tâche. Exécuter votre application en tant que `root` à l'intérieur du conteneur viole directement ce principe. Même si Docker fournit une couche d'isolation par rapport à l'hôte, un processus `root` à l'intérieur du conteneur dispose de privilèges étendus au sein de cet environnement isolé.

Adopter la bonne pratique consistant à exécuter vos processus applicatifs avec un utilisateur non privilégié (non-root) est une étape essentielle pour renforcer la sécurité de vos conteneurs et réduire la surface d'attaque potentielle.

Quels sont les risques concrets d'une exécution en root ?

Si votre application, s'exécutant en tant que `root` dans le conteneur, est compromise (par exemple, via une vulnérabilité de type injection de code ou exécution de commande à distance), l'attaquant obtient immédiatement les privilèges `root` à l'intérieur de ce conteneur. Cela peut avoir plusieurs conséquences graves :

1. Modification du système de fichiers du conteneur : L'attaquant peut lire, modifier ou supprimer n'importe quel fichier à l'intérieur du conteneur, y compris les binaires de l'application, les fichiers de configuration ou les données temporaires.

2. Installation de logiciels malveillants : Avec les privilèges `root`, l'attaquant peut facilement installer des outils supplémentaires, des portes dérobées (backdoors) ou des mineurs de cryptomonnaie à l'intérieur du conteneur.

3. Accès aux secrets et données montées : Si des volumes ou des bind mounts sont utilisés pour injecter des fichiers de configuration, des secrets ou des données persistantes dans le conteneur, un processus `root` compromis aura généralement un accès illimité à ces données montées, potentiellement bien au-delà de ce dont l'application a réellement besoin.

4. Facilitation de l'évasion de conteneur (plus rare) : Bien que Docker s'efforce de maintenir une isolation robuste, une compromission en tant que `root` à l'intérieur pourrait, dans le cas d'une vulnérabilité du noyau ou de Docker lui-même, potentiellement offrir plus de possibilités à un attaquant pour tenter d'échapper à l'isolation du conteneur et accéder au système hôte.

En exécutant l'application avec un utilisateur non privilégié, même si l'application est compromise, les actions de l'attaquant seront limitées par les permissions de cet utilisateur spécifique à l'intérieur du conteneur, réduisant considérablement les dommages potentiels.

Mettre en place un utilisateur non-root : étapes dans le Dockerfile

La configuration pour exécuter en tant qu'utilisateur non-root se fait principalement dans le `Dockerfile`. Voici les étapes typiques :

1. Créer un groupe et un utilisateur dédiés : Utilisez l'instruction `RUN` pour ajouter un groupe système et un utilisateur système (sans mot de passe, sans shell de login par défaut). Les commandes varient légèrement selon l'image de base :

  • Sur les bases Alpine : `RUN addgroup -S myappgroup && adduser -S myappuser -G myappgroup`
  • Sur les bases Debian/Ubuntu : `RUN groupadd --system myappgroup && useradd --system --gid myappgroup --no-create-home myappuser`
Choisissez des noms significatifs pour `myappgroup` et `myappuser`.

2. Définir le propriétaire des fichiers de l'application : Lorsque vous copiez les fichiers de votre application dans l'image avec `COPY` ou `ADD`, utilisez l'option `--chown` pour définir directement le propriétaire et le groupe corrects. Cela évite d'avoir à exécuter une commande `chown` supplémentaire en tant que `root`.

# Copie les fichiers et définit l'utilisateur/groupe créé précédemment
COPY --chown=myappuser:myappgroup . /app
Assurez-vous également que les répertoires où l'application pourrait avoir besoin d'écrire (ex: logs, uploads temporaires) appartiennent à cet utilisateur ou groupe, ou ont les permissions adéquates.

3. Basculer vers l'utilisateur non-root : Utilisez l'instruction `USER` pour spécifier que toutes les instructions suivantes (`RUN`, `CMD`, `ENTRYPOINT`) doivent s'exécuter en tant que l'utilisateur nouvellement créé.

USER myappuser
Cette instruction doit généralement être placée vers la fin du Dockerfile, juste avant l'instruction `CMD` ou `ENTRYPOINT` qui lance l'application.

Exemple combiné (base Alpine) :

FROM python:3.9-alpine

WORKDIR /app

# 1. Créer groupe et utilisateur
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

# S'assurer que le répertoire de travail existe et appartient au bon utilisateur
# (WORKDIR le crée mais en tant que root initialement)
RUN chown appuser:appgroup /app

# Copier requirements AVANT de changer d'utilisateur si pip doit être lancé en root
# Ou ajuster les permissions pour que l'utilisateur puisse écrire dans le site-packages
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 2. Copier le reste de l'application avec les bonnes permissions
COPY --chown=appuser:appgroup . .

# 3. Basculer vers l'utilisateur non-root
USER appuser

EXPOSE 5000
CMD [ "python", "./app.py" ]

Utiliser les utilisateurs existants et gérer les permissions

Il n'est pas toujours nécessaire de créer votre propre utilisateur. De nombreuses images officielles fournissent déjà un utilisateur non-root préconfiguré. Par exemple :

  • Les images `node` fournissent l'utilisateur `node`.
  • Les images `nginx` (officielles) s'exécutent par défaut en tant qu'utilisateur `nginx`.
  • Les images `postgres` s'exécutent en tant qu'utilisateur `postgres`.
Dans ces cas, vous pouvez simplement utiliser `USER node` (ou `USER postgres`, etc.) dans votre Dockerfile après avoir copié vos fichiers (en vous assurant qu'ils ont les bonnes permissions pour cet utilisateur prédéfini, souvent via `--chown`).

Un défi courant lors de l'exécution en non-root est la gestion des permissions. Votre application peut avoir besoin d'écrire dans certains répertoires (pour les logs, les fichiers temporaires, les données). Vous devez vous assurer que l'utilisateur non-root que vous avez défini a les permissions d'écriture nécessaires sur ces répertoires spécifiques. Cela peut être fait via des commandes `RUN chown ...` ou `RUN chmod ...` dans le Dockerfile (exécutées avant l'instruction `USER`), ou en définissant correctement les permissions sur les volumes montés depuis l'hôte.

De même, l'écoute sur des ports privilégiés (inférieurs à 1024, comme le port 80 pour HTTP ou 443 pour HTTPS) nécessite généralement des privilèges `root` sur les systèmes Linux. Si votre application doit écouter sur de tels ports, la solution recommandée n'est pas de l'exécuter en `root`, mais plutôt :

  • De configurer l'application pour écouter sur un port non privilégié à l'intérieur du conteneur (ex: 8080).
  • D'utiliser le mapping de ports de Docker pour mapper le port privilégié de l'hôte vers le port non privilégié du conteneur (ex: `docker run -p 80:8080 ...`).

En résumé, bien que cela demande un peu plus d'efforts lors de la création du Dockerfile, configurer vos conteneurs pour s'exécuter avec un utilisateur non privilégié est un investissement rentable en termes de sécurité et de robustesse de vos applications.