Blog

Se défendre contre l'évasion de conteneurs : un guide pratique de l'isolation Docker pour l'hébergement multi-locataire

Apprenez à sécuriser les conteneurs Docker contre les vulnérabilités d'évasion et les échecs d'isolation dans les environnements multi-locataires avec des étapes concrètes et des exemples.

Résumé

Les conteneurs Docker partagent le noyau de l'hôte, ce qui rend l'isolation critique, en particulier dans l'hébergement multi-locataire où une seule évasion de conteneur peut compromettre tous les locataires. De nombreux développeurs supposent que les conteneurs sont des machines virtuelles parfaitement isolées, mais la réalité est différente. Cet article explique les fonctionnalités du noyau Linux derrière l'isolation Docker (espaces de noms, cgroups) et les vecteurs d'attaque qui les menacent. Vous apprendrez des étapes pratiques pour renforcer votre configuration Docker : limiter les privilèges, utiliser un runtime sécurisé, scanner les images et implémenter une segmentation réseau. En suivant un exemple concret d'un fournisseur d'hébergement WordPress multi-locataire, vous verrez comment appliquer ces défenses. Nous abordons également les mises en garde telles que les compromis de performance et l'utilisation de seccomp/AppArmor. L'objectif est de vous donner une stratégie d'isolation robuste qui empêche les évasions de conteneurs et protège vos locataires.

Introduction

Si vous exploitez une plateforme d'hébergement multi-locataire – qu'il s'agisse d'hébergement WordPress partagé, d'une application SaaS ou d'un service d'environnement de développement – l'évasion de conteneur est le scénario cauchemardesque. Une vulnérabilité dans le noyau ou une mauvaise configuration peut permettre à un locataire de s'échapper de son conteneur et d'accéder aux données d'autres locataires ou à l'hôte lui-même. L'isolation de Docker repose sur des fonctionnalités du noyau Linux telles que les espaces de noms et les cgroups, mais les configurations par défaut sont souvent insuffisantes pour une sécurité robuste. Cet article vous guidera à travers les vecteurs d'attaque et fournira des étapes réalisables pour verrouiller vos conteneurs Docker, illustrées par un exemple concret de WordPress multi-locataire. Pour une vue d'ensemble de l'orchestration en production, consultez notre guide sur Orchestration d'applications conteneurisées prêtes pour la production.

Comprendre l'isolation Docker

Les conteneurs Docker utilisent les espaces de noms Linux pour fournir une isolation au niveau des processus : les espaces de noms PID isolent les arbres de processus, les espaces de noms réseau séparent les interfaces réseau, les espaces de noms de montage isolent les points de montage du système de fichiers, et les espaces de noms utilisateur permettent de mapper la racine du conteneur à un utilisateur hôte non privilégié. Les groupes de contrôle (cgroups) limitent l'utilisation des ressources telles que le CPU, la mémoire et les E/S disque. Ces fonctionnalités créent ensemble un « bac à sable » autour de chaque conteneur. Cependant, contrairement à une machine virtuelle qui exécute un noyau séparé, les conteneurs partagent le noyau de l'hôte. Cela signifie qu'une vulnérabilité dans le noyau (par exemple, CVE-2022-0492) peut être exploitée pour s'échapper de l'isolation de l'espace de noms du conteneur. De plus, des mauvaises configurations comme l'exécution de conteneurs en tant que root à l'intérieur du conteneur, l'octroi de toutes les capacités au conteneur, ou le non-rejet des capacités Linux inutiles peuvent élargir la surface d'attaque.

Vecteurs d'attaque

Les vecteurs d'attaque courants incluent :

  • Exploits du noyau : Exploiter un bug dans le noyau hôte pour obtenir un accès hôte.
  • Conteneurs privilégiés : L'exécution avec --privileged accorde toutes les capacités et contourne la plupart des isolements.
  • Abus de capacités : Même sans mode privilégié complet, un conteneur avec des capacités dangereuses comme CAP_SYS_ADMIN ou CAP_NET_ADMIN peut monter des systèmes de fichiers ou manipuler les paramètres réseau.
  • Pratiques d'images non sécurisées : Utiliser des images de base avec des vulnérabilités connues ou inclure des outils inutiles comme des compilateurs ou des interpréteurs shell.
  • Espaces de noms de montage partagés : Monter des répertoires hôtes dans les conteneurs peut permettre une évasion si ce n'est pas en lecture seule.

