GNU/Linux >> LINUX-Kenntnisse >  >> Linux

So debuggen Sie Probleme mit Volumes, die in Rootless-Containern gemountet sind

Eine der häufigsten Fragen, die mir zu Podman ohne Root gestellt werden, ist, wie man Probleme mit im Container gemounteten Volumes debuggt. Diese Frage ist täuschend schwer. In vielerlei Hinsicht läuft Podman ohne root ist fast identisch mit der Ausführung als root . Leider ist dies nicht immer der Fall, und Lautstärken sind einer der Bereiche mit den größten Unterschieden. Hier erkläre ich im Detail, was diese Unterschiede sind, welche Arten von Fehlern sie verursachen können und wie Sie sie umgehen können. Zu Beginn benötigen wir einige Hintergrundinformationen darüber, wie Container ohne Root funktionieren, beginnend mit einer der grundlegendsten Funktionen von Podman ohne Root:Benutzer-Namespaces .

Der Benutzernamensraum

Eine der grundlegenden Sicherheitsfunktionen von Containern sind Linux-Kernel-Namespaces. Ein Namensraum ist eine Möglichkeit, einen Prozess (oder eine Gruppe von Prozessen) vom Rest des Systems zu isolieren, indem er begrenzt, was er sehen kann. Es gibt viele verschiedene Namensräume, jeder mit einer anderen Wirkung. Beispielsweise begrenzt der PID-Namespace, welche PIDs ein Prozess anzeigen kann – nur PIDs innerhalb des Namespace sind sichtbar und werden unabhängig vom Host nummeriert. Der Prozess hat auch noch eine PID auf dem Host, also könnte PID 2000 auf dem Host PID 1 im Namespace sein. Zum jetzigen Zeitpunkt sind die Namespaces, die der Kernel bereitstellt, Mount, PID, Network, IPC, UTS, User, cgroup und time, die jeweils einen anderen Aspekt des Systems isolieren; Was uns in diesem Blog am meisten am Herzen liegt, ist der Benutzernamensraum.

Benutzernamensräume isolieren die im Container verfügbaren Benutzer und Gruppen von denen, die für das Hostsystem verfügbar sind. Ein Benutzernamensraum funktioniert, indem Benutzer im Container Benutzern auf dem Host zugeordnet werden. Beispielsweise könnten wir die Benutzer 0 bis 1000 im Container den Benutzern 100000 bis 101000 auf dem Host zuordnen (Gruppen werden ebenfalls auf identische Weise zugeordnet, aber wir konzentrieren uns der Einfachheit halber auf Benutzer). Diese Zuordnung verhält sich sehr ähnlich wie der oben beschriebene PID-Namespace, jedoch mit Benutzern.

Vom Host alle Zugriffe von root im Container (UID 0) scheint von UID 100000 zu sein. Innerhalb des Containers wird jede Datei, die Benutzer 100000 auf dem Host gehört, als Eigentum von UID 0 (root) erscheinen ). Eine interessante Frage ist, was mit Benutzern passiert, die nicht dem Container zugeordnet sind – was passiert, wenn ich ein Volume, das dem Benutzer 1001 gehört, auf dem Host in einen Container mounte, der den von mir beschriebenen Benutzernamensraum verwendet? Der Kernel ordnet in diesem Fall jede UID oder GID, die im Namespace nicht gültig ist, UID/GID 65534 zu, einem Benutzer und einer Gruppe namens nobody , was keine normale Gruppe ist.

Es ist immer noch möglich, mit Dateien zu interagieren, die niemand gehören wenn die Berechtigungen es zulassen (z. B. können Sie eine Datei lesen, die niemand gehört die weltweit lesbar ist, und schreiben Sie in diese Datei, wenn sie weltweit schreibbar ist), aber Sie können den Besitz nicht ändern. Benutzernamensräume gewähren auch eingeschränkte Versionen bestimmter Fähigkeiten, die normalerweise nur root zur Verfügung stehen – Das typische Beispiel ist, dass Benutzernamensräume bestimmte Arten von Dateisystemen wie tmpfs einhängen können.

