Contactez-nous

Sécurité à l'exécution (runtime security)

Découvrez comment renforcer la sécurité des conteneurs Docker pendant leur exécution : non-root, capabilities, seccomp, AppArmor, limites de ressources.

Introduction : au-delà de l'image statique

Avoir une image Docker sécurisée, sans vulnérabilités connues et construite selon les bonnes pratiques, est une première étape fondamentale. Cependant, la sécurité ne s'arrête pas là. Une fois qu'un conteneur est lancé à partir de cette image (`docker run`), il devient une entité dynamique qui interagit avec le noyau de l'hôte, le réseau, le système de fichiers et potentiellement d'autres conteneurs. C'est pendant cette phase d'exécution (runtime) que de nouveaux risques peuvent émerger.

Une vulnérabilité dans l'application elle-même, une mauvaise configuration lors du lancement, ou des permissions excessives accordées au conteneur peuvent être exploitées par un attaquant pour compromettre le conteneur, voire tenter de s'échapper vers l'hôte ou d'autres parties de l'infrastructure. La sécurité à l'exécution concerne donc l'ensemble des mesures et configurations appliquées pour protéger les conteneurs pendant qu'ils sont actifs.

Cette section explore les mécanismes et les bonnes pratiques essentiels pour renforcer la sécurité de vos conteneurs en cours d'exécution. Nous aborderons le principe de moindre privilège appliqué au runtime, l'utilisation des fonctionnalités de sécurité Linux intégrées à Docker, la limitation des ressources, et la surveillance continue pour détecter les comportements anormaux.

Le moindre privilège en action : non-root et capacités Linux

L'un des principes de sécurité les plus fondamentaux est celui du moindre privilège : n'accordez que les permissions strictement nécessaires à l'accomplissement d'une tâche. Appliqué aux conteneurs Docker, cela commence par éviter d'exécuter les processus applicatifs en tant qu'utilisateur `root` à l'intérieur du conteneur. Bien que nous ayons vu comment utiliser l'instruction `USER` dans le Dockerfile, il est crucial de comprendre pourquoi c'est important au runtime : même si le conteneur est isolé, une compromission d'un processus root à l'intérieur du conteneur donne plus de leviers à un attaquant, notamment pour interagir avec des fichiers système ou tenter des opérations nécessitant des privilèges élevés qui pourraient mener à une évasion.

Au-delà de l'utilisateur, Docker utilise les capacités Linux (Linux Capabilities) pour accorder de manière granulaire certains privilèges traditionnellement réservés à `root`, sans donner tous les pouvoirs de `root`. Par défaut, Docker démarre les conteneurs avec un ensemble limité de capacités, en supprimant celles jugées dangereuses (comme `SYS_ADMIN`). Cependant, même cet ensemble par défaut peut être excessif pour de nombreuses applications.

Il est fortement recommandé de réduire encore davantage les capacités accordées. Utilisez l'option `docker run --cap-drop=ALL` pour supprimer toutes les capacités par défaut, puis ajoutez uniquement celles qui sont absolument indispensables au fonctionnement de votre application avec `--cap-add=...`. Par exemple, un serveur web qui a besoin de se lier à un port privilégié (comme 80 ou 443) pourrait nécessiter uniquement la capacité `NET_BIND_SERVICE`. L'analyse des besoins réels de l'application est essentielle pour appliquer ce principe efficacement.

# Exécute un conteneur Nginx en supprimant toutes les capacités sauf NET_BIND_SERVICE
docker run -d --name secure-nginx \
  --cap-drop=ALL \
  --cap-add=NET_BIND_SERVICE \
  nginx:latest

Exploiter les options de sécurité (`--security-opt`) pour le confinement

Docker fournit l'option `--security-opt` lors de la commande `docker run` pour permettre un contrôle plus fin sur les mécanismes de sécurité appliqués au conteneur. Ces options interagissent directement avec les fonctionnalités de sécurité du noyau Linux.