Étapes de sécurité pratiques

1. Exécuter les conteneurs en tant qu'utilisateur non root

Par défaut, Docker exécute les conteneurs en tant que root à l'intérieur du conteneur. Si un attaquant obtient les privilèges root dans le conteneur, il a plus de levier. Créez un utilisateur dans votre Dockerfile et utilisez la directive USER. Évitez également d'utiliser le drapeau --user dans Docker Compose pour mapper à un utilisateur hôte arbitraire si possible.

2. Rejeter toutes les capacités et n'ajouter que celles nécessaires

Les capacités Linux décomposent les privilèges de superutilisateur en unités plus petites. Dans Docker Compose, utilisez cap_drop: ALL puis cap_add uniquement celles requises (par exemple, NET_BIND_SERVICE). Évitez les capacités dangereuses comme SYS_ADMIN, NET_ADMIN, SYS_PTRACE.

3. Utiliser un système de fichiers racine en lecture seule

Définissez read_only: true dans la définition de votre conteneur. Cela empêche les attaquants d'écrire dans le système de fichiers du conteneur. Si votre application a besoin d'écrire des fichiers temporaires, montez un volume tmpfs à cet endroit.

4. Activer le remappage des espaces de noms utilisateur

Le remappage des espaces de noms utilisateur mappe l'utilisateur root du conteneur à un utilisateur hôte non root. Cela ajoute une couche d'isolation, car même si un root de conteneur s'échappe, il aura les privilèges de l'utilisateur remappé. Activez-le dans /etc/docker/daemon.json avec "userns-remap": "default". Sachez que cela peut compliquer les permissions des volumes. Pour plus de détails, consultez Maîtriser l'isolation Docker pour un hébergement Web sécurisé et efficace.

5. Appliquer les profils Seccomp et AppArmor/AppArmor

Seccomp restreint les appels système qu'un conteneur peut effectuer. Docker fournit un profil seccomp par défaut qui bloque les appels système dangereux. Vous pouvez également créer des profils personnalisés. De même, AppArmor (ou SELinux) fournit un contrôle d'accès obligatoire. Utilisez AppArmor pour confiner votre conteneur à un ensemble minimal d'opérations autorisées. Le profil de sécurité peut être défini via security_opt dans Docker Compose.

6. Utiliser des images de base minimales et scanner les vulnérabilités

Choisissez des images petites comme Alpine ou Distroless qui ont une surface d'attaque réduite. Scannez régulièrement les images avec des outils comme Docker Scout, Trivy ou Clair. Intégrez le scan dans votre pipeline CI/CD pour empêcher le déploiement d'images vulnérables.

7. Segmentation réseau avec des réseaux bridge personnalisés

Créez des réseaux bridge séparés pour chaque locataire ou niveau d'application. Cela limite le trafic est-ouest. Dans Docker Compose, définissez des réseaux et isolez les services. Utilisez internal: true si un service n'a pas besoin d'un accès Internet sortant. Les règles de pare-feu sur l'hôte restreignent davantage le trafic inter-conteneurs.

8. Limiter les ressources avec Cgroups

Définissez des limites de CPU et de mémoire dans Docker Compose en utilisant deploy.resources.limits. Cela empêche un conteneur compromis de lancer une attaque par épuisement des ressources. De plus, définissez kernel_memory et memory_reservation pour un contrôle plus fin.

Exemple concret : Hébergement WordPress multi-locataire avec Docker Compose

Considérez un scénario où vous hébergez plusieurs sites WordPress pour différents clients, chacun dans son propre conteneur Docker. Une configuration non sécurisée pourrait ressembler à ceci :

version: '3'
services:
  wordpress:
    image: wordpress:latest
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: exampleuser
      WORDPRESS_DB_PASSWORD: examplepass
      WORDPRESS_DB_NAME: exampledb
    volumes:
      - ./wp-content:/var/www/html/wp-content
  db:
    image: mysql:5.7
    environment:
      MYSQL_DATABASE: exampledb
      MYSQL_USER: exampleuser
      MYSQL_PASSWORD: examplepass
      MYSQL_ROOT_PASSWORD: somewordpress
    volumes:
      - db_data:/var/lib/mysql
