Post

Setup Docker Host in rootless Mode

Docker-Host im "rootless" Modus einrichten.

Setup Docker Host in rootless Mode

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

This post is licensed under CC BY 4.0 by the author.