In der Standardkonfiguration ist es einem Docker-User ein leichtes, sich über einen Container root-Rechte auf dem Hostsystem zu verschaffen.
Um das zu vermeiden gibt es zwei Möglichkeiten, rootless mode und userns-remap. Die erste Variante benötigt nicht einmal root-Rechte um Docker zu installieren, es wird alles im Homeverzeichnis des Users installiert. Das macht es zwar relativ einfach, aber leider ungeeignet für größere Umgebungen wo eine beliebige Anzahl von Usern, die idealerweise noch aus einem Verzeichnisdienst kommen, den Docker-Dienst nutzen können soll.
Deutlich interessant ist hier userns-remap. Dabei läuft der Docker-Daemon weiterhin als root, die Container werden jedoch im Namespace des startenden Users angelegt. Das hat natürlich ein paar Einschränkungen, die ich hier nicht nochmal aufzählen möchte, die Dokumentation tut das schon ganz gut.
Um userns-remap einzurichten wird lediglich zusätzlich das Paket uidmap benötigt.
sudo apt-get install uidmap
Dann muss der Docker-Daemon konfiguriert werden.
sudo vi /etc/docker/daemon.json
{
"userns-remap": "default"
}
Einen Haken hat die Sache noch: Ein User kann immer noch einen Container im Host-Namespace erstellen indem er an sein docker run-Kommando den Parameter –userns=host anhängt. Den kann man jedoch recht einfach unterbinden, es hat schon jemand ein auth-Plugin für genau diesen Zweck geschrieben.
Leider gibt es das Plugin nicht als Binary oder Paket, man muss es sich selbst kompilieren. Das ist aber in einem Container kein Problem:
docker pull golang
git clone https://github.com/dandare100/docker-denyusernshost
cd docker-denyusernshost/
docker run --rm -it -v "$PWD":/usr/src/myapp -w /usr/src/myapp golang bash
go mod init
go mod vendor
go build -o denyusernshost
Danach hat man im Quellcodeverzeichnis das kompilierte binary denyusernshost liegen. Die Datei kopiert man an eine geeignete Stelle, zum Beispiel /usr/local/sbin/. Dann noch ein systemd unit-File dafür angelegt:
sudo vi /etc/systemd/system/denyusernshost.service
[Unit] Description=Docker denyusernshost Documentation=https://github.com/dandare100/docker-denyusernshost After=network.target [Service] Type=simple ExecStart=/usr/local/sbin/denyusernshost Restart=on-failure [Install] WantedBy=multi-user.target
Systemd neu laden und den Dienst starten
sudo systemctl daemon-reload sudo systemctl enable denyusernshost sudo systemctl start denyusernshost
Jetzt noch das Plugin beim Docker-Daemon eintragen:
sudo vi /etc/docker/daemon.json
{ "userns-remap": "default", "authorization-plugins": ["denyusernshost"] }
Zu guter Letzt Docker neu starten damit die Konfiguration aktiv wird
sudo systemctl restart docker
Und schon können nur noch unprivilegierte Container ohne root-Rechte gestartet werden. Versucht ein User das zu umgehen bekommt er eine entsprechende Fehlermeldung, und der Versuch wird protokolliert.
ini-sc@slurmtest01:~$ docker run --userns=host --rm -it ubuntu bash docker: Error response from daemon: authorization denied by plugin denyusernshost: userns=host is not allowed.
Um nicht jeden User der docker-Gruppe hinzufügen zu müssen (was gar nicht praktikabel ist, wenn die User aus dem Verzeichnisdienst kommen) kann man den Zugriff über ACL auf dem Docker-Socket regeln.
setfacl -m g:'domain users':rw /var/run/docker.sock
Die Einstellung ist allerdings nach dem nächsten Neustart des Hosts weg, da /var/run ein dynamisches Dateisystem ist und der Socket beim Start des Daemons erzeugt wird. Das können wir aber ebenfalls mit einer systemd-Unit machen.
sudo vi /etc/systemd/system/docker-setfacl.service
[Unit] Description=Set ACL for docker access After=docker.service [Service] Type=oneshot ExecStart=/usr/bin/setfacl -m g:'domain users':rw /var/run/docker.sock RemainAfterExit=true StandardOutput=journal [Install] WantedBy=multi-user.target
Auch hier wieder systemd neu laden und starten
sudo systemctl daemon-reload sudo systemctl enable docker-setfacl sudo systemctl start docker-setfacl
Damit wird die ACL nach dem Neustart neu gesetzt nachdem der Docker-Daemon gestartet wurde.