Tiefgreifendes Verständnis von UID und GID in Docker-Containern

Tiefgreifendes Verständnis von UID und GID in Docker-Containern

Standardmäßig werden Prozesse im Container mit Root-Benutzerberechtigungen ausgeführt und dieser Root-Benutzer ist derselbe Benutzer wie der Root-Benutzer auf dem Hostcomputer. Das klingt beängstigend, denn es bedeutet, dass der Prozess im Container, sobald er die richtige Gelegenheit dazu hat, alles auf dem Host-Computer steuern kann! In diesem Artikel versuchen wir zu verstehen, wie Benutzernamen, Gruppennamen, Benutzer-IDs (UIDs) und Gruppen-IDs (GIDs) zwischen Prozessen innerhalb eines Containers und dem Hostsystem zugeordnet werden, was für die Sicherheit des Systems sehr wichtig ist. Hinweis: Die Demonstrationsumgebung dieses Artikels ist Ubuntu 16.04 (das Bild unten stammt aus dem Internet).

Lassen Sie uns zunächst UID und GID verstehen

Der Linux-Kernel verwaltet UID und GID und verwendet Systemaufrufe auf Kernelebene, um zu entscheiden, ob einer Anforderung Berechtigungen erteilt werden sollen. Wenn ein Prozess beispielsweise versucht, in eine Datei zu schreiben, überprüft der Kernel die UID und GID des erstellenden Prozesses, um festzustellen, ob dieser über ausreichende Berechtigungen zum Ändern der Datei verfügt. Beachten Sie, dass der Kernel UID und GID verwendet, nicht Benutzernamen und Gruppennamen.

Der Einfachheit halber wird im Rest dieses Artikels uid nur als Beispiel verwendet. Das System behandelt gid grundsätzlich genauso wie uid.

Viele Studenten verstehen Docker-Container einfach als leichte virtuelle Maschinen. Dies vereinfacht zwar das Verständnis der Containertechnologie, kann aber auch leicht zu vielen Missverständnissen führen. Tatsächlich ist es anders als bei der virtuellen Maschinentechnologie: Alle Container, die auf demselben Host laufen, teilen sich denselben Kernel (den Kernel des Hosts). Der große Vorteil der Containerisierung besteht darin, dass alle diese unabhängigen Container (eigentlich Prozesse) einen Kernel gemeinsam nutzen können. Dies bedeutet, dass selbst wenn Hunderte oder Tausende von Containern auf dem Docker-Host ausgeführt werden, immer noch nur ein vom Kernel gesteuerter UID- und GID-Satz vorhanden ist . Daher repräsentiert die gleiche UID den gleichen Benutzer im Host und im Container (auch wenn an verschiedenen Stellen unterschiedliche Benutzernamen angezeigt werden).

Beachten Sie, dass die normalen Linux-Tools zum Anzeigen von Benutzernamen nicht Teil des Kernels sind (z. B. Befehle wie „id“). Daher wird in verschiedenen Containern möglicherweise dieselbe UID als unterschiedlicher Benutzername angezeigt. Sie können für dieselbe UID jedoch nicht unterschiedliche Berechtigungen haben, auch nicht in unterschiedlichen Containern.

Wenn Sie die Technologie des Linux-Benutzer-Namespaces bereits kennen, lesen Sie „Linux-Namespace: Benutzer“. Sie müssen beachten, dass Docker den Benutzernamensraum bisher nicht standardmäßig aktiviert, was auch der in diesem Artikel beschriebene Fall ist. Im nächsten Artikel stellt der Autor vor, wie Docker konfiguriert wird, um den Benutzer-Namespace zu aktivieren.

Der Standardbenutzer im Container ist root.

Werden keine entsprechenden Einstellungen vorgenommen, wird der Prozess im Container standardmäßig mit Root-Benutzerrechten gestartet. Die folgende Demo verwendet das Ubuntu-Image, um das Sleep-Programm auszuführen:

$ docker run -d --name sleepme ubuntu schlaf unendlich

Beachten Sie, dass sudo im obigen Befehl nicht verwendet wird. Der Login-Benutzer des Autors auf dem Host-Computer ist Nick und die UID ist 1000:

Zeigen Sie die Informationen zum Ruhezustand im Host an:

$ ps aux | grep sleep

Der effektive Benutzername des Sleep-Prozesses ist root, was bedeutet, dass der Sleep-Prozess über Root-Berechtigungen verfügt.