Benutzernamensräume sind äußerst nützlich, da sie es uns ermöglichen, als Root zu agieren innerhalb des Containers, ohne tatsächlich root zu sein auf dem System. Wir können Benutzernamensräume verwenden, um Container von verschiedenen Benutzern auf Multi-Tenant-Systemen zu trennen – die Container eines Benutzers würden als UIDs 10000 bis 10999 ausgeführt, die eines anderen als 11000 bis 11999 und so weiter. In jedem Container sieht es so aus, als wäre die Anwendung root , und durch die gewährten begrenzten Fähigkeiten kann es die meisten gängigen Operationen ausführen (z. B. Pakete installieren).

Angenommen, eine Anwendung schafft es, aus dem Container auszubrechen. In diesem Fall wird es nicht als root ausgeführt auf dem System und läuft nicht mit der gleichen UID und GID wie die Container anderer Benutzer – die Möglichkeit, verschiedene Teile des Systems anzugreifen, ist äußerst begrenzt.

Ihre Akzeptanz war jedoch durch technische Einschränkungen begrenzt – vor allem durch die Tatsache, dass es bis vor kurzem keine Möglichkeit gab, UIDs und GIDs auf Dateisystemebene neu zuzuordnen. Um einen Container mit den UIDs 10000 bis 10999 zu haben, mussten wir eine Kopie seines Images erstellen und dann chown jede UID in diesem Bild, indem 10000 zur bestehenden UID addiert wird. Dieses chown kann sehr langsam sein und (in vielen Dateisystemen) den benötigten Speicherplatz dramatisch erhöhen.

[ Erste Schritte mit Containern? Schauen Sie sich diesen kostenlosen Kurs an. Containerisierte Anwendungen bereitstellen:Eine technische Übersicht. ]

Rootless-Container

Wo Benutzernamensräume extrem nützlich und beliebt geworden sind, sind wurzellose Container. Ein Nicht-Root-Benutzer unter Linux hat Zugriff auf nur eine UID und GID (ihre eigenen). Container erwarten jedoch den Zugriff auf mehr als einen Benutzer und eine Gruppe – viele Dateien in Container-Images gehören nicht root , und Anwendungen werden häufig als Nicht-Root-Benutzer im Container ausgeführt, um die Trennung von Berechtigungen innerhalb des Containers durchzusetzen.

Für einige Umgebungen (High Performance Computing, HPC, eine bemerkenswerte) ist es akzeptabel (sogar wünschenswert), nur einen einzigen Benutzer und eine Gruppe im Container zu haben. Für die meisten anderen Umgebungen ist ein Benutzer und eine Gruppe eine starke Einschränkung für die Nützlichkeit des Containers. Wir können Benutzernamensräume verwenden, um diese zusätzlichen Benutzer und Gruppen zu gewinnen, die wir benötigen, um wie ein typischer Container zu agieren.

Wir benötigen jedoch erhöhte Berechtigungen, um diese zusätzlichen Benutzer und Gruppen zu erreichen. Dies ist die newuidmap und newgidmap ausführbare Dateien (und die /etc/subuid und /etc/subgid config-Dateien, die sie lesen) tun – sie gewähren uns Zugriff auf einen Block von Benutzern und Gruppen, die dann einem Benutzernamensraum zugeordnet werden, den rootlose Container verwenden können. Die Einschränkungen von Benutzer-Namespaces bezüglich der Dateisystemunterstützung gelten immer noch bis zu einem gewissen Grad, werden aber dadurch gemildert, dass nur ein einziger Satz von UIDs und GIDs für jeden Benutzer verwendet werden kann.