Seccomp (Secure Computing Mode) : Docker applique par défaut un profil seccomp qui filtre et restreint les appels système autorisés depuis le conteneur, bloquant de nombreux appels potentiellement dangereux. C'est une mesure de sécurité très efficace. Vous pouvez désactiver ce profil (`--security-opt seccomp=unconfined`), mais c'est fortement déconseillé. Il est également possible de fournir votre propre profil seccomp JSON personnalisé (`--security-opt seccomp=/path/to/profile.json`) pour restreindre encore davantage les appels système autorisés, en fonction des besoins très spécifiques de votre application.AppArmor / SELinux (Mandatory Access Control - MAC) : Si AppArmor ou SELinux sont activés et configurés sur votre système hôte, Docker peut les utiliser pour appliquer des politiques de contrôle d'accès obligatoire aux conteneurs. Ces systèmes définissent des règles fines sur les fichiers, les capacités réseau et autres ressources auxquels un processus (conteneur) peut accéder. Vous pouvez spécifier un profil AppArmor spécifique avec `--security-opt apparmor=your_profile_name` ou des labels SELinux avec `--security-opt label=...` pour un confinement encore plus strict.`no-new-privileges` : L'option `--security-opt no-new-privileges` empêche les processus à l'intérieur du conteneur d'acquérir des privilèges supplémentaires via des mécanismes comme les exécutables `setuid` ou `setgid`. Il est fortement recommandé d'activer cette option (`--security-opt no-new-privileges=true`) pour empêcher certaines formes d'escalade de privilèges au sein du conteneur.

Durcir le système de fichiers et maîtriser les ressources

Le système de fichiers du conteneur peut également être une cible. Une bonne pratique consiste à rendre le système de fichiers racine du conteneur lecture seule (read-only) en utilisant l'option `docker run --read-only`. Cela empêche toute modification du système de fichiers du conteneur après son démarrage, rendant plus difficile pour un attaquant d'installer des outils malveillants, de modifier des configurations ou de persister après une compromission. Si votre application a besoin d'écrire dans des répertoires spécifiques (comme `/tmp` ou des répertoires de logs/cache), vous pouvez les monter explicitement en tant que volumes `tmpfs` (en mémoire) ou volumes nommés.

# Lance un conteneur avec un FS racine en lecture seule, mais /tmp en écriture
docker run -d --read-only --tmpfs /tmp my-app:latest

Une autre dimension de la sécurité à l'exécution est la limitation des ressources. Un conteneur compromis ou simplement défectueux pourrait tenter de consommer toutes les ressources CPU ou mémoire de l'hôte, provoquant un déni de service (DoS) pour l'hôte et les autres conteneurs. Utilisez systématiquement les options de `docker run` pour définir des limites strictes :

  • --memory=limit : Limite la quantité de mémoire RAM utilisable.
  • --memory-swap=limit : Limite la mémoire totale (RAM + swap).
  • --cpus=limit : Limite la fraction de CPU utilisable (ex: `1.5` pour un coeur et demi).
  • --pids-limit=limit : Limite le nombre de processus pouvant être créés (utile contre les attaques de type "fork bomb").

Définir des limites de ressources appropriées est crucial pour la stabilité et la sécurité de l'hôte, en empêchant qu'un seul conteneur ne puisse monopoliser les ressources système.

Surveillance continue et l'écueil du mode privilégié

La sécurité à l'exécution n'est pas seulement une question de configuration initiale, mais aussi de surveillance continue. Des outils spécialisés dans la détection des menaces au runtime (Runtime Threat Detection) peuvent surveiller l'activité des conteneurs en temps réel. Ils analysent les appels système, les accès réseau et les exécutions de processus pour détecter des comportements suspects ou malveillants (comme l'exécution de commandes inattendues, des tentatives d'accès à des fichiers sensibles, des connexions réseau anormales) et peuvent générer des alertes ou même bloquer l'activité. Des outils comme Falco ou des solutions commerciales s'inscrivent dans cette catégorie.

Enfin, il est absolument essentiel d'éviter d'utiliser l'option `--privileged` lors du lancement de conteneurs, sauf cas d'extrême nécessité et parfaitement compris (comme certains scénarios Docker-in-Docker ou l'accès direct à du matériel spécifique). Lancer un conteneur en mode privilégié désactive la plupart des mécanismes d'isolation et de sécurité (capacités, seccomp, AppArmor/SELinux pour certaines opérations). Un conteneur privilégié a un accès quasi total aux périphériques de l'hôte et une évasion est beaucoup plus facile, compromettant gravement la sécurité de l'hôte.

La combinaison de l'exécution en tant qu'utilisateur non-root, la limitation drastique des capacités, l'utilisation des `--security-opt`, le durcissement du système de fichiers, la limitation des ressources et l'évitement du mode privilégié forment une défense en profondeur robuste pour la sécurité de vos conteneurs pendant leur exécution. Ces mesures, combinées à une image sécurisée et un démon bien configuré, contribuent à créer un environnement conteneurisé résilient.