====== 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) $