Bemerkenswert ist auch die Tatsache, dass der Kernel den chown automatisch handhabt Operation für uns, wenn wir das Bild innerhalb eines Benutzernamensraums entpacken. Die Benutzerverschiebung des Namensraums stellt sicher, dass die richtige UID bei der Erstellung zugewiesen wird, anstatt dass die Containerlaufzeit sie manuell festlegen muss.

Die zusätzlichen Fähigkeiten eines Benutzernamensraums sind auch für einige der Dinge unerlässlich, die Container ohne Root benötigen – ohne die Möglichkeit, FUSE- und tmpfs-Dateisysteme zu mounten, wären Container ohne Root viel eingeschränkter (bis zu dem Punkt, an dem sie fast unbrauchbar wären).

Benutzernamensräume in Podman

Nachdem wir nun verstanden haben, wie Benutzer-Namespaces im Allgemeinen funktionieren, wollen wir diskutieren, wie sie in rootless Podman implementiert werden.

Alle rootlosen Podman-Container werden in einem Benutzernamensraum ausgeführt, auch wenn der Benutzer nicht mehr als eine UID und GID zur Verfügung hat. Alle Container eines Benutzers teilen sich einen einzigen Benutzernamensraum, der durch den Rootless-Pause-Prozess offen gehalten wird. Namespaces werden normalerweise vom Kernel gekürzt, wenn sich keine Prozesse mehr darin befinden. Wenn Sie also einen Prozess in der Nähe halten, der nur schläft und nie beendet wird, wird der Benutzer-Namespace am Leben erhalten.

Das erste, was ein Podman-Prozess ohne Root tut, ist, dem Namespace des Rootless-Benutzers beizutreten (oder einen neuen Namespace zu erstellen und den Prozess anzuhalten, falls er noch nicht existiert). Als Teil der Erstellung des Benutzernamensraums führt Podman die newuidmap aus und newgidmap ausführbare Dateien, um zusätzliche UIDs und GIDs zuzuweisen, die dem Benutzer in /etc/subuid zugewiesen wurden und /etc/subgid (Der Standardbetrag, der bei der Benutzererstellung gewährt wird, beträgt jeweils 65536). Sie können die verfügbaren Benutzerzuordnungen in der Podman-Info anzeigen Befehl in den idMappings Feld:

mheon@podman-rhel8-test $ podman info
  idMappings:
    gidmap:
    - container_id: 0
      host_id: 1000
      size: 1
    - container_id: 1
      host_id: 100000
      size: 65536
    uidmap:
    - container_id: 0
      host_id: 1000
      size: 1
    - container_id: 1
      host_id: 100000
      size: 65536

Bitte beachten Sie, dass der Rootless-Benutzernamensraum standardmäßig nicht neu erstellt wird, wenn die Datei /etc/subuid und /etc/subgid Dateien werden geändert; Dies geschieht durch Ausführen von podman system migrate Befehl. Wenn Sie diese Dateien bearbeitet haben und Podman Ihre Änderungen nicht zu erkennen scheint, führen Sie diesen Befehl aus.

Alle Rootless-Podman-Befehle werden innerhalb des Rootless-Benutzernamensraums ausgeführt, der erstellt wurde, um sicherzustellen, dass wir die richtigen Benutzerzuordnungen und Berechtigungen haben. Sogar einfache Informationsbefehle wie podman info , erfordern den Rootless-Benutzernamensraum. Daher ist ein nicht funktionierender Benutzernamensraum ein Showstopper für wurzellosen Podman. Glücklicherweise kommt dies nicht oft vor. Wenn dies der Fall ist, stellen wir normalerweise fest, dass dies durch unzureichende Dateiberechtigungen auf der newuidmap verursacht wird und/oder newgidmap Binärdateien auf dem System (normalerweise fehlt eine Dateifunktion). Neuinstallation des Pakets, das sie enthält (genannt shadow-utils auf RHEL, CentOS und Fedora) wird dies normalerweise beheben.

