Warum Prozesse in Docker-Containern nicht als Root ausgeführt werden sollten
Veröffentlicht: 2022-08-19 Prozesse in einem Docker-Container sollten nicht als Root ausgeführt werden. Es ist sicherer, Ihre Anwendungen als Nicht-Root-Benutzer auszuführen, den Sie als Teil Ihrer Dockerfile oder bei Verwendung von docker docker run
angeben. Dies minimiert das Risiko, indem es allen Bedrohungen in Ihrem Container eine reduzierte Angriffsfläche bietet.
In diesem Artikel erfahren Sie mehr über die Gefahren beim Ausführen von containerisierten Anwendungen als Root. Sie werden auch sehen, wie Sie einen Nicht-Root-Benutzer erstellen und Namensräume in Situationen einrichten, in denen dies nicht möglich ist.
Warum ist das Ausführen als Root gefährlich?
Container werden standardmäßig als Root ausgeführt. Der Docker-Daemon wird auf Ihrem Host als Root ausgeführt, und laufende Container werden ebenfalls als Root ausgeführt.
Obwohl es den Anschein haben kann, dass root im Container ein unabhängiger Benutzer ist, ist es tatsächlich dasselbe wie das root-Konto auf Ihrem Host. Die Trennung wird nur durch die Container-Isolationsmechanismen von Docker bereitgestellt. Es gibt keine starke physische Grenze; ein anderer Prozess Ihres Containers, der vom Root-Benutzer auf dem Kernel Ihres Hosts ausgeführt wird. Dies bedeutet, dass eine Schwachstelle in Ihrer Anwendung, der Docker-Laufzeit oder dem Linux-Kernel es Angreifern ermöglichen könnte, aus dem Container auszubrechen und root-privilegierte Operationen auf Ihrem Computer auszuführen.
Es gibt einige eingebaute Schutzmaßnahmen, die das Risiko verringern, dass dies geschieht. Root innerhalb des Containers ist nicht privilegiert und hat eingeschränkte Fähigkeiten. Dadurch wird verhindert, dass der Container Systemverwaltungsbefehle verwendet, es sei denn, Sie fügen manuell Funktionen hinzu oder verwenden den privilegierten Modus, wenn Sie Ihre Container starten.
Trotz dieser Abschwächung bleibt es ein Risiko, Anwendungen als Root ausführen zu lassen. Genauso wie Sie die Verwendung von root in einer traditionellen Umgebung einschränken würden, ist es unklug, es unnötigerweise in Ihren Containern zu verwenden. Sie stellen eine überprivilegierte Umgebung bereit, die Angreifern mehr Halt gibt, falls es zu einer Sicherheitsverletzung kommt.
Containerisierte Anwendungen als Nicht-Root-Benutzer ausführen
Es hat sich bewährt, containerisierte Anwendungen als normaler Benutzer auszuführen. Die meiste Software benötigt keinen Root-Zugriff, sodass das Ändern des Benutzers eine sofortige Verteidigungsebene gegen Container-Breakout darstellt.
Sie sollten als eine der letzten Phasen in Ihrem Dockerfile ein neues Benutzerkonto erstellen. Dies erreichen Sie mit der USER
Anweisung:
FROM base-image:neueste RUN apt install demo-package BENUTZER Demo-Benutzer: Demo-Gruppe ENTRYPOINT ["demo-binary"]
Von diesem Image gestartete Container werden als demo-user
ausgeführt. Der Benutzer wird Mitglied der demo-group
Gruppengruppe. Sie können den Gruppennamen weglassen, wenn der Benutzer nicht in einer Gruppe sein muss:
USER Demo-Benutzer
Sie können anstelle von Namen eine Benutzer-ID (UID) und eine Gruppen-ID (GID) angeben:
BENUTZER 950:950
Die Vergabe einer bekannten UID und GID ist in der Regel die sicherste Vorgehensweise. Es verhindert, dass der Benutzer im Container einem überprivilegierten Hostkonto zugeordnet wird.
USER
wird oft als vorletzte Stufe in einem Dockerfile angegeben. Das bedeutet, dass Sie weiterhin Vorgänge ausführen können, für die root früher in der Image-Erstellung erforderlich ist. Die apt install
-Anweisung im obigen Beispiel benötigt root. Wenn die USER
-Anweisung darüber platziert würde, würde apt
als demo-user
ausgeführt, dem die erforderlichen Berechtigungen fehlen würden. Da die Dockerfile-Anweisungen nur für Image-Builds gelten und nicht für das Ausführen von Containern, ist es sicher, den Benutzer später in Ihrem Dockerfile zu ändern.
Wenn Sie den Benutzer ändern, unter dem Ihr Container ausgeführt wird, müssen Sie möglicherweise die Berechtigungen für die Dateien und Ordner aktualisieren, auf die er zugreift. Legen Sie den Besitz für alle Pfade fest, die von Ihrer Anwendung verwendet werden:
COPY initial-config.yaml /data/config.yaml BENUTZER Demo-Benutzer: Demo-Gruppe RUN chown demo-user:demo-group /data
In diesem Beispiel muss das Verzeichnis /data
dem demo-user
gehören, damit die Anwendung Änderungen an ihrer Konfigurationsdatei vornehmen kann. Die frühere COPY
-Anweisung hat die Datei als root hineinkopiert. Eine Abkürzung ist verfügbar, indem das Flag --chown
mit copy
verwendet wird:
COPY --chown=demo-user:demo-group initial-config.yaml /data/config.yaml
Ändern des Benutzers beim Starten eines Containers
Während Sie den Benutzer in Ihren eigenen Dockerfiles problemlos ändern können, laufen viele Anwendungen von Drittanbietern weiterhin als Root. Sie können das damit verbundene Risiko verringern, indem Sie bei jedem Aufruf von --user
docker run
Flag --user setzen. Dies überschreibt den Benutzersatz in der Dockerfile des Images.
$ docker run -d --user demo-user:demo-group demo-image:latest $ docker run -d --user demo-user demo-image:latest $ docker run -d --user 950:950 demo-image:latest
Das Flag --user
führt den Prozess des Containers als der angegebene Benutzer aus. Es ist weniger sicher als die Dockerfile USER
-Anweisung, da Sie es einzeln auf jeden docker run
-Befehl anwenden müssen. Eine bessere Option für regelmäßig verwendete Bilder ist es, ein eigenes abgeleitetes Bild zu erstellen, das ein neues Benutzerkonto festlegen kann:
FROM image-das-als-root-läuft:neueste USER Demo-Benutzer
$ Docker-Build. -t Bild-das-jetzt-als-nicht-root-läuft:neueste
Das Ändern des Benutzers eines Drittanbieter-Images kann zu Problemen führen: Wenn der Container voraussichtlich als Root ausgeführt wird oder auf Dateisystempfade zugreifen muss, die Root gehören, werden bei der Verwendung der Anwendung Fehler angezeigt. Sie können versuchen, die Berechtigungen für die Pfade, die Probleme verursachen, manuell zu ändern. Überprüfen Sie alternativ, ob der Anbieter eine unterstützte Methode zum Ausführen der Anwendung mit einem nicht privilegierten Benutzerkonto anbietet.
Umgang mit Anwendungen, die als Root ausgeführt werden müssen
Benutzer-Namensräume sind eine Technik für den Umgang mit Anwendungen, die einige Root-Rechte benötigen. Damit können Sie root in einem Container einem Nicht-Root-Benutzer auf Ihrem Host zuordnen. Der simulierte Root innerhalb des Containers verfügt über die erforderlichen Berechtigungen, aber ein Breakout bietet keinen Root-Zugriff auf den Host.
Die Namespace-Neuzuordnung wird aktiviert, indem Sie ein userns-remap
Feld zu Ihrer /etc/docker/daemon.json
:
{ "users-remap": "default" }
Die Verwendung von default
als Wert für userns-remap
weist Docker an, automatisch einen neuen Benutzer namens dockremap
auf Ihrem Host zu erstellen. Root innerhalb von Containern wird dockremap
auf Ihrem Host zugeordnet. Sie können stattdessen optional einen vorhandenen Benutzer und eine vorhandene Gruppe angeben, indem Sie eine Kombination aus UID/GID oder Benutzername/Gruppenname verwenden:
{ "users-remap": "demo-user" }
Starten Sie den Docker-Daemon neu, nachdem Sie Ihre Änderung übernommen haben:
$ sudo service docker neustarten
Wenn Sie nsuser-remap: default
verwenden, sollte der dockremap
-Benutzer jetzt auf Ihrem Host vorhanden sein:
$id dockremap uid=140(Dockremap) gid=119(Dockremap) groups=119(Dockremap)
Der Benutzer sollte auch in den untergeordneten ID-Dateien /etc/subuid
und /etc/subgid
erscheinen:
$ dockremap:231500:65535
Dem Benutzer wurde ein Bereich von 65.535 untergeordneten IDs beginnend mit 231500 zugewiesen. Innerhalb des Benutzernamensraums wird die ID 231500
auf 0
abgebildet, wodurch sie zum Root-Benutzer in Ihren Containern wird. Als UID mit hoher Nummer hat 231500 keine Privilegien auf dem Host, sodass Container-Breakout-Angriffe nicht so viel Schaden anrichten können.
Alle Container, die Sie starten, werden unter Verwendung des neu zugeordneten Benutzernamensraums ausgeführt, sofern Sie sich nicht mit docker run --userns=host
. Der Mechanismus funktioniert durch Erstellen von Namespace-Verzeichnissen in /var/lib/docker
, die der untergeordneten UID und GID des Namespace-Benutzers gehören:
$ sudo ls -l /var/lib/docker/231500.231500 insgesamt 14 drwx------ 5 231500 231500 13 Jul 22 19:00 aufs drwx------ 3 231500 231500 13. Juli 22 19:00 Container ...
Benutzer-Namensräume sind eine effektive Möglichkeit, die Container-Isolierung zu erhöhen, Ausbrüche zu vermeiden und die Kompatibilität mit Anwendungen zu wahren, die Root-Berechtigungen benötigen. Es gibt jedoch einige Kompromisse: Die Funktion funktioniert am besten auf einer neuen Docker-Instanz, Volumes, die vom Host gemountet werden, müssen ihre Berechtigungen anpassen, und einige externe Speichertreiber unterstützen überhaupt keine Benutzerzuordnung. Sie sollten die Dokumentation lesen, bevor Sie diese Option übernehmen.
Zusammenfassung
Das Ausführen containerisierter Anwendungen als Root ist ein Sicherheitsrisiko. Obwohl leicht zu übersehen, ist die durch Container bereitgestellte Isolierung nicht stark genug, um Kernel-Benutzer vollständig von Container-Benutzern zu trennen. Root im Container ist dasselbe wie Root auf Ihrem Host, sodass eine erfolgreiche Kompromittierung die Kontrolle über Ihren Computer ermöglichen könnte.
Als Bildautor sollten Sie die USER
-Anweisung in Ihr Dockerfile aufnehmen, damit Ihre Anwendung ohne Root ausgeführt wird. Image-Benutzer können dies mit docker run --user
, um eine bestimmte UID und GID zuzuweisen. Dies trägt dazu bei, Fälle zu entschärfen, in denen das Image normalerweise root verwendet.
Sie können die Sicherheit weiter erhöhen, indem Sie mit --cap-drop=ALL
alle Funktionen aus dem Container löschen und dann die erforderlichen Funktionen mit --cap-add
auf die Whitelist setzen. Durch die Kombination dieser Techniken wird Ihre Anwendung als Nicht-Root-Benutzer mit den erforderlichen Mindestberechtigungen ausgeführt, wodurch Ihre Sicherheitslage verbessert wird.