Docker Sécurité : 10 bonnes pratiques

Docker permet de faire beaucoup de choses, mais si on ne fait pas attention, on peux rapidement se retrouver avec des problèmes de sécurité. Voici donc 10 bonnes pratiques sélectionnés pour sécuriser rapidement vos Docker.

Je tiens à préciser que je ne suis pas un expert en sécurité des systèmes Linux, je suis avant tout un développeur qui tient à assurer un minimum de sécurité à ce qu’il installe sur ses serveurs. Il ne sera donc pas question ici de recompiler le noyau Linux en utilisant telle librairie et pas une autre. Je n’ai donc retenu que les solutions compréhensibles pour un développeur et facile à mettre en place.

Créer une partition séparée pour Docker

L’ensemble des données de docker sont stocké dans /var/lib/docker. C’est le répertoire par défaut que docker utilise pour stocker toutes ses images et ses containers. Dès les premiers jours d’utilisation, on se rend vite compte que  Docker prend rapidement de la place.

L’inconvénient de ce répertoire par défaut, c’est qu’il est situé sous le répertoire racine / et que si Docker remplit ce répertoire, votre répertoire racine sera également plein, ce qui va rendre votre système hôte inutilisable. Une image mal intentionnée pourrait volontairement se mettre à occuper tout l’espace disponible pour votre système hôte. Même sans un esprit malveillant, Docker peut en tout état de cause occuper tout l’espace disponible après seulement quelques pull d’images.

Solution

Il faut créer une partition physique séparée pour le répertoire /var/lib/docker dès l’installation de votre système hôte.

Si votre système est déjà installé, créez une partition logique avec LVM (Logical Volume Manager).

Ces 2 solutions permette de définir un quota à ne pas dépasser et ne mettre pas en péril votre partition racine.

Pour créer une partition logique, vous pouvez suivre cette article sur LVM (en français)

Maintenez votre système hôte à jour

Ça peux paraître évident, mais que ce soit votre système linux, le kernel ou Docker Engine, assurez-vous que votre système soit bien à jour.

Mettez à jour votre noyau linux et utilisez la version recommandée, évitez les versions instables.

CIS_Docker_1.6_Benchmark (voir plus bas) recommande au minimum la version 3.10 du kernel Unix qui corrige de nombreuses failles concernant Docker.

Assurez vous de mettre régulièrement à jour Docker dès qu’une version stable est sortie. Elles peuvent corriger des lacunes des versions antérieures.

Interdire les communications entre les containers

Par défaut, la communication entre tous les containers est possible sans forcement utiliser la fonction de link. Une mauvaise image pourrait donc faire du sniffing et voir tout ce qui se passe sur le sous-réseau Docker de votre système hôte.

C’est particulièrement dangereux car la plupart du temps, il n’y a pas de connexion sécurisé entre vos containers que vous considérez comme “isolé” sur le sous-réseau docker0

Solution

La bonne pratique est d’interdire ce comportement par défaut. Seul les containers liés entre eux par la fonction link de docker seront capable de communiquer entre eux.

Cette solution est disponible nativement avec Docker. Il suffit de passer le paramètre -icc=false au deamon. Sous debian et ubuntu, cette opération se fait dans le fichier /etc/default/docker en modifiant la variable DOCKER_OPTS

DOCKER_OPTS="-icc=false"

N’utilisez pas privileged pour n’importe quel image

Quand vous lancez un container avec le mot clé –privileged, Docker va lui donner tous les droits, y compris celui de lancer de nouveaux container sur la machine hôte (Docker in Docker)

C’est par exemple le cas de l’image dockerui qui a besoin de lister les images et les containers pour éventuellement les démarrer et les arrêter. Cette image étant fournit par un développeur de Docker, nous pouvons à priori lui faire confiance, mais ce n’est pas le cas de toutes les images.

Si une image demande à être privileged, demandez-vous pourquoi elle a besoin de ce droit. Si l’utilisation de privileged est justifiée, il n’est pas nécessaire pour autant d’accorder la totalité des droits, c’est ce que permet les options –cap-add et –cap-drop. Selon votre besoin, ces commandes permettre à un container privileged de lui retirer un ou plusieurs droits, ou à l’inverse à un container normal de lui rajouter uniquement le droit nécessaire.

Exemple : ntpd (Network Time Protocol Deamon) est un serveur de temps, il ne parait donc pas absurde de lui donner la capacité de modifier l’heure du système hôte.

docker run -d --cap-add SYS_TIME ntpd

N’utilisez pas n’importe quel registry