Sobald der Rootless-Benutzernamensraum erstellt wurde, können wir mit der Ausführung von Containern beginnen. Die Verwendung von Volumes mit diesen Containern ist der erste Punkt, an dem die meisten Benutzer auf die praktischen Unterschiede zwischen root und rootless Podman stoßen werden. Der Benutzer führt einen Container mit einem darin gemounteten Volume aus und stellt sofort fest, dass der Container nicht auf Dateien im Volume zugreifen kann, obwohl alles scheinbar richtig eingestellt ist.

Sehen wir uns zum Beispiel einen einfachen Podman-Befehl an:

podman run --user 1000:1000 -v /home/mheon/data:/data:Z ubi8 sh

Dieser Befehl wird von meinem Benutzer mheon ausgeführt , wobei UID und GID auf 1000 festgelegt sind (derselbe Benutzer, zu dessen Verwendung der Container angewiesen wurde). Meinem Benutzer wurden 65536 UIDs und GIDs ab UID/GID 100000 über /etc/subuid zugewiesen und /etc/subgid . Der SELinux-Kontext wurde so eingestellt, dass der Container über :Z auf das Verzeichnis zugreifen kann Option auf dem Volume-Mount.

Zugriff auf /data Ordner im Container wird verweigert, und die einzige Fehlermeldung, die das System zurückgibt, ist eine allgemeine Berechtigung verweigert aus dem Kern. Jeder Benutzer, der nicht weiß, was ein Benutzernamensraum ist, hätte keine Ahnung, was falsch ist.

Jetzt, da wir es wissen, sollte die Ursache jedoch offensichtlich sein:Die Benutzerzuordnung bedeutet, dass UID 1000 im Container nicht tatsächlich UID 1000 auf dem Host ist. Sie können den Benutzer im Container und den tatsächlichen Benutzer auf dem Host über podman top anzeigen Befehl:

mheon@podman-rhel8-test $ podman top -l user,huser
USER   HUSER
1000   100999

Hier, BENUTZER ist der Benutzer im Container, während HUSER ist der Benutzer auf dem Host.

Zu wissen, warum etwas passiert, bedeutet jedoch nicht, dass wir wissen, wie wir es beheben können. Wir möchten unseren Podman-Container ohne Rootberechtigung weiterhin mit einem bestimmten darin gemounteten Volume ausführen. Wie machen wir das? Glücklicherweise gibt es viele Möglichkeiten, dies zu beheben, auf die ich weiter unten eingehen werde.

Die erste Lösung

Der erste ist einfach:Der --user kann aus dem Container weggelassen werden, indem der Containerbefehl als root ausgeführt wird . Wie oben erwähnt, ordnet Podman den Benutzer, der den Container ausführt, standardmäßig root zu im Container – also greifen wir jetzt als UID/GID 1000 auf dem Host auf das Volume zu, obwohl wir root sind im Behälter. Ausführung als root in einem Root-Container ist ein potenzielles Sicherheitsproblem, da Sie als Root des Systems ausgeführt werden Benutzer – wenn ein Angreifer aus dem Container ausbricht, kann er als Root agieren auf dem System. Der springende Punkt bei einem wurzellosen Container ist, dass dies niemals zutrifft – die Sicherheitsprobleme sind in erster Linie ein Nebenfaktor.

Leider besteht die Lösung darin, den Container als root auszuführen fällt flach, wenn das Image speziell für die Verwendung durch einen Nicht-Root-Benutzer geschrieben wurde. Einige Container-Images verfügen über komplexe Einstiegspunktskripts zum Löschen von Berechtigungen, die nicht einfach geändert werden können. Diese erfordern eine alternative Lösung.

Die zweite Lösung

Die zweite Option besteht darin, dem Benutzer, der im Container ausgeführt wird, die Berechtigung zu erteilen, den vom Host bereitgestellten Ordner zu lesen und zu schreiben. Ab Podman v3.1.0 kann dies automatisch über :U erfolgen volume-Option zum -v Flag (z. B. -v /home/mheon/data:/data:Z,U ).

