Aller au contenu

Docker pour les curieux : initiation et mode rootless

Docker permet de lancer des logiciels dans des conteneurs : des processus isolés du reste du système. Cette fiche explique à quoi cela sert, comment cela fonctionne, et comment l'installer sans donner les clés de la machine (mode rootless).

À quoi sert Docker pour un particulier ?

C'est un atelier propre et jetable pour apprendre, expérimenter et héberger de petits services.

  • Essayer sans risque : un docker run lance un logiciel, un docker rm le fait disparaître entièrement, sans paquets ni fichiers de configuration laissés sur le système.
  • Plusieurs versions côte à côte : PHP 8.1 et PHP 8.4, MariaDB 10 et 11, chacun dans son conteneur.
  • Un environnement identique partout : très pratique pour l'enseignement, où un simple fichier compose.yaml remplace une longue installation à la main.
  • Héberger ses propres services : stockage de fichiers, gestionnaire de photos, lecteur RSS, blocage de publicités sur le réseau.
  • Reproductibilité : un fichier décrit toute une installation, que l'on peut reconstruire en quelques minutes après une panne.

Les limites : pour un ou deux logiciels stables, un simple apt install reste plus direct. Il faut apprendre quelques notions (images, volumes, réseaux) pour ne pas perdre de données. Les images non officielles ne sont pas toutes fiables, et images et volumes occupent de la place disque (docker system prune de temps en temps).

Docker Engine ou Docker Desktop ?

  • Docker Engine est le moteur : le démon dockerd, le client docker, containerd et runc. Il est libre, sans interface graphique, et utilise directement le noyau Linux.
  • Docker Desktop est une application qui emballe Engine avec une interface, Compose et des extensions. Sous Windows et macOS, elle lance une petite machine virtuelle Linux, car ces systèmes n'ont pas de noyau Linux.

Sous Linux, Engine seul suffit : plus léger, plus direct, et compatible avec le mode rootless.

Que se passe-t-il derrière un docker run ?

┌─ Espace utilisateur (son compte, en rootless) ──┐
│                                                 │
│   docker (client)   La commande que l'on tape   │
│         │                                       │
│         ▼                                       │
│   dockerd           Images, réseaux, volumes    │
│         │                                       │
│         ▼                                       │
│   containerd        Gère le cycle de vie        │
│         │                                       │
│         ▼                                       │
│   runc ─────────────────────────────────┐       │
│         │                               │       │
│         ▼                               │       │
│   Conteneur (un processus isolé)        │       │
│                                         │       │
└─────────────────────────────────────────┼───────┘
                                          ▼
                Noyau Linux : namespaces et cgroups
  • Le client envoie la commande au démon par un socket.
  • dockerd télécharge les images et gère réseaux et volumes.
  • containerd démarre, arrête et surveille les conteneurs.
  • runc demande au noyau de créer les isolations, lance le processus, puis s'efface.
  • Le noyau fait le vrai travail : les namespaces donnent au conteneur sa propre vue des processus, du réseau et des fichiers, et les cgroups limitent ses ressources.

Un conteneur n'est donc pas une machine virtuelle : c'est un simple processus Linux enfermé par le noyau, sans noyau propre.

Les dangers de l'installation classique

Dans l'installation par défaut, le démon tourne en root, et l'accès à son socket équivaut à être root sur la machine.

  • Le groupe docker : en faire partie permet, par exemple, de monter tout le disque dans un conteneur et d'y obtenir un shell root. C'est en pratique un sudo sans mot de passe.
  • --privileged désactive presque toutes les protections.
  • Monter le socket Docker dans un conteneur donne à ce conteneur le contrôle de l'hôte.
  • Les ports publiés (-p 8080:80) écoutent sur toutes les interfaces, et Docker peut contourner le pare-feu. Préfèrer-p 127.0.0.1:8080:80.
  • Les images douteuses : privilégiez les images officielles, épinglez les versions (pas de latest) et scannez-les avec trivy.
  • Les secrets placés dans les variables d'environnement restent visibles avec docker inspect.

Installer Docker en mode rootless

Le démon tourne alors sous l'utilisateur, dans un espace de noms utilisateur : le « root » du conteneur n'est qu'un simple UID sans privilège sur l'hôte.

1. Prérequis et arrêt du Docker root

