[#if coverImage??]
    [#if coverImage?is_hash]
        [#if coverImage.alt??]
            ${coverImage.alt}
        [/#if]
    [/#if]
[/#if]

La conteneurisation est aujourd'hui un paradigme essentiel des infrastructures modernes. D'aucuns mettront en avant des approches très abouties, notamment grâce à Kubernetes, mais ces approches exigent un engagement total de l'IT dans ce sens. Dans un cadre moins jusqu'au-boutiste, la mise en oeuvre de conteneurs Docker sur une infrastructure plus modeste est une solution parfois très pertinente. Dans ce cas, se pose, vous l'aurez deviné, la question de la sécurité.

 

Docker, en tant que socle d’architecture sur une machine, présente 6 catégories principales de risques de sécurité:

  1. Propagation des vulnérabilités des images originelles des conteneurs. Les images originelles sont la base d’un conteneur. Si une vulnérabilité est incluse dans une image originelle, toutes les images qui en héritent et tous les conteneurs basés sur ces images présenteront la même vulnérabilité. Qu’il s’agisse d’une image créée en interne ou, pire, d’une image open-source tirée d’un dépôt public.

  2. Visibilité/lisibilité de crédentiels. Le principe d’une image est la réutilisabilité. Pour assurer cette mission et proposer un environnement sécurisé, toute information sensible doit être injectée dynamiquement. Pas question de stocker un mot de passe, une clé d’API ou un jeton d’accès, afin d’éviter de fournir un moyen d’accès à un utilisateur non autorisé. De plus, si votre image est stockée sur un dépôt public, les informations sensibles deviennent accessibles au public.

  3. Large surface d’attaque. Docker nécessite les privilèges d’administration. Donc un accès à la machine hôte et au démon lance une escalade de privilèges totale sur les conteneurs et les images. Un attaquant peut alors, avec des droits d’administration, créer, arrêter des conteneurs, supprimer ou récupérer des images, injecter des commandes dans un conteneur pendant son exécution, exposer des informations sensibles.

  4. Manque de granularité dans le contrôle d’accès par rôles (RBAC). Le mécanisme RBAC de Docker utilise des accès pour différents rôles, tels que les utilisateurs, les équipes, les organisations et les comptes de service, ce qui reste un modèle très simple. Dans une organisation DevOps, les développeurs, les testeurs et les équipes techniques ont tous besoin d’accéder aux différents conteneurs, à des moments différents du projet. Certains utilisateurs doivent avoir un accès restreint, d’autres doivent pouvoir modifier et gérer les conteneurs. Ce type d’accès variable est compliqué à mettre en place.

  5. Manque de visibilité. Les conteneurs sont des objets jetables, souvent remplacés, éventuellement mis à jour très régulièrement au gré des nouvelles versions de la plate-forme. Chaque conteneur contient un hôte, un registre, un client et plusieurs autres composants. La complexité conteneurs Docker et leur volatilité rendent leur suivi et leur sécurisation délicats.

  6. Attaque réseau horizontale. Dans une attaque horizontale, les attaquants progressent à travers un réseau en changeant de noms d’utilisateur et en pénétrant de nouveaux systèmes jusqu’à atteindre leur cible. Comme un hôte Docker peut infecter n’importe quel hôte connecté au même réseau, la protection de périmètre assurée par les pare-feux traditionnels n’est d’aucune utilité pour les conteneurs Docker.

 

Bonnes pratiques pour la sécurité des conteneurs Docker

Dans ces conditions, nous organisons la sécurité selon les principes suivants.

 

Sécurité de l’exécution de Docker

Nous nous assurons de la composition de nos conteneurs à l’exécution et au build. La meilleure façon de modifier un conteneur est de modifier son image et de redéployer une nouvelle instance de conteneur. Nous mettons également en place une politique de sécurité qui émet une alerte et confine le problème lorsqu’un comportement suspicieux est détecté.

 

Authenticité de l’image Docker et confiance en la source

Les images de conteneur sont la base d’une infrastructure Docker. Nous avons constitué notre propre registre d’images validées, en vérifiant les conteneurs open-source ou tiers. Cette stratégie associée à une construction simple et une mise à jour régulière des mesures de sécurité.

 

Utilisation des Docker secrets pour la gestion des informations sensibles

Les secrets peuvent contenir des informations sensibles, comme des mots de passe ou des adresses. Pour assurer leur sécurité, nous diffusons aux conteneurs les secrets à l’exécution, à travers l’orchestrateur Docker Compose. Dans Docker, les secrets sont cryptés lors du transfert, mais aussi à l’utilisation. Ils ne seront lisibles que par les services ayant la permission.

 

Limitation des ressources

La possibilité d’exécuter autant de conteneurs que souhaité apporte une grande flexibilité en production.  Mais également un fort risque de compromission. L’activité des conteneurs est sous surveillance et la consommation de ressources est limitée. En cas d’erreur de conception, de bug ou d’attaque malveillante, le DoS est limité.

 

Utilisation d’un profil SECCOMP pour limiter les appels système

SECCOMP est un mécanisme open-source du noyau Linux qui permet une protection à l’exécution par le contrôle d’accès obligatoire (MAC). SECCOMP propose la désactivation de 44 appels système (par défaut) sur les plus de 300 possibles sur un conteneur Docker. Il est possible d’allonger la liste pour bloquer d’autres appels. Nous utilisons un profil SECCOMP pour éviter certains types d’attaques et limiter l’expansion d’autres attaques au reste de l’infrastructure.

 

Gestion granulaire des accès

Il existe des solutions pour la gestion des accès dans Docker, de manière à réduire les risques de sécurité. Nous utilisons un annuaire pour autoriser l’exécution des conteneurs avec des droits minimaux, tout en gérant les rôles au niveau des équipes, à chaque phase de développement.

 

Gestion du cycle de vie complet

Nous surveillons, gérons et analysons en continu l’infrastructure des conteneurs. En vérifiant les vulnérabilités lors du cycle de livraison, nous évitons la livraison de conteneurs contaminés. La gestion continue du cycle de vie garantit la sécurité des conteneurs tout au long du projet, du développement au déploiement.

 

Surveillance de l’activité des conteneurs

Nous utilisons des outils similaires à Scout, DataDog ou Prometheus pour la surveillance des conteneurs. La surveillance des systèmes est essentielle pour identifier les attaques, envoyer des alertes et parfois mettre en place des correctifs. Notre processus de surveillance inclut une relecture régulière des logs générés par les conteneurs pour assurer une sécurité préventive.

 

Check-list de configuration de l’hôte

  • créer une partition séparée pour les conteneurs

  • durcir l’hôte

  • maintenir l’exécutable Docker à jour

  • définir les autorisations d’accès du démon Docker a minima

  • configurer proprement les répertoires des fichiers Docker

  • surveiller et auditer l’activité du démon Docker

 

Check-list de configuration du démon Docker

  • restreindre le trafic réseau entre les bridges standard des conteneurs et restreindre l’accès aux nouveaux privilèges depuis les conteneurs

  • activer le support des user namespaces pour les autorisations des commandes du client Docker, la restauration à chaud et utiliser les cgroups.

  • désactiver les opérations “legacy” sur le registre et désactiver le Userland Proxy

  • éviter les mauvaises configurations réseau en permettant à Docker de changer les IPTables et éviter l’utilisation de fonctionnalités expérimentales en production

  • configurer l’authentification TLS pour le démon Docker et configurer la journalisation externe centralisée

  • régler le niveau de journalisation à “INFO” et régler une valeur appropriée de ulimit

  • ne pas utiliser des registres non sécurisés ou des drivers de stockage aufs

  • appliquer une taille de base par défaut pour les conteneurs et appliquer un profil SECCOMP adapté du démon pour éviter les appels système

 

Check-list de construction des images

  • créer un utilisateur pour le conteneur

  • vérifier que les conteneurs n’utilisent que des images approuvées

  • vérifier qu’aucun paquet superflu n’est installé dans le conteneur

  • inclure les patches de sécurité lors des vérifications et des actions de reconstruction/redéploiement

  • activer le Docker Content Trust lors des appels aux registres externes

  • ajouter les directives HEALTHCHECK sur les images de conteneurs

  • supprimer les permissions setuid et setgid des images

  • utiliser COPY au lieu de ADD dans le Dockerfile

  • n’installer que des paquets vérifiés

  • ne pas utiliser d’instructions d’update isolées sur une ligne dans le Dockerfile

  • ne pas stocker de secrets dans les Dockerfiles

 

Check-list à l’exécution des conteneurs

  • empêcher les conteneurs d’acquérir des privilèges supplémentaires et restreindre les capacités du noyau Linux

  • activer le profil AppArmor

  • éviter d’utiliser des conteneurs privilégiés à l’exécution, d’utiliser ssh depuis les conteneurs, de mapper des ports privilégiés avec des conteneurs

  • vérifier qu’aucun répertoire sensible du système hôte n’est monté dans un conteneur, que le système de fichiers racine de chaque conteneur est monté en lecture seule, que le socket Docker n’est pas monté depuis l’intérieur d’un conteneur

  • régler une priorité appropriée sur le CPU pour chaque conteneur, régler le redémarrage sur erreur ‘on-error’ sur la stratégie ‘5’, n’ouvrir que les ports nécessaires sur le conteneur

  • appliquer au besoin les options de sécurité de SELinux, et surcharger la valeur par défaut de ulimit à l’exécution

  • ne pas partager le namespace du réseau de l’hôte, ni celui des processus de l’hôte ou des IPC, ne pas partager le mode de propagation de montage, ni le namespace UTS ou user de l’hôte

  • limiter l’utilisation mémoire par conteneur et attacher le trafic entrant dans un conteneur à une interface spécifique de l’hôte

  • ne pas exposer les périphériques de l’hôte directement aux conteneurs, ne pas désactiver le profil SECCOMP par défaut, ne pas utiliser des commandes ‘docker exec’ avec un utilisateur privilégié ou nommé, ne pas utiliser le bridge par défaut de Docker ‘docker0’

  • confirmer l’utilisation de cgroup et utiliser la limite de PIDs par cgroup, vérifier la santé des conteneurs à l’exécution et toujours mettre à jour les commandes Docker avec la dernière version de l’image

 

En conclusion, loin d'être secondaire, la sécurité des conteneurs Docker est à prendre au sérieux. Non seulement les gains frauduleux de privilèges peuvent être énormes, mais surtout il est facile de mettre les risques de côté quand ils ne sont pas vraiment connus. L'environnement Docker, quoique facilitateur dans la réutilisabilité, la modularité et la mise en oeuvre de micro-services, est très exigeant en termes de sécurité, tant les contraintes qui lui sont associées (notamment l'exécution sous utilisateur privilégié) sont grandes.

 

Note: cet article est très largement inspiré d'un article de Roni Asnat "Top Docker Security Best Practices".