Geben Sie als Nächstes podman unshare chown 1000:1000 /home/mheon/data ein . Diese Volume-Option passt automatisch den Besitz des Verzeichnisses an, sodass der im Container ausgeführte Benutzer – unabhängig davon, welcher Benutzer-UID 1000 im Container auf dem Host zugeordnet ist – das Verzeichnis besitzt. In Versionen ohne dieses Flag wird der podman unshare Der Befehl kann verwendet werden, um den Namensraum des rootlosen Benutzers einzugeben, und dann chown das Verzeichnis, das dem Benutzer gehört, der den Container ausführt.

In diesem Fall podman unshare chown 1000:1000 /home/mheon/data würde den Besitz des Verzeichnisses auf dem Host auf den Benutzer und die Gruppe ändern, die UID/GID 1000 im Benutzernamensraum zugeordnet sind. Bitte beachten Sie, dass bei einem Eigentümerwechsel auch alle übergeordneten Verzeichnisse auf dem Host die Ausführungsberechtigung für alle Benutzer benötigen (chmod a+x… )-Berechtigung, um sicherzustellen, dass auf das betreffende Verzeichnis zugegriffen werden kann.

Leider ist der chown Der Ansatz hat seine eigenen Nachteile. Die /home/mheon/data Das Verzeichnis befindet sich im Home-Verzeichnis meines Benutzers, aber es gehört nicht mehr meinem Benutzer (in diesem Fall gehört es dem Benutzer und der Gruppe 100999 ). Im Rootless-Benutzernamensraum ist die mheon Benutzer kann als root fungieren und Dateien lesen, schreiben und ändern, die diesem Benutzer gehören; aber es kann keines dieser Dinge außerhalb von ihm tun. Podman stellt einen Befehl bereit, um eine Shell innerhalb des Rootless-Benutzernamensraums einzugeben (podman unshare ), die verwendet werden können, um solche Dateien zu ändern oder zu entfernen, aber die Unfähigkeit, diese Dateien anderweitig zu verwalten, ist unpraktisch.

Die dritte Lösung

Die dritte Option ist die Verwendung von --userns=keep-id Option zum podman run . Dieses Flag weist Podman an, zwei Dinge zu tun:Erstens, den Benutzer, den der Container ausführt, auf die UID und GID des Benutzers festzulegen, der Podman ausgeführt hat (sofern nicht explizit durch --user überschrieben -Flag) und zweitens, um die dem Container zugeordneten Benutzer neu anzuordnen, sodass der Benutzer, der Podman ausgeführt hat, seiner eigenen UID und GID anstelle von root zugeordnet wird (Dies geschieht über einen zweiten Benutzer-Namensraum, der innerhalb des rootlosen Benutzer-Namensraums verschachtelt ist und nur für diesen Container erstellt wurde). Der Benutzer im Benutzernamensraum, unter dem der Container ausgeführt wird, ist nicht root aber immer noch dem Benutzer zugeordnet, der Podman ausführt (mheon ) auf dem Host und kann so auf das im Beispiel gemountete Verzeichnis /home/mheon/data zugreifen .

Dies wird jedoch nicht alle Zugriffsfehler beheben. Ein weiteres häufiges Problem ist der Versuch, eine Datei oder ein Gerät einzuhängen, auf das der Benutzer, der Podman ausführt, Zugriff hat, jedoch nur über eine zusätzliche Gruppe. Nehmen wir zum Beispiel an, dass mein Benutzer mheon , ist Teil des kvm Gruppe, die /dev/kvm besitzt , und ich entscheide mich, /dev/kvm zu mounten in einen Container mit podman run -t -i -v /dev/kvm:/dev/kvm fedora bash . Der Container kann nicht auf /dev/kvm zugreifen , obwohl es als mein Benutzer auf dem Host ausgeführt wird (der Zugriff haben sollte).

