====== docker rootless sous ubuntu ====== ^ Date | août 2023 | ^ ::: | 2024/07/31 | ^ Temps | 10 minutes | ^ Difficulté | ★★☆☆☆ | ^ Type | how-to | ^ OS | ✓ Ubuntu GNU/Linux 24.04 LTS «noble numbat» | ^ ::: | ✓ Ubuntu GNU/Linux 22.04 LTS «jammy jellyfish» | ^ cible | sysadmins tout venant | ^ Références | docker : [[https://docs.docker.com/engine/security/rootless/|rootless]] (anglais) | ^ ::: | docker : [[https://docs.docker.com/engine/install/ubuntu/|installation sur ubuntu]] (anglais) | ^ ::: | docker : [[https://docs.docker.com/engine/install/|installation]] (anglais) | | ::: | github : [[https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent|Générer une clé ssh et l'ajouter à l'agent]] | | ::: | digital ocean: [[https://www.digitalocean.com/community/tutorials/how-to-configure-ssh-key-based-authentication-on-a-linux-server-fr|configurer une authentication par clé ssh]] | Le plus simple est de suivre les documentations citées en référence. Mais j'ai noté un ou deux trucs. Ici, un résumé. ===== Commandes à passer sous le compte root ===== Pour économiser mes petits doigts, je ne tape sudo qu'une seule fois, pour devenir root. Si vous ne faites pas cela, vous préfixerez les commandes par sudo quand il le faudra. sudo -i ===== Créer un utilisateur non privilégié ===== Utilisateur système, volontairement sans sudo et à mot de passe bloqué. Connexion uniquement via ssh et des clés. - Création du compte système, ajout dans ''/etc/subuid'' et ''/etc/subgid''. nouveauCompte=dockeruser adduser --system --gecos "Docker rootless account" --group \ --home /home/$nouveauCompte --shell /bin/bash $nouveauCompte chpasswd -e <<<"${nouveauCompte}:*" && IFS=: read _ base taille < <( sort -b -t: -k 2,2n /etc/subuid | tail -1 ) && echo ${nouveauCompte}:$(( base + taille )):65536 >> /etc/subuid && IFS=: read _ base taille < <( sort -b -t: -k 2,2n /etc/subgid | tail -1 ) && echo ${nouveauCompte}:$(( base + taille )):65536 >> /etc/subgid - Copie des clés ssh autorisées de root (très personnel et donc très optionnel). M'enfin, comme mot de passe est verrouillé, il faudra bien des clés ssh autorisées... Copiez les votres, quoi((En cas de manque de connaissances sur le sujet, mettez un mot de passe à cet utilisateur, ou vous pouvez également lire les deux dernières références citées dans le cartouche ce cet article.)). install -d -m 0700 /home/${nouveauCompte}/.ssh cp -a /root/.ssh/authorized_keys /home/${nouveauCompte}/.ssh - Préconfigurer le démon docker rootless avec un fichier daemon.json. Là encore c'est très personnel -- et donc très optionnel -- mais ça donne une idée de ce qu'on peut faire facilement. install -d /home/${nouveauCompte}/.config/docker cat > /home/${nouveauCompte}/.config/docker/daemon.json <<'EOH' { "default-address-pools": [ { "base": "10.14.0.0/16", "size": 24 } ], "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } } EOH - Et finalement, corriger les propriétaires et groupes des fichiers. chown -hR ${nouveauCompte}: /home/${nouveauCompte} ===== Installation des commandes docker ===== Supprimer toute trace de docker qui n'est pas celui livré par docker soi même. for pkg in \ docker.io docker-doc docker-compose docker-compose-v2 \ podman-docker containerd runc \ ; do apt-get purge $pkg done Et puis installation dpkg -l dbus-user-session | grep -q ^.i || sudo apt-get install dbus-user-session dpkg -l uidmap | grep -q ^.i || sudo apt-get install uidmap apt-get install ca-certificates curl gnupg install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg \ -o /etc/apt/keyrings/docker.asc chmod a+r /etc/apt/keyrings/docker.asc echo \ "deb [arch=$(dpkg --print-architecture) \ signed-by=/etc/apt/keyrings/docker.asc] \ https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null apt-get update apt-get install docker-ce docker-ce-cli containerd.io \ docker-buildx-plugin docker-compose-plugin Docker est maintenant installé. Pour l'instant, en mode normal. ===== Préparation du mode rootless ===== Pour qu'on puisse utiliser les port réservés, il faut utiliser **une et une seule** des deux méthodes présentées ci-dessous. - 1ère Alternative, ma préférée.\\ Ajouter la capacité ''CAP_NET_BIND_SERVICE'' à l'exécutable ''rootlesskit''. setcap cap_net_bind_service=ep $(which rootlesskit) * 2023 :\\ A priori, ce réglage est permanent et devrait résister aux mises à jour. mais c'est pas certain. CF [[https://askubuntu.com/a/92703|réponse 92703]] d'//ask ubuntu// et [[https://unix.stackexchange.com/questions/283366/linux-file-capabilities-are-lost-when-i-modify-the-file-is-this-expected-behavi/283408#283408|cette réponse]] sur //unix & linux//. * 2024 :\\ Ça résiste, sans souci. - 2ème Alternative\\ Abaisser le numéro du premier port non privilégié.\\ On indique que les ports non privilégiés démarrent à 0, tout simplement. Mais dans ce cas, tout process peut utiliser les ports non privilégiés, pas seulement les conteneurs docker //rootless//. echo net.ipv4.ip_unprivileged_port_start=0 > /etc/sysctl.d/99-z-unprivileded-ports-for-all.conf sysctl --system ===== Passage en mode rootless ===== En préliminaire, arrêter tout service docker qui traînerait par là, et installer le paquet ''docker-ce-rootless-extras''. systemctl disable --now docker.service docker.socket apt-get install docker-ce-rootless-extras ==== S'assurer que docker sera démarré au boot ==== loginctl enable-linger $nouveauCompte ==== Devenir l'utilisateur non privilégié dockeruser et continuer ==== ssh $nouveauCompte@localhost dockerd-rootless-setuptool.sh install Cette commande crée et active le contexte docker ''rootless''. C'est parfait. Avec cet utilisateur, on peut maintenant utiliser la commande docker, comme si docker était installé de manière standard. ++++détails si ça vous intéresse| dockeruser@pc:~$ dockerd-rootless-setuptool.sh install [INFO] Creating /home/dockeruser/.config/systemd/user/docker.service [INFO] starting systemd service docker.service + systemctl --user start docker.service + sleep 3 + systemctl --user --no-pager --full status docker.service ● docker.service - Docker Application Container Engine (Rootless) Loaded: loaded (/home/dockeruser/.config/systemd/user/docker.service; disabled; preset: enabled) Active: active (running) since Wed 2024-07-31 18:18:23 CEST; 3s ago Docs: https://docs.docker.com/go/rootless/ Main PID: 7091 (rootlesskit) Tasks: 40 Memory: 40.3M (peak: 41.4M) CPU: 586ms CGroup: /user.slice/user-112.slice/user@112.service/app.slice/docker.service ├─7091 rootlesskit --state-dir=/run/user/112/dockerd-rootless --net=slirp4netns --mtu=65520 --slirp4netns-sandbox=auto --slirp4netns-seccomp=auto --disable-host-loopback --port-driver=builtin --copy-up=/etc --copy-up=/run --propagation=rslave /usr/bin/dockerd-rootless.sh ├─7102 /proc/self/exe --state-dir=/run/user/112/dockerd-rootless --net=slirp4netns --mtu=65520 --slirp4netns-sandbox=auto --slirp4netns-seccomp=auto --disable-host-loopback --port-driver=builtin --copy-up=/etc --copy-up=/run --propagation=rslave /usr/bin/dockerd-rootless.sh ├─7127 slirp4netns --mtu 65520 -r 3 --disable-host-loopback --enable-sandbox --enable-seccomp 7102 tap0 ├─7134 dockerd └─7154 containerd --config /run/user/112/docker/containerd/containerd.toml Jul 31 18:18:23 pc dockerd-rootless.sh[7134]: time="2024-07-31T18:18:23.544446299+02:00" level=warning msg="WARNING: No io.weight support" Jul 31 18:18:23 pc dockerd-rootless.sh[7134]: time="2024-07-31T18:18:23.544458281+02:00" level=warning msg="WARNING: No io.weight (per device) support" Jul 31 18:18:23 pc dockerd-rootless.sh[7134]: time="2024-07-31T18:18:23.544468189+02:00" level=warning msg="WARNING: No io.max (rbps) support" Jul 31 18:18:23 pc dockerd-rootless.sh[7134]: time="2024-07-31T18:18:23.544480578+02:00" level=warning msg="WARNING: No io.max (wbps) support" Jul 31 18:18:23 pc dockerd-rootless.sh[7134]: time="2024-07-31T18:18:23.544489911+02:00" level=warning msg="WARNING: No io.max (riops) support" Jul 31 18:18:23 pc dockerd-rootless.sh[7134]: time="2024-07-31T18:18:23.544499485+02:00" level=warning msg="WARNING: No io.max (wiops) support" Jul 31 18:18:23 pc dockerd-rootless.sh[7134]: time="2024-07-31T18:18:23.544546115+02:00" level=info msg="Docker daemon" commit=cc13f95 containerd-snapshotter=false storage-driver=overlay2 version=27.1.1 Jul 31 18:18:23 pc dockerd-rootless.sh[7134]: time="2024-07-31T18:18:23.544688024+02:00" level=info msg="Daemon has completed initialization" Jul 31 18:18:23 pc dockerd-rootless.sh[7134]: time="2024-07-31T18:18:23.596819194+02:00" level=info msg="API listen on /run/user/112/docker.sock" Jul 31 18:18:23 pc systemd[4564]: Started docker.service - Docker Application Container Engine (Rootless). + DOCKER_HOST=unix:///run/user/112/docker.sock /usr/bin/docker version Client: Docker Engine - Community Version: 27.1.1 API version: 1.46 Go version: go1.21.12 Git commit: 6312585 Built: Tue Jul 23 20:00:07 2024 OS/Arch: linux/arm64 Context: default Server: Docker Engine - Community Engine: Version: 27.1.1 API version: 1.46 (minimum version 1.24) Go version: go1.21.12 Git commit: cc13f95 Built: Tue Jul 23 20:00:07 2024 OS/Arch: linux/arm64 Experimental: false containerd: Version: 1.7.19 GitCommit: 2bf793ef6dc9a18e00cb12efb64355c2c9d5eb41 runc: Version: 1.7.19 GitCommit: v1.1.13-0-g58aa920 docker-init: Version: 0.19.0 GitCommit: de40ad0 rootlesskit: Version: 2.0.2 ApiVersion: 1.1.1 NetworkDriver: slirp4netns PortDriver: builtin StateDir: /run/user/112/dockerd-rootless slirp4netns: Version: 1.2.1 GitCommit: 09e31e92fa3d2a1d3ca261adaeb012c8d75a8194 + systemctl --user enable docker.service Created symlink /home/dockeruser/.config/systemd/user/default.target.wants/docker.service → /home/dockeruser/.config/systemd/user/docker.service. [INFO] Installed docker.service successfully. [INFO] To control docker.service, run: `systemctl --user (start|stop|restart) docker.service` [INFO] To run docker.service on system startup, run: `sudo loginctl enable-linger dockeruser` [INFO] Creating CLI context "rootless" Successfully created context "rootless" [INFO] Using CLI context "rootless" Current context is now "rootless" [INFO] Make sure the following environment variable(s) are set (or add them to ~/.bashrc): export PATH=/usr/bin:$PATH [INFO] Some applications may require the following environment variable too: export DOCKER_HOST=unix:///run/user/112/docker.sock dockeruser@pc:~$ ++++ Vérification que ça fonctionne bien dockeruser@pc:~$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES dockeruser@pc:~$ On va malgré tout ajouter la variable d'environnement DOCKER_HOST à ''.bashrc''. En effet certaines commandes tierces, tel ''docker-compose''((Maintenant que la commande compose est un plugin de docker, est-ce que ''docker-compose'' est toujours vraiment nécessaire ? En effet, on peut utiliser docker compose ...)), n'utilisent pas le contexte docker mais cette variable. echo . $HOME/.bashrc > $HOME/.profile echo export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock >> ~/.bashrc S'assurer que le service utilisateur est bien démarré automatiquement. systemctl --user enable docker Et voilà, c'est tout pour le compte dockeruser. ===== Utiliser ===== reboot Et après un redémarrage, on doit avoir le démon dockerd qui fonctionne. Sur une machine distante, ou sur la même machine avec un compte d'utilisateur ordinaire différent du compte dédié qu'on vient de créer, on peut créer un contexte docker et l'activer. L'activation est en fait un réglage de la commande docker. À un moment donné, pour un utilisateur donné, il ne peut y avoir qu'un seul contexte docker actif. On peut créer des centaines de contextes différents si besoin. docker context create \ --docker host=ssh://dockeruser@docker-pc \ --description "rootless docker on docker-pc" \ docker-pc docker context use docker-pc Ensuite, tout est possible. Voilà une session d'exemple. moi@monpc:~ (0) $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES moi@monpc:~ (0) $ docker run --rm busybox echo hello world! Unable to find image 'busybox:latest' locally latest: Pulling from library/busybox ec562eabd705: Pull complete Digest: sha256:9ae97d36d26566ff84e8893c64a6dc4fe8ca6d1144bf5b87b2b85a32def253c7 Status: Downloaded newer image for busybox:latest hello world! moi@monpc:~ (0) $ docker run --rm busybox echo hello world! hello world! moi@monpc:~ (0) $ docker run --name patience --rm busybox sleep infinity ^C context canceled moi@monpc:~ (1) $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 7c15a6d3b253 busybox "sleep infinity" 48 seconds ago Up 22 seconds patience moi@monpc:~ (0) $ : "Ah ben ça alors, le ^C n'a tué que la commande locale" moi@monpc:~ (0) $ : "Et la commande docker continue à fonctionner" moi@monpc:~ (0) $ docker stop patience patience moi@monpc:~ (0) $ : Voyons le réseau, maintenant. Détachons un conteneur moi@monpc:~ (0) $ : qui ne fait rien et allons voir. moi@monpc:~ (0) $ docker run --name patience --rm -d busybox sleep infinity af3adee0ae4485eaa50f1c5bcb486d58effa6df92528eb0e23aa85569bc3dda9 moi@monpc:~ (0) $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES af3adee0ae44 busybox "sleep infinity" 31 seconds ago Up 5 seconds patience moi@monpc:~ (0) $ docker exec -it patience sh / # ip a 1: lo: mtu 65536 qdisc noqueue qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 17: eth0@if18: mtu 1500 qdisc noqueue link/ether 02:42:0a:0e:00:02 brd ff:ff:ff:ff:ff:ff inet 10.14.0.2/24 brd 10.14.0.255 scope global eth0 valid_lft forever preferred_lft forever / # exit moi@monpc:~ (0) $ docker stop patience patience moi@monpc:~ (0) $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES moi@monpc:~ (0) $