Docker rootless : installation, limites et dépannage des erreurs de permissions
Docker « rootless » permet d’exécuter le démon Docker et les conteneurs sans privilèges root, en s’appuyant sur des mécanismes du noyau (namespaces utilisateur, cgroups, etc.) et sur des outils comme RootlessKit et slirp4netns. L’objectif principal est de réduire l’impact d’une compromission : si un conteneur s’échappe, il n’obtient pas directement les privilèges root sur l’hôte.
Ce tutoriel couvre :
- le pré-requis (noyau, paquets, subuid/subgid, cgroups),
- l’installation et l’activation,
- l’usage quotidien (images, réseaux, volumes, ports),
- les limites connues,
- un gros chapitre de dépannage des erreurs de permissions (les plus fréquentes).
1) Comprendre le « rootless » : ce qui change vraiment
En mode classique, Docker fonctionne avec :
- un démon
dockerdlancé en root, - des conteneurs isolés, mais dont certains composants (montages, réseau, cgroups) sont orchestrés par root.
En mode rootless :
dockerdtourne dans votre session utilisateur (par exemplealice),- les conteneurs voient un utilisateur
rootdans leur namespace, mais cerootest mappé vers un UID non privilégié sur l’hôte, - les opérations nécessitant des privilèges (montages, réseau, cgroups) sont réalisées via des mécanismes compatibles non-root (fuse-overlayfs, slirp4netns, RootlessKit…).
1.1 Namespaces utilisateur et mappage UID/GID
Le cœur du rootless repose sur userns. Le conteneur peut croire qu’il est root (UID 0), mais sur l’hôte, ce « root » est remappé vers un UID réel non privilégié (ex: 1000). Pour que cela fonctionne, il faut des plages d’UID/GID « subordonnées » déclarées dans :
/etc/subuid/etc/subgid
Ces plages servent à attribuer des identités supplémentaires aux processus du conteneur sans être root sur l’hôte.
2) Pré-requis et vérifications
Les étapes exactes varient selon la distribution, mais les points essentiels sont identiques.
2.1 Vérifier la version de Docker
Docker rootless est supporté depuis Docker 20.10+ (et amélioré au fil du temps). Vérifiez :
docker --version
Si Docker n’est pas installé, installez-le via votre gestionnaire de paquets ou les dépôts officiels Docker.
2.2 Outils nécessaires (paquets)
Selon la distribution, vous aurez besoin de certains paquets :
uidmap(fournitnewuidmap/newgidmap)slirp4netns(réseau en mode user)fuse-overlayfs(stockage overlay en user)iptablesn’est pas requis pour rootless (souvent non utilisé), mais peut être utile pour d’autres usages- parfois
dbus-user-session
Exemples :
Debian/Ubuntu :
sudo apt update
sudo apt install -y uidmap slirp4netns fuse-overlayfs
Fedora :
sudo dnf install -y shadow-utils slirp4netns fuse-overlayfs
Sur Fedora,
newuidmap/newgidmappeuvent venir deshadow-utils. Sur Debian/Ubuntu, c’estuidmap.
2.3 Vérifier la présence de newuidmap/newgidmap
command -v newuidmap
command -v newgidmap
Vous devriez obtenir un chemin, par exemple /usr/bin/newuidmap.
2.4 Configurer /etc/subuid et /etc/subgid
Vérifiez si votre utilisateur a une plage :
grep -E "^$USER:" /etc/subuid /etc/subgid
Exemple attendu :
/etc/subuid:alice:100000:65536
/etc/subgid:alice:100000:65536
Si rien n’apparaît, ajoutez une plage (exemple avec 65536 IDs) :
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 "$USER"
Déconnectez-vous / reconnectez-vous pour que la session prenne en compte les changements.
2.5 Vérifier cgroups (v2 recommandé)
Rootless fonctionne mieux avec cgroup v2. Vérifiez :
stat -fc %T /sys/fs/cgroup
cgroup2fs= v2tmpfsou autre = probablement v1 (ou hybride)
Sur systemd, vous pouvez aussi vérifier :
mount | grep cgroup
Rootless peut fonctionner en cgroup v1, mais avec des limitations (notamment sur la gestion fine des ressources). Sur certaines distros, cgroup v2 est déjà activé.
3) Installation de Docker rootless
Docker fournit un script d’installation rootless qui configure un service systemd utilisateur et un contexte Docker.
3.1 Lancer le script d’installation
Le script s’appelle souvent dockerd-rootless-setuptool.sh. Selon la distro, il peut être installé avec Docker ou disponible dans docker-ce-rootless-extras.
Sur Debian/Ubuntu (dépôts Docker), installez l’extra :
sudo apt install -y docker-ce-rootless-extras
Puis lancez :
dockerd-rootless-setuptool.sh install
Sortie typique : création d’un service docker.service dans votre session utilisateur, et instructions pour définir DOCKER_HOST.
3.2 Activer le service systemd utilisateur
Activez et démarrez le service :
systemctl --user enable --now docker
Vérifiez l’état :
systemctl --user status docker
Consultez les logs :
journalctl --user -u docker -e
3.3 Configurer l’environnement DOCKER_HOST
En rootless, le socket Docker n’est pas /var/run/docker.sock (réservé à root), mais généralement :
unix:///run/user/<UID>/docker.sock
Pour l’exporter dans votre shell :
export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock
Pour le rendre permanent, ajoutez-le à ~/.bashrc ou ~/.zshrc.
Vérifiez que le client parle au bon démon :
docker context ls
docker info
Dans docker info, cherchez :
rootless: trueDocker Root Dir: /home/<user>/.local/share/docker(souvent)Server Version: ...
3.4 Astuce : éviter la déconnexion du service (linger)
Si vous voulez que Docker rootless continue de tourner même sans session active (utile sur serveur), activez le « linger » :
sudo loginctl enable-linger "$USER"
4) Utilisation quotidienne : images, conteneurs, ports, volumes
4.1 Tester avec un conteneur simple
docker run --rm hello-world
Puis un shell :
docker run --rm -it alpine:3.20 sh
id
Vous verrez uid=0(root) dans le conteneur, mais cela ne signifie pas root sur l’hôte.
4.2 Réseau en rootless : slirp4netns et conséquences
En rootless, Docker ne peut pas créer des interfaces réseau privilégiées (bridge, veth) comme root. Il utilise souvent :
slirp4netns(NAT user-space)- ou
vpnkitselon environnements
Conséquences :
- performances réseau parfois inférieures,
- certaines fonctionnalités bas niveau (ex: ping ICMP) peuvent être limitées,
- l’exposition de ports est gérée via RootlessKit.
Vérifiez le réseau :
docker network ls
docker network inspect bridge
4.3 Publier des ports : attention aux ports < 1024
Publier un port est identique côté CLI :
docker run --rm -p 8080:80 nginx:alpine
Mais publier un port privilégié (ex: 80) peut échouer car un utilisateur non-root ne peut pas binder un port <1024.
Option A : utiliser un port > 1024
docker run --rm -p 8080:80 nginx:alpine
Option B : autoriser les ports bas via sysctl (si acceptable)
Vous pouvez abaisser la limite de ports non privilégiés :
sudo sysctl -w net.ipv4.ip_unprivileged_port_start=80
Pour rendre permanent (exemple) :
echo "net.ipv4.ip_unprivileged_port_start=80" | sudo tee /etc/sysctl.d/99-rootless-ports.conf
sudo sysctl --system
Sécurité : cela autorise tous les utilisateurs à binder des ports à partir de 80. À évaluer selon votre contexte.
4.4 Volumes et bind mounts : le point le plus délicat
En rootless, les bind mounts (-v /chemin/hote:/data) sont souvent la source principale d’erreurs de permissions.
Exemple :
mkdir -p ~/data
docker run --rm -it -v ~/data:/data alpine sh
touch /data/test
Si ça échoue, c’est généralement lié au mappage UID/GID et à la propriété des fichiers.
4.4.1 Comprendre le problème UID/GID
Dans le conteneur, vous êtes root (UID 0) mais sur l’hôte, ce root correspond à votre UID réel (ex: 1000) ou à un UID subordonné selon la configuration. Résultat : un fichier créé dans le conteneur peut apparaître sur l’hôte avec un UID « étrange » (ex: 100999) ou être inaccessible.
4.4.2 Utiliser --user pour aligner les permissions
Souvent, le plus simple est d’exécuter le processus du conteneur avec votre UID/GID :
docker run --rm -it \
--user "$(id -u):$(id -g)" \
-v ~/data:/data \
alpine sh
Ainsi, les fichiers créés appartiennent à votre utilisateur sur l’hôte.
4.4.3 Utiliser les options :Z / :z (SELinux)
Sur Fedora/RHEL avec SELinux, les bind mounts peuvent être bloqués par des contextes SELinux. Essayez :
docker run --rm -it -v ~/data:/data:Z alpine sh
:Z: label privé (recommandé pour un seul conteneur):z: label partagé (si plusieurs conteneurs partagent le volume)
En rootless, SELinux peut être plus subtil : selon la distro, certaines opérations de relabel peuvent être limitées. Si
:Zéchoue, regardez les logs SELinux (ausearch,journalctl).
5) Stockage : overlay2, fuse-overlayfs et performance
En rootless, Docker ne peut pas toujours utiliser overlay2 de manière classique. Il peut basculer sur :
fuse-overlayfs(overlay via FUSE)- ou
vfs(très lent, à éviter)
Vérifiez le driver :
docker info | grep -E "Storage Driver|Backing Filesystem"
Si vous voyez vfs, installez/activez fuse-overlayfs et vérifiez que Docker le détecte.
6) Limites connues de Docker rootless (et contournements)
6.1 Accès aux périphériques et fonctionnalités kernel
--privilegedperd beaucoup de sens en rootless (vous ne devenez pas root sur l’hôte).- Accès à
/dev/kvm, GPU, périphériques USB : possible mais dépend de permissions hôte (groupes, udev) et reste limité. - Montage de systèmes de fichiers (ex:
--mount type=bindok, mais certains montages spéciaux non).
6.2 Réseau : pas de vrai bridge L2
- Pas de
macvlan/ipvlantypiquement. - Performances et latence parfois moins bonnes.
- Certains outils réseau nécessitant CAP_NET_ADMIN ne fonctionneront pas.
6.3 Cgroups et limitations de ressources
Selon votre configuration cgroup v2 et systemd, certaines limites (--memory, --cpus) peuvent ne pas s’appliquer comme en rootful, ou nécessiter des réglages.
Vérifiez :
docker run --rm --memory=256m alpine sh -c "cat /sys/fs/cgroup/memory.max 2>/dev/null || true"
6.4 Exposition de ports et services système
- Ports < 1024 : voir section précédente.
- Intégration avec des firewalls système : rootless ne manipule pas iptables de la même manière.
7) Dépannage : erreurs de permissions les plus fréquentes
Cette section est volontairement détaillée : en rootless, 80% des problèmes sont des problèmes de permissions (fichiers, sockets, cgroups, user namespaces).
7.1 « Cannot connect to the Docker daemon… permission denied »
Message typique :
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
Got permission denied while trying to connect to the Docker daemon socket...
Cause : votre client Docker essaie de parler au socket rootful (/var/run/docker.sock) au lieu du socket rootless.
Correctifs :
- Vérifier
DOCKER_HOST:
echo "$DOCKER_HOST"
- Pointer vers le socket user :
export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock
- Vérifier que le service tourne :
systemctl --user status docker
- Vérifier l’existence du socket :
ls -l /run/user/$(id -u)/docker.sock
Si le socket n’existe pas, regardez les logs :
journalctl --user -u docker -n 200 --no-pager
7.2 « newuidmap: write to uid_map failed: Operation not permitted »
Cause : newuidmap/newgidmap manquants, ou /etc/subuid//etc/subgid non configurés, ou noyau limitant les user namespaces.
Diagnostic :
command -v newuidmap newgidmap
grep -E "^$USER:" /etc/subuid /etc/subgid
sysctl kernel.unprivileged_userns_clone 2>/dev/null || true
Correctifs :
- Installer
uidmap(Debian/Ubuntu) ou paquet équivalent. - Ajouter une plage subuid/subgid (voir section 2.4).
- Sur certaines distros, activer userns :
sudo sysctl -w kernel.unprivileged_userns_clone=1
Pour persister :
echo "kernel.unprivileged_userns_clone=1" | sudo tee /etc/sysctl.d/99-userns.conf
sudo sysctl --system
Attention : activer les user namespaces non privilégiés peut être un choix de sécurité. Certaines distributions les désactivent par défaut.
7.3 « failed to mount overlay: permission denied » / stockage en échec
Cause : overlayfs non utilisable en rootless, fuse-overlayfs absent, ou restrictions du FS.
Diagnostic :
docker info | grep -E "Storage Driver|Rootless|Docker Root Dir"
Regarder les logs :
journalctl --user -u docker -e
Correctifs :
- Installer
fuse-overlayfs:
sudo apt install -y fuse-overlayfs
- Redémarrer Docker rootless :
systemctl --user restart docker
- Vérifier que le driver n’est pas
vfs:
docker info | grep "Storage Driver"
Si vous êtes sur un FS exotique ou monté avec des options restrictives, essayez de placer le Docker Root Dir sur un FS plus standard (ext4/xfs). En rootless, le répertoire est souvent dans ~/.local/share/docker.
7.4 « permission denied » sur un bind mount (volume)
Exemple :
docker run --rm -v /srv/data:/data alpine sh -c "touch /data/x"
# touch: /data/x: Permission denied
Causes fréquentes :
/srv/dataappartient à root et n’est pas accessible en écriture,- mappage UID/GID : l’UID dans le conteneur ne correspond pas à l’UID propriétaire sur l’hôte,
- SELinux/AppArmor bloque l’accès,
- montage NFS/CIFS avec options empêchant les userns.
Étapes de diagnostic :
- Vérifier les permissions hôte :
ls -ld /srv/data
getfacl -p /srv/data 2>/dev/null || true
- Tester avec votre UID dans le conteneur :
docker run --rm -it --user "$(id -u):$(id -g)" -v /srv/data:/data alpine sh
- Si SELinux (Fedora/RHEL) :
ls -Z /srv/data 2>/dev/null || true
Puis tester :
docker run --rm -it -v /srv/data:/data:Z alpine sh
Correctifs possibles :
- Ajuster propriétaire/permissions :
sudo chown -R "$USER:$USER" /srv/data
# ou
sudo chmod -R u+rwX /srv/data
- Utiliser
--user(recommandé pour les apps qui écrivent dans le volume). - Utiliser ACL :
sudo setfacl -m u:$USER:rwx /srv/data
sudo setfacl -d -m u:$USER:rwx /srv/data
- Si NFS/CIFS : vérifier les options de montage. Certains partages ne supportent pas bien les user namespaces. Dans ce cas, privilégier un répertoire local, ou exécuter en rootful, ou adapter le montage (selon infra).
7.5 « bind: permission denied » lors du publish de port
Exemple :
docker run --rm -p 80:80 nginx:alpine
# Error starting userland proxy: listen tcp 0.0.0.0:80: bind: permission denied
Cause : port < 1024.
Solutions :
- Utiliser un port > 1024 :
docker run --rm -p 8080:80 nginx:alpine
- Ou abaisser
ip_unprivileged_port_start(voir section 4.3).
7.6 « cgroup: permission denied » / limites de ressources non appliquées
Symptômes : --memory, --pids-limit, --cpus ne fonctionnent pas, ou erreurs dans les logs.
Diagnostic :
docker info | grep -E "Cgroup Driver|Cgroup Version|rootless"
Vérifier cgroup v2 :
stat -fc %T /sys/fs/cgroup
Correctifs :
- Activer cgroup v2 au niveau système (dépend de la distro et du bootloader).
- S’assurer que systemd user a les droits nécessaires (parfois via configuration systemd et
Delegate=côté unités). En rootless « pur », Docker s’appuie sur systemd user ; si la distro est ancienne, cela peut être limité.
7.7 « operation not permitted » lors de certaines syscalls (ex: ping, mount, setcap)
En rootless, certaines capacités Linux ne peuvent pas être accordées réellement.
pingpeut échouer (ICMP brut).mountdans le conteneur : souvent non.setcap: peut échouer selon FS et userns.
Contournements :
- Utiliser des outils applicatifs qui ne nécessitent pas ces syscalls.
- Pour
ping, installeriputils-pingsetuid dans l’image (pas toujours souhaitable) ou utiliser des alternatives (HTTP checks, TCP connect). - Accepter que certaines opérations nécessitent rootful.
7.8 Problèmes de DNS / accès Internet depuis les conteneurs
Cause : slirp4netns, resolv.conf, VPN, DNS split-horizon.
Diagnostic :
docker run --rm alpine sh -c "cat /etc/resolv.conf; nslookup example.com 2>/dev/null || true"
Tester connectivité :
docker run --rm alpine sh -c "wget -qO- https://example.com >/dev/null && echo OK"
Correctifs :
- Définir explicitement des DNS :
docker run --rm --dns 1.1.1.1 --dns 8.8.8.8 alpine nslookup example.com
- Configurer le démon rootless via
~/.config/docker/daemon.json(si applicable) :
mkdir -p ~/.config/docker
cat > ~/.config/docker/daemon.json <<'EOF'
{
"dns": ["1.1.1.1", "8.8.8.8"]
}
EOF
systemctl --user restart docker
8) Configuration utile du démon rootless
8.1 Où sont les fichiers ?
- Socket :
/run/user/<UID>/docker.sock - Données :
~/.local/share/docker(souvent) - Config client :
~/.docker/config.json - Config démon :
~/.config/docker/daemon.json(selon installation)
8.2 Exemple de daemon.json
mkdir -p ~/.config/docker
nano ~/.config/docker/daemon.json
Exemple :
{
"log-driver": "json-file",
"log-opts": { "max-size": "10m", "max-file": "3" },
"dns": ["1.1.1.1", "8.8.8.8"]
}
Puis :
systemctl --user restart docker
9) Bonnes pratiques en rootless
- Préférer
--user $(id -u):$(id -g)pour les conteneurs qui écrivent sur des bind mounts. - Éviter de binder des répertoires système (
/srv,/var/lib/...) sans ajuster propriétaires/ACL. - Documenter la gestion des ports (80/443) : soit reverse proxy rootful, soit sysctl, soit ports >1024.
- Sur serveurs multi-utilisateurs, rootless réduit le risque, mais ne remplace pas :
- mises à jour,
- images minimales,
- politiques seccomp/apparmor,
- scanning d’images.
- Sur workloads exigeants (réseau perf, stockage perf, GPU), évaluer si rootless convient.
10) Scénarios pratiques
10.1 Lancer un service web en rootless sur 8080
docker run -d --name web \
-p 8080:80 \
nginx:alpine
Tester :
curl -I http://127.0.0.1:8080
docker logs web
10.2 Développement avec volume et UID aligné
mkdir -p ~/projet
docker run --rm -it \
--user "$(id -u):$(id -g)" \
-v ~/projet:/work \
-w /work \
node:20-alpine sh
Dans le conteneur :
node -v
npm init -y
Sur l’hôte, les fichiers appartiendront à votre utilisateur.
10.3 Diagnostiquer un conteneur qui ne peut pas écrire
docker run --rm -it -v ~/data:/data alpine sh -c '
id
ls -ld /data
touch /data/test || (echo "ECHEC"; exit 1)
'
Si échec, relancez avec --user :
docker run --rm -it --user "$(id -u):$(id -g)" -v ~/data:/data alpine sh -c '
id
touch /data/test && echo OK
'
11) Désinstallation / retour arrière
Si vous voulez retirer l’installation rootless :
- Arrêter le service user :
systemctl --user stop docker
systemctl --user disable docker
- Supprimer les fichiers (attention : cela supprime images/volumes rootless) :
rm -rf ~/.local/share/docker
rm -rf ~/.config/docker
- Optionnel : retirer
docker-ce-rootless-extrassi installé :
sudo apt remove docker-ce-rootless-extras
12) Checklist rapide de dépannage
- Le démon tourne-t-il ?
systemctl --user status docker journalctl --user -u docker -n 200 --no-pager - Le client pointe-t-il vers le bon socket ?
echo "$DOCKER_HOST" ls -l /run/user/$(id -u)/docker.sock subuid/subgidsont-ils configurés ?grep -E "^$USER:" /etc/subuid /etc/subgid- Les paquets nécessaires sont-ils présents ?
command -v newuidmap newgidmap slirp4netns fuse-overlayfs - Les volumes échouent-ils ? Tester
--useret (si SELinux):Z. - Ports <1024 ? Utiliser 8080/8443 ou
ip_unprivileged_port_start.
Conclusion
Docker rootless est une excellente option quand vous voulez réduire l’exposition aux privilèges root, notamment sur des postes de dev, des environnements partagés, ou des serveurs où vous souhaitez limiter l’impact d’un conteneur compromis. En échange, vous acceptez des compromis : réseau moins « bas niveau », limitations sur ports privilégiés et certaines capacités kernel, et surtout une gestion des volumes plus exigeante.
Si vous me donnez votre distribution, votre version de Docker, et un extrait de docker info + l’erreur exacte (et la commande qui la déclenche), je peux proposer un diagnostic ciblé (réseau, stockage, userns, SELinux, cgroups).