logo
Menu

Checklist pour la sécurisation de votre installation et de vos containers Docker

22 décembre 2019 - Docker, Programmation, Sécurité
45 vues
Checklist pour la sécurisation de votre installation et de vos containers Docker

CE GUIDE NE CONCERNE QUE DOCKER SUR LINUX.

Docker est une technologie de containérisation de logiciels et services formidable qui vous permet d’accélérer drastiquement l’installation de vos applications et d’en assurer une intégration continue simple.

En plus de faire bénéficier de ces avantages, Docker permet d’isoler vos applications de sorte à ce qu’elles se lancent sans risque d’endommager l’hôte qui les fait tourner et tout en ayant la possibilité d’auditer les fichiers et les ports dont votre application a accès.

Néanmoins, pour profiter de ces avantages de sécurité, il est nécessaire de faire attention à plusieurs choses dans la manière d’installer Docker sur vos machines et de construire vos Dockerfiles.
Si vous donnez à Docker accès à tout, évidemment que vous risquez la compromission de vos données !

0. La machine hôte

Comme pour n’importe quelle machine / VM :

  1. Docker doit être à jour
  2. Pour éviter tout risque, ne mettez sur l’hôte que les données dont vos containers vont avoir besoin
  3. Isolez votre réseau en fonction de vos exigences de sécurité

 

1. Ajoutez un utilisateur « docker »

De base, lorsque vous installez Docker, seul l’utilisateur « root » aura accès à Docker. Par conséquent, Docker aura accès à tous les fichiers et dossiers de vos disques durs.

Exécutez le script suivant pour restreindre Docker à un utilisateur :

groupadd -g 500000 dockremap && 
groupadd -g 501000 dockremap-user && 
useradd -u 500000 -g dockremap -s /bin/false dockremap && 
useradd -u 501000 -g dockremap-user -s /bin/false dockremap-user

echo "dockremap:500000:65536" >> /etc/subuid && 
echo "dockremap:500000:65536" >>/etc/subgid

echo "
  {
   \"userns-remap\": \"default\"
  }
" > /etc/docker/daemon.json

systemctl daemon-reload && systemctl restart docker

Ce script va créer l’utilisateur et le groupe dockremap que Docker est prêt à utiliser.
Cet utilisateur n’aura aussi pas de shell (/bin/false), pour limiter ce qu’il est autorisé à faire.

 

2. Ne jamais utiliser une image sans la vérifier

Toujours vérifier les Dockerfils et/ou le docker-compose.yml :

  1. Vérifiez les volumes auxquels a accès le container (et surtout que le Dockerfile ne demande pas accès à des dossiers ou processus noyau)
  2. Vérifiez que l’instruction FROM du Dockerfile définit une image exacte (Ex: il faut que le Dockerfile spécifie « nginx:1.16-alpine« , pas uniquement « nginx« )
  3. Ne pas autoriser d’autre registry que le votre ou l’officiel (hub.docker.com) pour la récupération des images Docker ! (cf. Docker Content Trust)

 

3. Ne stockez pas vos secrets (mots de passe, clés SSH, identifiants…)

Ne stockez pas vos mots de passe ou n’importe quel autre identifiant dans un fichier ou en mémoire. Docker Secret est fait pour ça et se chargera de chiffrer les secrets.
Il fonctionne sous forme de clés/valeurs.

Lorsque vous accordez à un service nouvellement créé ou en cours d’exécution l’accès à un secret, le secret déchiffré est monté dans le conteneur dans un système de fichiers en mémoire. L’emplacement du point de montage dans le conteneur est par défaut /run/secrets/<secret_name>. Vous pouvez spécifier un emplacement personnalisé dans depuis Docker 17.06.

Cependant, Docker Secret a l’inconvénient de nécessiter l’installation de Docker Swarm.

Si vous ne voulez pas utiliser Docker Secret, utilisez s’il vous plaît au moins un fichier .env que vous NE PUBLIEREZ PAS dans votre instance Git.
Spécifiez ensuite « –env-file .env » dans votre commande Docker ou dans votre docker-compose.yml avec l’instruction « env_file: .env« .

A ce moment là, assurez vous de donner des accès restreints à ces fichiers.

 

4. Limiter le nombre de dépendances

N’installez que le strict nécessaire sur votre container. Le container tournera plus vite et sera sujet à moins de vulnérabilités.

Par exemple, avez-vous vraiment besoin de nano ou de VIM dans votre container ?
Avez-vous vraiment besoin du compilateur gcc après avoir compilé votre application C ?
Avez-vous vraiment besoin de l’image complète ? Ne pouvez-vous pas plutôt utiliser une version « -alpine » ?
Avez-vous vraiment besoin de ce paquet Python, Javascript ?

 

5. Restreignez les ressources allouées à un container

Les conteneurs, lorsqu’ils sont infectés ou attaqués, peuvent finir par consommer beaucoup de ressources comme la mémoire ou du CPU. Ceci peut être évité en fixant des limites aux ressources que chaque conteneur peut consommer. Cela peut être fait en utilisant les commandes suivantes.

