Warum Prozesse in Docker-Containern nicht als Root ausgeführt werden sollten

Veröffentlicht: 2022-08-19

Grafik mit dem Docker-Logo

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.