Setup Docker Host in rootless Mode
Docker-Host im "rootless" Modus einrichten.
Ausgangslage
Ich betreibe meine Docker Host als LXC Container auf Proxmox, was grundsätzlich gut funktioniert.
Ein “normaler” Benutzer kann docker Container ausführen, allerdings läuft Docker selbst immer noch als „root“.
Ich möchte einen „rootless“ Docker-Host aufbauen, um die Container noch besser vom Host zu isolieren.
Voraussetzungen
Als allererstes stelle ich eine neue VM mit Debian 12 bereit. Es wäre schön gewesen, docker rootless im LXC zu betrieben, was aber schlicht nicht funktionierte.
Danach wird Docker auf der neuen VM installiert. Wie das geht, ist auf https://docs.docker.com/engine/install/debian/
dokumentiert und auch hier in einem separaten Post zu finden.
Bei der Einrichtung des „Docker rootless mode“ habe ich mich ebenfalls an die Docker Doku unter
https://docs.docker.com/engine/security/rootless/
gehalten.
Erster Schritt
Die Schritte sind der o.g. Quelle entnommen:
Auf dem Host werden newuidmap und newgidmap benötigt. Diese sind im uidmap Paket enthalten.
/etc/subuid und /etc/subgid sollten mindestens 65,536 subordinate UIDs/GIDs für den Benutzer beinhalten. Im folgenden Beispiel hat der benutzer testuser 65,536 subordinate UIDs/GIDs (231072-296607) zur Verfügung.
Befehle zur Prüfung und deren Ausgabe :
1
2
3
4
5
6
7
8
id -u
1001
whoami
testuser
grep ^$(whoami): /etc/subuid
testuser:231072:65536
grep ^$(whoami): /etc/subgid
testuser:231072:65536
Zweiter Schritt
Das dbus-user-session Packet muß - wenn nicht vorhanden - mit
1
apt-get install -y dbus-user-session
installiert werden. Das Paket fuse-overlayfs wird ebenfalls benötigt für Debian 11, war auf meinem Debian 12 jedoch nich terforderlich. Der rootless docker benötigt slirp4netns in der Version größer als v0.4.0 (wenn vpnkit nicht installiert ist). Überprüfen kann man das mit:
1
slirp4netns –-version
Wenn das nicht klappt ggf. mit
1
apt-get install -y slirp4netns
instalieren.
Danach wird der “normale” Docker Daemon (System) disablet:
1
systemctl disable --now docker.service docker.socket
1
rm /var/run/docker.sock
Für die weitere Installation wähle ich den Installationspfad mit DEB Paketen.
Wenn Docker 20.10 oder besser installiert ist, dann sollte die Datei dockerd-rootless-setuptool.sh im Verzeichnis /usr/bin existieren.
Um den Daemon einzurichten führt man
dockerd-rootless-setuptool.sh install als normaler Benutzer aus.
1
2
3
4
5
6
7
8
9
10
11
$ dockerd-rootless-setuptool.sh install
[INFO] Creating /home/testuser/.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 testuser`
[INFO] Make sure the following environment variables are set (or add them to ~/.bashrc):
export PATH=/usr/bin:$PATH
export DOCKER_HOST=unix:///run/user/1000/docker.sock
Wenn die Datei dockerd-rootless-setuptool.sh nicht vorhanden ist, muß das Paket docker-ce-rootless-extras manuell installiert werden.
1
apt-get install -y docker-ce-rootless-extras
Mögliche Fehler bei der Einrichtung werden in Bereich “Troubleshooting” von https://docs.docker.com/engine/security/rootless/ behandelt.
Deinstallieren
Um den systemd Service wieder zu deinstallieren muß einfach
1
dockerd-rootless-setuptool.sh uninstall
ausgeführt werden. Das ganze Verfahren wird super im Bereich Uninstall von https://docs.docker.com/engine/security/rootless/ beschrieben.
Daemon Einrichtung
Das systemd unit file ist als ~/.config/systemd/user/docker.service installiert.
Mit dem Kommando systemctl --user kann man den Daemon steuern.
Mit
1
systemctl --user start docker
startet man einmalig. Um das Ganze auch während des System Starts für den Benutzer durchzuführen muß man folgende Kommandos durchführen:
1
2
systemctl --user enable docker
sudo loginctl enable-linger $(whoami)
Achtung: Der Rootless Docker Service lässt sich nicht als systemweiter Service einrichten.
Pfade
Noch ein paar Hinweise zu den Pfaden:
Der Socket Pfad liegt im Kontext des Users. Die Umgebungsvariable $XDG_RUNTIME_DIR zeigt dabei auf /run/user/1000 ($UID ist 1000 auf meinem Host).
Das Datenverzeichnis liegt per default unter
1
~/.local/share/docker
Die Docker-Daemon Config liegt unter
1
~/.config/docker/daemon.json
Falls man z.B. das Datenverzeichnis anpassen möchte, sollte hier die daemon.json liegen.
1
2
3
4
5
6
7
8
{
"data-root": "/data/docker",
"log-driver": "local",
"log-opts": {
"max-size": "15m",
"max-file": "5"
}
}
Die Client-Konfiguration liegt unter
1
~/.docker/config.json
In dieser config.json ist z.B. geregelt, das direkt der richtige docker context konfiguriert wird.
1
2
3
4
{
"auths": {},
"currentContext": "rootless"
}
Docker API via TCP freigeben
Um den Zugriff auf die Docker API freizugeben, muß man dockerd-rootless.sh mit dem Flag
DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS=”-p 0.0.0.0:2376:2376/tcp” starten.
1
2
3
4
5
$ DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS="-p 0.0.0.0:2376:2376/tcp" \\
[dockerd-rootless.sh](http://dockerd-rootless.sh) \\
-H tcp://0.0.0.0:2376 \\
--tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem
Hinsichtlich der Ports ist zu beachten, das 2375/tcp für unverschlüsselte und 2376/tcp für verschlüsselte Verbindungen vorgesehen ist.
Nun möchte man dockerd-rootless.sh nicht nur aus der Shell starten, sondern bitte direkt beim Start des Servers. Dazu geht man analog zum “rootfull / non-rootless” Server vor:
Variante 1
/etc/docker/daemon.json bzw.
~/.config/docker/daemon.json
anpassen / erweitern
Beispiel für eine daemon.json von meinem normalen root-docker:
1
2
3
4
5
6
7
8
9
10
11
12
13
{
"hosts": ["unix:///var/run/docker.sock", "tcp://192.168.111.111:2376"],
"tls": true,
"tlscacert": "/root/certs/ca.pem",
"tlscert": "/root/certs/server-cert.pem",
"tlskey": "/root/certs/server-key.pem",
"tlsverify": true,
"data-root": "/data/docker",
"log-driver": "local",
"log-opts": {
"max-size": "15m",
"max-file": "5"
}
Variante 2
/etc/systemd/system/docker.service.d/override.conf bzw.
~/.config/systemd/user/docker.service.d/override.conf
anpassen / erweitern
Beispiel für ein override.conf von meinem rootless-docker:
Im Verzeichnis ~/.config/systemd/user/docker.service.d/ liegt die override.conf. In dieser Datei steuere ich die Parameter, um den docker-socket via API freizugeben.
1
2
3
4
5
6
7
8
9
10
[Service]
Environment=DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS="-p 0.0.0.0:2376:2376/tcp"
ExecStart=
ExecStart=/usr/bin/dockerd-rootless.sh \
-H fd:// \
-H tcp://0.0.0.0:2376 \
--tlsverify \
--tlscacert=/home/myuser/.config/cert/my-ca.pem \
--tlscert=/home/myuser/.config/cert/server-cert.pem \
--tlskey=/home/myuser/.config/cert/server-key.pem
Schlussendlich habe bei allen Docker-Hosts konsistent die daemon.json-Variante verwendet. Wichtig ist bei beiden Varianten, das man sowohl den API-Port als auch den Socket anspricht bzw. startet.
Wer sich fragt, warum man die Docker API freigibt, der möge sich gern den Beitrag zu Portainer anschauen.
Ausblick: Eine Portainer Instanz kann mehrere Host verwalten