Un conteneur est une unité logicielle standard qui regroupe le code et toutes ses dépendances afin que l’application s’exécute rapidement et de manière fiable d’un environnement informatique à un autre. Docker est une technologies de conteneur qui a grandement facilité la distribution de logiciels et simplifié le partage de ressources sur un système.
Cependant, en raison de sa grande capacité d’intégration, centralisation et ses usages multiples, des erreurs de configuration ou des failles intrinsèques peuvent avoir un gros impact de sécurité sur les systèmes sur lesquels ils sont installés.
L’objectif de ce tutoriel est présenter des méthodes et règles qui permettent de créer et utiliser de manière sécurisée, un conteneur Docker.
1- Exécutez le conteneur en tant qu’utilisateur « non-root »
L’utilisateur par défaut dans un conteneur Docker est « root », c’est-à-dire le super utilisateur. Si aucun utilisateur spécifique n’est désigné au démarrage d’un conteneur, l’utilisateur défini dans l’image (dernière commande USER enregistrée dans le fichier Dockerfile) ou celui hérité du fichier image parent (défini par la commande FROM dans le fichier Dockerfile) qui sera exécuté par défaut. Par ailleurs, si l’utilisateur n’est pas spécifié lors du démarrage du conteneur ou si l’utilisateur spécifié n’est pas présent dans l’image, le conteneur s’exécutera en tant que root (uid 0).
La meilleure chose à faire est de créer un utilisateur non root pour exécuter le premier processus du conteneur et supprimer tous les utilisateurs non essentiels.
Pour connaître le nom ou l’ID de l’utilisateur, vous pouvez saisir la commande ci-dessous.
docker ps — quiet — all | xargs docker inspect — format ‘{{ .Id }}: User={{ .Config.User }}’
Utilisez la commande USER pour définir quel utilisateur doit s’exécuter au lancement de l’image et pour l’exécution des commandes RUN, CMD et ENTRYPOINT dans le fichier Dockerfile.
Pour déterminer si un utilisateur est configuré pour exécuter la commande, utilisez la commande DOCKER HISTORY, comme suit :
docker history <Image Name/ID>
2- Supprimez, les packages/logiciels inutiles de l’image
Les packages ou logiciels non indispensables augmentent la surface d’attaque du conteneur. En effet, plus on a de packages, plus le risque de vulnérabilités potentiellement exploitables est important. De ce fait, il est capital d’examiner les packages installés et supprimer ceux qui ne sont pas nécessaires pour le bon fonctionnement de l’image, au regard des services et fonctionnalités attendues.
La création d’images Docker peut être réalisée en utilisant des images de base minimalistes comme Alpine, BusyBox et autres. Pour lister tous les packages installés dans un conteneur, exécutez la commande suivante :
docker exec <CONTAINER_ID> apk info
NB : La commande ci-dessus est spécifique au gestionnaire de paquets apk. Cependant, en fonction du gestionnaire de packages utilisé dans votre image, vous devriez modifier la commande.
On peut également envisager de supprimer le programme d’installation du package avant d’utiliser l’image dans l’environnement de production.
3- Analysez et reconstruisez les images pour inclure les correctifs de sécurité
Que votre image Docker soit construite de zéro ou à partir d’une image tierce préconfigurée, il est très important d’analyser les images, afin d’identifier d’éventuelles vulnérabilités dans le cadre de votre processus d’intégration continue. Cela inclut l’analyse des packages, binaires, bibliothèques, fichiers, etc. inclus par rapport à une ou plusieurs bases de données de vulnérabilités bien connues.
Plusieurs outils open source puevnet vous aider à effecteur une annalyse de vulnérablités, tels que CoreOs-Clair, open-SCAP, Anchore-Engine, etc.
4- Activez Docker Content Trust (DCT)
Docker Content Trust (DCT) utilise des signatures numériques pour valider l’intégrité des images téléchargées ) partir des répertoires Docker distants. Avec DCT, les créateurs d’images Docker peuvent signer les images pour marquer qu’ils sont bien à l’origine d’une image en particulier. Les utilisateurs de l’image (ceux qui vont la télécharger) peuvent en vérifier l’intégrité à travers les signatures numériques pour s’assurer que les images n’ont pas été falsifiées.
Si DCT est activé, les instructions d’extraction, d’exécution et de construction ne fonctionneront qu’avec les images approuvées (signées).
echo $DOCKER_CONTENT_TRUST
Par défaut, le DCT est désactivé. Pour l’activer sur une image, vous pouvez exécuter la commande ci-dessous.
export DOCKER_CONTENT_TRUST=1
5- Utilisez COPY en lieu et place de ADD dans le fichier Dockerfile
Les commandes COPY et ADD ont des fonctionnalités très similaires et être utilisées pour copier des fichiers locaux dans le système de fichiers de l’image du conteneur. Cependant, la commande ADD a quelques fonctionnalités supplémentaires comme l’extraction de fichier tar local et la récupération d’URL à distance. De ce fait, son utilisation dans le Dockerfile présente le risque d’ajouter des fichiers malveillants à partir d’URL distantes sans avoir été analysés.
Ainsi, la meilleure utilisation de l’instruction ADD est de copier l’archive tar dans le système de fichiers image et de les extraire automatiquement. Dans la mesure du possible, COPY doit être utilisé à la place de l’instruction ADD.
On peut inspecter une image pour vérifier l’utilisation de la commande ADD dans la construction de l’image en utilisant la commande DOCKER HISTORY.
docker history <Image Name/ID>
6- Ne stockez aucune information confidentielle dans Dockerfile
Les mots de passe, clés API, clés privées et autres secrets ne doivent jamais être conservées dans le Dockerfile. Les secrets dans Dockerfile peuvent facilement être exposés par de simples commandes comme DOCKER HISTORY et une fois exposés, ils peuvent être exploités par un attaquant.
Dockerfile ne doit jamais être utilisé pour transmettre des secrets. Pour vous assurer qu’aucun secret ne soit présent dans Dockerfiles, un moyen simple consiste à utiliser la commande DOCKER HISTORY et à examiner attentivement son contenu.
docker history <Image Name/ID>
7- Installez des packages vérifiés et utilisez des images préconfigurée de confiance
Installez les packages uniquement à partir des référentiels approuvés. En plus de cela, utilisez des checksum et des signatures numériques pour vérifier l’authenticité et l’intégrité des packages téléchargés.
FROM alpine:3.11
Il est recommandé de référencer exactement la version d’image de base recherchée. De plus si l’on veut utiliser la même image à chaque fois, on peut utiliser des résumés avec l’instruction FROM. L’utilisation du résumé protégera également l’image contre la falsification et la corruption.
FROM alpine@sha256:ddba4d27a7ffc3f86dd6c2f92041af252a1f23a8 e742c90e6e1297bfa1bc0c45
Tout comme les images de base lors du téléchargement des packages logiciels, référencez la version exacte, vérifiez l’intégrité et l’authenticité des packages à l’aide de la checksum et des signatures numériques. Cela protégera les packages contre la corruption accidentelle et MITM si les packages sont téléchargés via un canal non sécurisé (HTTP).
8- Supprimer les autorisations setuid et setgid de l’image
setuid (Set User ID on execution) est un type spécial d’autorisation sous Unix qui permet aux utilisateurs d’exécuter certains programmes avec des privilèges élevés. Lorsque l’autorisation setuid d’un fichier exécutable est définie, les utilisateurs peuvent exécuter ce programme avec un niveau d’accès correspondant à l’utilisateur propriétaire du fichier.
setgid (Set Group ID on execution) est similaire à setuid, à la différence que lorsque l’exécutable avec l’autorisation setgid s’exécute, il s’exécute comme s’il était membre du même groupe dont le fichier est membre.
La suppression des autorisations setuid et setgid des fichiers binaires empêche leur utilisation abusive pour les attaques d’élévation de privilèges.
Pour rechercher tous les fichiers exécutables dans une image Docker avec les autorisations setuid et setgid, exécutez la commande ci-dessous :
docker run <Image Name/ID> find / -perm +6000 -type f -exec ls -ld {} \; 2> /dev/null
Le responsable de l’image doit examiner attentivement la liste de tous les fichiers renvoyés en sortie de la commande ci-dessus, puis doit supprimer les autorisations setuid et setgid de tous les exécutables où elles ne sont pas nécessaires.
Les fichiers binaires exécutables peuvent également être gérés lors de la construction de l’image, en incluant les éléments suivants dans le Dockerfile.
FROM alpine:3.11
RUN find / -perm +6000 -type f -exec chmod a-s {} \; || true
9- Utilisez attentivement les instructions de mise à jour dans Dockerfile
Si vous utilisez des instructions de mise à jour de package telles que apt-get update ou apk update (selon l’image de base et le gestionnaire de packages) dans votre fichier Docker, assurez-vous qu’elles ne sont jamais présentes isolément dans une seule ligne Dockerfile.
Si l’instruction de mise à jour du package est présente sur une seule ligne dans le Dockerfile, la même couche de mise à jour présent dans le cache sera utilisée. Cela empêchera toute nouvelle mise à jour de faire partie des versions ultérieures.
Pour atténuer ce problème, passez en revue votre Dockerfile et supprimez toute instruction de mise à jour présente seule. Si vous n’avez pas accès au Dockerfile, vous pouvez utiliser la commande DOCKER HISTORY pour vérifier qu’il n’y a pas d’instructions de mise à jour isolées.
docker history <Image Name/ID>
On peut également utiliser you–no-cacheflag pour éliminer toutes les couches mises en cache lors de la création d’images Docker.
10- Ajouter HEALTHCHECK à l’image Docker
L’instruction HEALTHCHECK peut être utilisée pour vérifier si un conteneur est toujours en cours d’exécution ou non. Si une image Docker a un healthcheck spécifié, les images auront également un healthcheck en plus de l’état normal. L’état de santé est initialement défini en starting. Chaque fois qu’un contrôle de santé réussit, il devient Healthy (quel que soit l’état dans lequel il se trouvait auparavant). Après un certain nombre d’échecs consécutifs, il devient unhealthy.
En fonction de l’état des conteneurs, Docker peut fournir de nouveaux conteneurs pour remplacer les conteneurs défectueux.
HEALTHCHECK --interval=15m --timeout=3s \
CMD curl https://localhost:8443/version -k || exit 1
L’inclusion de l’instruction HEALTCHCHECK ci-dessus dans le fichier Docker vérifiera toutes les quinze minutes si le serveur Web exécuté sur le port 8443 est capable de servir l’API de version dans les trois secondes.
Pour vous assurer que l’instruction HEALTHCHECK est présente dans l’image Docker, exécutez la commande suivante :
docker inspect --format='{{ .Config.Healthcheck }}' <Image Name/ID>