Geben Sie dann den Container ein und sehen Sie, dass die Situation dieselbe ist wie zuvor. Der Ruheprozess verfügt ebenfalls über Root-Berechtigungen:

Ist also der Root-Benutzer im Container derselbe wie der Root-Benutzer auf dem Host?

Die Antwort lautet: Ja, sie entsprechen derselben UID. Den Grund haben wir bereits erklärt: Das gesamte System nutzt denselben Kernel und der Kernel verwaltet nur einen UID- und GID-Satz.

Tatsächlich können wir die obige Schlussfolgerung einfach anhand der Datenmengen überprüfen. Erstellen Sie auf dem Hostcomputer eine Datei, die nur der Root-Benutzer lesen und schreiben kann:

Dann montieren Sie es in den Container:

$ docker run --rm -it -w=/testv -v $(pwd)/testv:/testv ubuntu

Die Datei kann im Container gelesen und geschrieben werden:

Wir können die Benutzeridentität des Prozesses im Container über den USER-Befehl im Dockerfile oder den Parameter --user des Docker-Run-Befehls angeben. Lassen Sie uns diese beiden Situationen separat untersuchen.

Angeben der Benutzeridentität im Dockerfile

Wir können im Dockerfile einen Benutzer appuser hinzufügen und mit dem Befehl USER angeben, dass das Programm als dieser Benutzer ausgeführt wird. Der Inhalt des Dockerfiles ist wie folgt:

VON Ubuntu
RUN useradd -r -u 1000 -g appuser
BENUTZER appuser
EINSTIEGSPUNKT ["Schlaf", "Unendlichkeit"]

Kompilieren Sie es in ein Image mit dem Namen „Test“:

$ docker build -t test. 

Starten Sie einen Container mit dem Testimage:

$ docker run -d --name sleepme test

Zeigen Sie die Informationen zum Ruhezustand im Host an:

Dieses Mal wird als tatsächlicher Benutzer Nick angezeigt. Dies liegt daran, dass auf dem Hostcomputer der Name des Benutzers mit der UID 1000 Nick ist. Dann betrete den Container und schau nach:

$ docker exec -it schlafe mich bash 

Der aktuelle Benutzer im Container ist der von uns festgelegte Appuser. Wenn Sie die Datei /etc/passwd im Container überprüfen, werden Sie feststellen, dass die UID des Appusers 1000 ist, was mit der UID des Benutzers Nick auf dem Hostcomputer identisch ist.

Erstellen wir eine weitere Datei, die nur der Benutzer Nick lesen und schreiben kann:

Hängen Sie es außerdem als Datenträger in den Container ein:

$ docker run -d --name sleepme -w=/testv -v $(pwd)/testv:/testv test 

Im Container wird der Besitzer der Testdatei zum App-Benutzer, und natürlich hat der App-Benutzer die Berechtigung, die Datei zu lesen und zu schreiben.

Was genau ist hier passiert? Und was bedeutet das?

Erstens gibt es im Hostsystem einen Benutzer-Nick mit der UID 1000. Zweitens läuft das Programm im Container als Appuser, der von uns im Dockerfile-Programm durch den Befehl USER appuser angegeben wird.

Tatsächlich gibt es nur eine vom Systemkernel verwaltete UID 1000. Auf dem Hostcomputer wird dies als der Benutzer „Nick“ betrachtet, und im Container wird dies als der Benutzer „Appuser“ betrachtet.
Daher muss uns eines klar sein: Innerhalb des Containers kann der Benutzer „appuser“ die Rechte und Privilegien des Benutzers „nick“ außerhalb des Containers erhalten. Dem Benutzer „Nick“ oder „UID 1000“ auf dem Host gewährte Berechtigungen werden auch dem App-Benutzer innerhalb des Containers gewährt.

Passen Sie die Benutzeridentität über Befehlszeilenargumente an

Wir können die Benutzeridentität des Prozesses im Container auch über den Parameter --user des Befehls „Docker Run“ angeben. Führen Sie beispielsweise den folgenden Befehl aus:

$ docker run -d --Benutzer 1000 --Name sleepme Ubuntu Schlaf unendlich

Da wir in der Befehlszeile den Parameter --user 1000 angegeben haben, wird der effektive Benutzer des Sleep-Prozesses als Nick angezeigt. Betreten Sie den Container und schauen Sie nach:

$ docker exec -it schlafe mich bash 