Der Grund dafür ist, dass Container aus Sicherheitsgründen (standardmäßig) alle zusätzlichen Gruppen als Teil ihrer Erstellung löschen. Dieses Verhalten kann deaktiviert werden, aber nur mit crun OCI-Laufzeit (dies sollte ab Podman 3.0 auf allen Distributionen außer RHEL standardmäßig sein) durch Übergabe einer speziellen Anmerkung (--annotation run.oci.keep_original_groups=1). ).

In der kommenden Podman v3.2.0 wird dies über ein spezielles Argument für group-add verfügbar sein Flag (--group-add keep-groups ). Bitte beachten Sie, dass wir zwar den Zugriff auf diese Gruppen behalten können, aber nicht berechtigt sind, sie zum Rootless-Benutzernamensraum hinzuzufügen – wir können dies nur für die Benutzer und Gruppen tun, die uns in /etc/subuid und /etc/subgid . Ein ls -al auf /dev/kvm im Container finden Sie, dass es nobody:nobody gehört (als tatsächlicher Besitzer und Gruppe, root:kvm , werden nicht in den Container gemappt).

Es kann jedoch darauf zugegriffen werden, obwohl die Datei kvm group nicht Teil des Benutzernamensraums ist, ist der Containerprozess in den Augen des Kernels immer noch Teil der Gruppe. Dies ist insofern etwas einschränkend, als es nicht möglich ist, Dateien explizit als eine dieser zusätzlichen Gruppen zu erstellen (als nobody ist keine echte Gruppe, mit der wir interagieren können), aber es reicht aus, dem Container Zugriff auf Inhalte auf dem Host zu geben, die er sonst nicht erreichen könnte, und Verzeichnisse mit dem SUID-Bit, das einer zusätzlichen Gruppe gehört, setzen immer noch den richtigen Eigentümer .

Eine andere Art von häufigem Fehler tritt beim Abrufen von Bildern mit Dateien oder Ordnern auf, die Benutzern mit hoher UID gehören. Jede Datei oder jeder Ordner im Besitz einer UID oder GID, die zu groß ist, um in den Benutzernamensraum aufgenommen zu werden, erzeugt einen Fehler. Ich habe zuvor einen Blog darüber und mögliche Lösungen geschrieben, die hier zu finden sind.

Schlussfolgerung

Eines der stärksten Merkmale von Podman ist unsere starke Unterstützung für Container ohne Wurzeln, und es ist nicht schwer zu verstehen, warum die Leute begeistert sind. Rootlose Container sind einfach einzurichten und sicherer als root Container und kann fast alles tun, was ein Container als root ausführt tun können. Das Schlüsselwort ist natürlich fast – da die Gesamterfahrung mit root und rootless so ähnlich ist, können Unterschiede verwirrend sein und sind oft nicht leicht zu erklären. Nachdem Sie diesen Blog gelesen haben, sollten Sie einen der größten dieser Unterschiede gut verstehen und wissen, wie Sie mit Podman arbeiten, damit Ihre Container so laufen, wie Sie es möchten.


Linux
  1. So löschen Sie Benutzerkonten mit Home-Verzeichnis in Linux

  2. So fügen Sie OpenVZ-Containern PPP-Kernel-Unterstützung hinzu

  3. So installieren Sie Nextcloud mit ISPConfig 3.1

  4. Benutzer zu Gruppe in Linux hinzufügen, Anleitung (mit Beispielen)

  5. So erstellen und starten Sie LXC-Linux-Container mit LXC-Befehlen

So führen Sie Container als Systemd-Dienst mit Podman aus

Wie Cirrus CLI Podman verwendet, um rootless Builds zu erreichen

So bearbeiten Sie Code in Docker-Containern mit Visual Studio-Code

So führen Sie Docker-Container aus

Gewusst wie:Erste Schritte mit Windows-Containern und Docker

So verwalten Sie Docker-Container