sudo apt install uidmap dbus-user-session
sudo systemctl disable --now docker.service docker.socket
sudo systemctl disable --now containerd

Vérifiez que le socket root a disparu :

ls -l /var/run/docker.sock   # doit répondre : Aucun fichier ou dossier de ce type

2. Installer et démarrer le démon rootless

Depuis une session normale (pas un shell ouvert avec su -, newgrp ou sg) :

dockerd-rootless-setuptool.sh install
systemctl --user enable --now docker
sudo loginctl enable-linger $USER   # démon actif même sans session ouverte

3. Retirer les groupes à risque

sudo gpasswd -d $USER docker

Puis se déconnecter complètement de sa session graphique (ou redémarrer) : les groupes ne sont pris en compte qu'à l'ouverture de session. Fermer le navigateur ne suffit pas.

4. Vérifier

id                                             # ni docker ni root dans les groupes
echo $DOCKER_HOST                              # vide
docker info --format '{{.SecurityOptions}}'    # doit contenir name=rootless
ps -o user,pid,cmd -C dockerd,containerd       # USER = ton compte
docker run --rm hello-world

Pièges rencontrés en pratique

Symptôme Cause Remède
Aborting because rootful Docker ... is running Le socket root existe encore, souvent relancé par docker.socket Arrêter docker.socket et docker.service, vérifier que /var/run/docker.sock a disparu
newuidmap ... gid:123 pw_gid:1000 Le shell tourne avec le groupe docker comme groupe principal (après newgrp docker, par exemple) Retirer le groupe docker, se reconnecter, vérifier id
systemd not detected Shell ouvert avec su -, sans XDG_RUNTIME_DIR Utiliser une vraie session de bureau, ou vérifier systemctl --user status
Le client vise .../podman/podman.sock Une variable DOCKER_HOST définie par des fichiers restants de podman-docker dans /etc/profile.d sudo apt purge podman-docker, puis se reconnecter
Le contexte rootless pointe vers ~/.docker/run/docker.sock Reliquat d'une installation ratée docker context rm rootless, puis docker context create rootless --docker host=unix:///run/user/1000/docker.sock
Avertissements No io.max, No cpuset support Contrôleurs cgroup non délégués à l'utilisateur Sans gravité ; voir cgroup.controllers pour ce qui est disponible

Durcir un conteneur

Même en rootless, on réduit la surface d'attaque conteneur par conteneur :

docker run -d --name web \
  --read-only --tmpfs /tmp --tmpfs /var/cache/nginx --tmpfs /var/run \
  --cap-drop ALL \
  --security-opt no-new-privileges \
  --pids-limit 100 --memory 256m \
  -p 127.0.0.1:8080:8080 \
  nginxinc/nginx-unprivileged
  • --read-only : système de fichiers en lecture seule (les --tmpfs fournissent les dossiers inscriptibles nécessaires).
  • --cap-drop ALL : retire toutes les capabilities.
  • --security-opt no-new-privileges : empêche l'élévation via setuid.
  • --pids-limit et --memory : évitent qu'un conteneur ne plombe la machine.
  • -p 127.0.0.1:... : n'expose le service qu'à la machine locale.

Bonnes habitudes

  • Ne jamais utiliser --privileged ni --network host sans raison précise.
  • Ne pas monter / ou /home en volume, ni le socket Docker dans un conteneur.
  • Utiliser des images officielles à version épinglée.
  • Garder l'hôte (noyau, Docker) à jour.

Particularités du rootless

  • Les ports inférieurs à 1024 sont interdits par défaut : publier sur un port élevé (8080, par exemple), ou autoriser l'accès avec sudo sysctl net.ipv4.ip_unprivileged_port_start=80.
  • Le réseau passe par slirp4netns, un peu plus lent, ce qui est sans conséquence pour l'expérimentation.
  • Certaines limites de ressources (débit disque, cpuset) dépendent des contrôleurs cgroup délégués à l'utilisateur.

Pour aller plus loin

  • Commandes de base : run, ps, logs, exec, stop, rm, images, pull.
  • Volumes et ports, pour conserver les données et accéder aux services.
  • Docker Compose, pour décrire toute une installation dans un fichier compose.yaml.
  • Documentation officielle du mode rootless : https://docs.docker.com/engine/security/rootless/