Was ist los? Der Benutzername wird als „Ich habe keinen Namen!“ angezeigt! Überprüfen Sie die Datei /etc/passwd. Es gibt tatsächlich keinen Benutzer mit der UID 1000. Auch ohne Benutzernamen hat dies keinerlei Auswirkungen auf die Berechtigungen der Benutzeridentität. Es können weiterhin Dateien gelesen und geschrieben werden, die nur Nick-Benutzer lesen und schreiben können, und die Benutzerinformationen werden durch die Benutzerkennung anstelle des Benutzernamens ersetzt:

Es ist zu beachten, dass die beim Erstellen des Containers durch docker run --user angegebene Benutzeridentität den im Dockerfile angegebenen Wert überschreibt.
Wir führen erneut zwei Container mit dem Testimage aus:

$ docker run -d test

Informationen zum Schlafvorgang anzeigen:

$ docker run --user 0 -d test

Überprüfen Sie die Informationen zum Ruhezustand erneut:

Der Prozess, der den Parameter --urser 0 angibt, zeigt, dass der effektive Benutzer root ist, und weist darauf hin, dass der Befehlszeilenparameter --user 0 die Einstellung des USER-Befehls im Dockerfile überschreibt.

Zusammenfassen

Aus den Beispielen in diesem Artikel können wir ersehen, dass der im Container ausgeführte Prozess auch die Berechtigung hat, auf die Hostressourcen zuzugreifen (Docker isoliert Benutzer standardmäßig nicht). Natürlich blockiert die Containertechnologie im Allgemeinen die sichtbaren Ressourcen des Prozesses im Container im Container. Wie wir jedoch bei den Vorgängen an Dateien im Datenvolumen gezeigt haben, können wir sehen, dass, sobald der Prozess im Container die Möglichkeit hat, auf die Ressourcen des Hostcomputers zuzugreifen, seine Berechtigungen dieselben sind wie die des Benutzers auf dem Hostcomputer. Daher ist es sicherer, einen Benutzer mit entsprechenden Berechtigungen für den Prozess im Container anzugeben, anstatt den Standard-Root-Benutzer zu verwenden. Natürlich gibt es eine bessere Lösung, nämlich die Verwendung der Benutzer-Namespace-Technologie von Linux, um Benutzer zu isolieren. Im folgenden Artikel werde ich vorstellen, wie Docker konfiguriert wird, um die Unterstützung von Benutzer-Namespaces zu aktivieren.

siehe:

Verstehen, wie uid und gid in Docker-Containern funktionieren
Einführung in Benutzer-Namespaces in der Docker Engine
Isolieren Sie Container mit einem Benutzernamensraum

Das Obige ist der vollständige Inhalt dieses Artikels. Ich hoffe, er wird für jedermanns Studium hilfreich sein. Ich hoffe auch, dass jeder 123WORDPRESS.COM unterstützen wird.

<<:  So verwenden Sie Node-Scaffolding zum Erstellen eines Servers zur Implementierung der Token-Verifizierung

>>:  Detaillierte Erläuterung der MySQL-Datumszeichenfolgen-Zeitstempelkonvertierung

Artikel empfehlen

Vue implementiert eine einfache Notizblockfunktion

In diesem Artikelbeispiel wird der spezifische Co...

Der Unterschied und die Verwendung von LocalStorage und SessionStorage in Vue

Inhaltsverzeichnis Was ist LocalStorage Was ist S...

So überprüfen Sie die PCIe-Version und -Geschwindigkeit unter Linux

PCIE verfügt über vier verschiedene Spezifikation...

Zusammenfassung zur Positionierung in CSS

Es gibt vier Arten der Positionierung in CSS, die...

So finden und löschen Sie doppelte Zeilen in MySQL

Inhaltsverzeichnis 1. So finden Sie doppelte Zeil...

So implementieren Sie adaptive Container mit gleichem Seitenverhältnis mit CSS

Als ich kürzlich eine mobile Seite entwickelte, s...

Linux-Unlink-Funktion und wie man Dateien löscht

1. Unlink-Funktion Bei Hardlinks wird mit „unlink...

Datagrip2020 kann MySQL-Treiber nicht herunterladen

Wenn Sie es nicht durch direktes Klicken auf „Dow...

Schritte zur Bereitstellungsmethode für Docker Stack für Webcluster

Docker wird immer ausgereifter und seine Funktion...

Zusammenfassung der 10 wichtigsten JavaScript-Interviewfragen (empfohlen)

1.Dies deutet auf 1. Wer ruft wen an? Beispiel: F...