To set a memory limit:

--memory="1000M"

To restrict CPU usage

--cpus=X

où X est le nombre de CPU mis à disposition pour ce conteneur.

 

6. Restreignez l’accès en lecture aux volumes qui n’ont pas besoin d’écrire

Il est possible de restreindre l’accès au volume d’un container en read-only. Cela évitera des suppression de volumes accidentels.

Si votre container n’a pas besoin d’écrire dans un volume, il suffit d’ajouter « :ro » à la fin d’une déclaration de volume Docker.
Exemple :

-v "./my_data:/app/my_data:ro"

 

7. Stockez les données persistantes à un endroit unique

Je vous conseille de stocker les données persistantes de vos containers (base de données, logs, fichiers de stockage etc…)  dans un dossier unique. Cela évitera les suppressions accidentelles.

Par exemple, si j’ai un service « GitLab », je stockerai ses données persistantes dans le dossier :

/srv/docker/gitlab

 

8. Limitez l’accès de votre container à Internet

Est-ce que votre container a vraiment besoin d’internet pour fonctionner ? Si vous faites tourner une API qui se suffit à elle-même, votre image Docker peut suffire à contenir les dépendances nécessaires au bon fonctionnement de votre API.

Empêcher l’accès internet d’un container via docker run :

docker run ... --network none

Empêcher l’accès internet d’un container dans un docker-compose.yml :
Ici, l’objectif est d’isoler les réseaux entre les containers.

version: '3'

services:
    container-no-internet:
        image: alpine
        networks:
            - no-internet

    container-with-internet:
        image: alpine
        networks:
            - internet
    
    container-in-both-networks:
        image: alpine
        networks: 
            - internet
            - no-internet

networks:
    no-internet:
        driver: bridge
        internal: true # CETTE LIGNE PERMET DE BLOQUER INTERNET
    internet:
        driver: bridge

 

9. Créez votre propre registre Docker

Un registre (registry) Docker est un espace où sont stockées des images Docker. GitLab peut disposer d’un GitLab Registry qui fait office de Docker Registry. Le registre officiel est « hub.docker.com« .

Avoir son propre registre a plusieurs avantages :

  1. Stocker et utiliser les images que vous souhaitez en hors ligne (se fait via commande « docker save », puis « docker load » et « docker push »)
  2. Stocker et utiliser VOS propres images, sans les publier sur internet
  3. Contrôler les mises à jour des images (et donc leur contenu)

Docker propose même un système d’images signées qui permet d’authentifier ses images et celles que l’on souhaite exécuter avec Docker Notary.
Pour activer cette fonctionnalité, utilisez la commande « export DOCKER_CONTENT_TRUST=1« .

L’idéal est d’avoir la machine exécutant vos containers et votre registre Docker sur un réseau indépendant sans accès internet.
Vérifiez vos images Docker sur une machine connectée à internet puis une fois certain qu’elle ne présente pas de faille de sécurité, chargez la en hors ligne dans votre registre.

 

10. GitLab Runners – Intégration continue

Cette partie est extrêmement importante si vous utilisez Docker pour faire tourner vos scripts d’intégration continue au travers d’un runner GitLab.
Il y a toujours un risque de privilege escalation avec Docker si vous faites tourner vos scripts d’IC sur une machine de production.

En effet, votre runner peut lancer des commandes en tant que l’utilisateur classique qui lance vos containers de production.
Il faut garder en tête que n’importe quel utilisateur qui « push » du code (si configuré par défaut dans votre .gitlab-ci.yml) va faire exécuter le script d’intégration continue qu’il a décidé d’écrire .

  1. Ajoutez un utilisateur Docker (règle 1 de ce guide)
  2. Veillez à utiliser l’exécuteur « docker » avec l’image DinD (docker in docker). N’utilisez jamais l’exécuteur « shell »
  3. Contrôlez sérieusement vos tags
    1. Vos runners peuvent être configurés avec des tags. Par exemple, un runner acceptera les tâches « development » et un autre les tâches « production ».
      Veillez à avoir au moins 2 runners qui tournent sur des machines ou des VMs indépendantes : 1 machine pour chaque tag.
    2. Dans vos .gitlab-ci.yml, taggez correctement vos jobs.
    3. Tout ce qui n’a PAS vocation à être lancé sur une machine de production doit porter le tag « development ».

 

11. Docker Bench Security : auditez votre installation Docker

Un super outil proposé par Docker qui va vérifier des dizaines de « best-practices » communes concernant le déploiement des conteneurs Docker en production.

docker run -it --net host --pid host --userns host --cap-add audit_control \
    -e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
    -v /var/lib:/var/lib \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v /usr/lib/systemd:/usr/lib/systemd \
    -v /etc:/etc --label docker_bench_security \
    docker/docker-bench-security

Le rapport de cette analyse est affiché en temps-réel lors de l’exécution.

 

Share This:

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.