volumes:
  db_data:

Cette configuration est vulnérable : le conteneur WordPress s'exécute en tant que root à l'intérieur, possède toutes les capacités (puisqu'aucune n'est rejetée), monte un répertoire hôte avec un accès en écriture et a un accès réseau illimité.

Durcissons-la maintenant :

version: '3'
services:
  wordpress:
    image: wordpress:latest
    user: www-data
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    read_only: true
    tmpfs:
      - /var/www/html/wp-content/plugins
    security_opt:
      - seccomp=seccomp-profile.json
      - apparmor=wordpress-profile
    networks:
      - frontend
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: exampleuser
      WORDPRESS_DB_PASSWORD: examplepass
      WORDPRESS_DB_NAME: exampledb
    volumes:
      - wp-uploads:/var/www/html/wp-content/uploads
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 256M
  db:
    image: mysql:5.7
    user: mysql
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    networks:
      - backend
    environment:
      MYSQL_DATABASE: exampledb
      MYSQL_USER: exampleuser
      MYSQL_PASSWORD: examplepass
      MYSQL_ROOT_PASSWORD: somewordpress
    volumes:
      - db_data:/var/lib/mysql
    deploy:
      resources:
        limits:
          cpus: '0.25'
          memory: 128M
networks:
  frontend:
    driver: bridge
    internal: false
  backend:
    driver: bridge
    internal: true
volumes:
  wp-uploads:
  db_data:

Améliorations clés :

  • Les deux conteneurs s'exécutent en tant qu'utilisateurs non root (www-data et mysql).
  • Toutes les capacités sont rejetées, seule NET_BIND_SERVICE est ajoutée.
  • Le système de fichiers WordPress est en lecture seule, à l'exception d'un montage tmpfs et d'un volume de téléchargements.
  • Les profils Seccomp et AppArmor sont appliqués (vous devrez fournir des profils personnalisés).
  • Des réseaux séparés isolent le web de la base de données, le réseau de la base de données étant interne.
  • Les limites de ressources empêchent l'épuisement des ressources.

Pour en savoir plus sur le durcissement Docker spécifique à WordPress, consultez Docker pour WordPress : pourquoi les conteneurs isolés changent tout.

Mises en garde

  • Remappage des espaces de noms utilisateur : Bien que puissant, il casse le montage des volumes car l'UID hôte remappé n'est pas le même que l'UID du conteneur. Vous devrez peut-être pré-créer des répertoires avec les bonnes permissions ou utiliser des volumes Docker avec prise en charge du remappage.
  • Profils Seccomp/AppArmor : Les profils personnalisés nécessitent de comprendre les appels système et les modèles d'accès aux fichiers de votre application. Des profils trop restrictifs peuvent casser la fonctionnalité. Testez minutieusement.
  • Performance : Les couches de sécurité supplémentaires comme seccomp et AppArmor ont une surcharge minimale, mais les limites de ressources et les systèmes de fichiers en lecture seule peuvent affecter les applications gourmandes en écriture.
  • Complexité de l'orchestration : Dans un environnement multi-locataire, la gestion des fichiers Docker Compose par locataire peut devenir fastidieuse. Envisagez d'utiliser un outil d'orchestration de plus haut niveau comme Kubernetes, mais cela introduit ses propres considérations de sécurité.

Conclusion

L'évasion de conteneur est une menace réelle dans l'hébergement Docker multi-locataire, mais elle est évitable. En comprenant les mécanismes d'isolation et en appliquant une défense en profondeur – rejet des capacités, exécution en tant que non-root, activation des espaces de noms utilisateur, seccomp, AppArmor, segmentation réseau et scan régulier des images – vous pouvez réduire considérablement le risque. N'oubliez pas que les paramètres par défaut de Docker ne sont pas prêts pour la production pour les charges de travail multi-locataires. Mettez en œuvre ces étapes dès aujourd'hui pour protéger vos locataires et votre infrastructure. Pour un aperçu complet des meilleures pratiques de sécurité Docker, consultez Sécuriser vos applications Web avec Docker : un guide pratique de l'isolation et des meilleures pratiques.

Sources (5)