La plupart des images sont téléchargeables depuis le registry Docker Hub. D’une manière générale, préférez toujours les images “official” proposées et validées par la société Docker. Si ce n’est pas le cas, assurez que l’image a suffisamment été téléchargée et à priori “testés” par les autres utilisateurs. N’hésitez pas non plus à aller vérifier le Dockerfile sur github afin de s’assurer que l’image correspond à ce qu’elle est censé être.

Il est important également de vérifier les images intermédiaires utilisées et remonter les différentes images jusqu’à une image official ou une image from scratch.

Il arrive parfois que vous ne trouviez pas l’image sur Docker Hub, vérifier donc la réputation du registry que vous allez utiliser, mais également que la communication établie avec ce nouveau registry se fera de manière sécurisé. Par défaut, Docker bloque la commande pull si ce n’est pas le cas.

Créer un utilisateur dans votre Dockerfile

La plupart de vos applications ne nécessite pas d’être lancé en root, c’est même très rarement le cas. Par conséquent, il n’est pas nécessaire d’utiliser le user root dans vos containers.  La création d’un utilisateur dans votre image peut se faire directement depuis le Dockerfile avec les 2 instructions suivantes:

RUN useradd -d /home/myappuser -m -s /bin/bash myappuser
USER myappuser

Pour vérifier l’utilisateur utilisé par vos containers lancés, vous pouvez utiliser la commande

docker ps -q | xargs docker inspect --format '{{ .Id }}: User={{.Config.User}}'

Si User est vide, c’est que vos containers sont lancés en root

Pour une image téléchargée, pensez à vérifier la présence de l’instruction USER dans le Dockerfile et éventuellement à surcharger l’image si ce n’est pas le cas.

FROM apacheOnlyRoot
USER www-data

Ne lancer pas vos containers en root

Cela rejoint le point précédent, mais si votre image ne nécessite l’utilisateur root, il est préférable de ne pas lancer le container avec l’utilisateur root

Voici la commande pour le faire

docker run -u <Username or ID> <Run args> <Container Image Name or ID> <Command>

et voici un exemple avec une image apache

docker run -u www-data -d -t apache

Cette option est également disponible dans docker-compose

user: www-data

Ne mapper que les ports utiles

Docker propose un mapping entre les ports déclarés par le Dockerfile et ceux ouvert sur votre système hôte. L’option -P (en majuscule)  permet lancer un container en exposant tous les ports déclarés dans votre Dockerfile.

N’utiliser jamais l’option -P (en majuscule), préférez l’option -p (en minuscule) qui permet mapper les ports un par un.

docker run -p 80:80 apache

De plus, si vous n’avez pas besoin de rendre public l’adresse IP, vous pouvez spécifier sur quel interface réseau vous souhaitez écouter

docker run -p 192.168.0.1:9200:9200 elasticsearch

Cette commande permet de n’ouvrir le port 9200 que pour votre réseau local. Une connexion depuis une machine hors réseau local (internet) sera refusée.

Permission sur les fichiers

De la même manière qu’on doit vérifier les permissions du répertoire /var/www pour un serveur apache, il n’y a pas de raison de ne pas vérifier les permissions pour un container faisant tourner un serveur apache.

Les bonnes pratiques de sécurité Linux en terme de permissions doivent s’appliquer à toutes vos images et volumes.

Docker est certes très facile à prendre en main, mais il faut pour autant comprendre les notions de base d’un système unix pour assurer à minima la sécurité d’un container tournant sous Linux grâce aux permissions et aux groupes utilisateurs.

Veille Techno

Enfin, la dernière bonne pratique consiste à se tenir informé des très nombreuses évolutions de Docker. La dernière version 1.10 de Docker sortit il y seulement quelques jours permet enfin de manipuler les User Namespace. On peut faire croire aux processus des containers qu’il appartienne à un utilisateur root alors que ce n’est pas le réellement le cas.

Docker Engine 1.10 Security Improvements (en anglais)

Docker promet donc encore de nombreuses améliorations en termes de sécurité. La configuration par défaut de docker n’est malheureusement pas suffisante en terme de sécurité, mais représente surement plus de 90% des installations docker (moi y compris). A mon sens, beaucoup d’options devrait être activées par défaut pour éviter ce genre de risque.

Afin de finaliser mon article, j’ai lu ce pdf qui présente un rapport d’audit à effectuer pour une installation Docker. Très complet, je m’en suis servi pour atteindre le nombre de 10 bonnes pratiques.

https://benchmarks.cisecurity.org/tools2/docker/CIS_Docker_1.6_Benchmark_v1.0.0.pdf