daemonless und rootless

Podman: Container ohne Daemon und Root-Rechte

Mario Kleinsasser

© Shutterstock / Maridav

Podman ist seit seinem ersten Release im Februar 2018 [1] rasant gewachsen und wird mittlerweile vielerorts als Ersatz für Docker gehandelt. Das eine durch das andere zu ersetzen, klingt logisch. Jedoch ist eine Synergie oft besser – gemeinsam mehr erreichen! Es gibt durchaus gute Gründe, Podman einzusetzen. Allerdings kann Podman alleine nicht alle Möglichkeiten von Docker direkt ersetzen. Was funktioniert, was ist möglich und wie lässt sich das Meiste aus Podman herausholen? In diesem Artikel beantworten wir diese Fragen.

Die Unterschiede von Podman und Docker ergeben sich aus den unterschiedlichen Ansätzen, welche beide Container-Manger verfolgen. Docker wurde 2013 als Werkzeug entworfen, um einen einfachen Zugang zu Linux Namespaces und deren Isolationsmöglichkeiten bereitszustellen. Podman, 2018 entstanden, konnte auf den von Docker gewonnen Erfahrungen aufbauen und diese in bestimmten Bereichen erweitern. Das Tool verwendet dieselben Kommandozeilenparameter und Schalter wie Docker, daher ist ein Umstieg von Docker auf Podman für den lokalen Betrieb von Containern recht problemlos möglich. Ausgenommen davon ist jedoch der Betrieb mehrerer Pods über mehrere Hosts hinweg. Dies ist mit Podman nicht möglich, da hier die Funktionalität von Docker Swarm (Overlay-Netzwerke) fehlt.

Hier muss also alternativ auf Kubernetes zurückgegriffen werden. Kubernetes stellt viele Konzepte und Vorgehensweisen zur Verfügung, welche sich immer mehr am Markt etablieren, sofern man den medialen Berichten glauben schenken mag. Zu diesen Konzepten zählen beispielsweise Pods oder daemonless und rootless Container.

Pods

Podman [2] ist ein Container-Manger, der ohne einen Daemon ausgeführt werden kann und auf dem Konzept von Kubernetes Pods aufbaut (Abb. 1). Kubernetes Pods [3] stellen einen Zusammenschluss von mehreren Containern innerhalb eines gemeinsamen Linux „Namespaces“ dar [4]. Die Möglichkeit „Pods“ zu erstellen, ist sicherlich einer der Vorteile von Podman. Dadurch ist es möglich, sehr flexible Kombinationen und Konfigurationen von Containern gemeinsam zu starten. Zum Beispiel kann ein Container innerhalb eines Pods dafür zuständig sein, in regelmäßigen, zeitgesteuertn Abständen den Inhalt einer statischen Webseite aus einem Git Repository zu pullen, während ein weiterer Container desselben Pods den Inhalt mittels eines Webservers zur Verfügung stellt.

Abbildung 1: Das Pod-Konzept

Ein derartiger Zusammenschluss von Containern ist mit Docker nicht ohne weitere Konfiguration realisierbar, da das Konzept von Pods in Docker nicht als solches bekannt ist. Das zusätzliche Werkzeug docker-compose stellt dies Funktionen jedoch zur Verfügung. Die Idee dahinter basiert auf dem Teilen eines gemeinsamen Netzwerk-Namespaces. Die auf dem Blog von Michael Irwin [5] beschriebene Vorgehensweise kommt beim Einsatz von Kubernetes automatisch zur Anwendung. Leider ist es derzeit nicht möglich, Kubernetes Deployments, welche die Pod-Konfiguration enthalten, mittels Podman direkt zu verwenden [6]. Das ist schade, da es somit mit Podman zwar einfacher wird, lokale Pods zu starten [7], ein echter Vorteil gegenüber docker-compose ist jedoch unmittelbar nicht gegeben. Vor allem die unterschiedliche Benennung derselben Funktionalität zwischen Kubernetes und Podman dürfte für viele Anwender den Zugang zu diesem Thema nicht gerade erleichtern. Als Beispiel sei hier der „infra“-Container angeführt, der exakt den gleichen Zweck erfüllt, ja sogar dasselbe Container-Image verwendet, wie in Kubernetes, nämlich k8s.gcr.io/pause [7]. Warum dieser Container im Gegensatz zu Kubernetes jedoch nicht als „pause“-Container bezeichnet wird, sondern als „infra“-Container, ist nicht nicht ganz klar — selbst wenn „infra“ die technisch korrektere Bezeichnung ist — richtiger bedeutet nicht immer zugänglicher für die Anwender.

Daemonless

Docker steht breits seit längerem im Fokus von vielen Sicherheitsforschern, da der Docker Daemon, also der Dienst, der für die Verwaltung der Container (starten, stoppen, etc.) zuständig ist, als Root-Benutzer ausgeführt wird. Dies wird bedingt durch die Möglichkeiten, welche Docker ohne weitere Werkzeuge zur Verfügung stellt, beispielsweise Overlay-Netzwerke im Docker-Swarm-Betrieb. Podman arbeitet grundsätzlich ohne einen zentralen Daemon und stellt somit ein Werkzeug dar, das im User-Space und damit im Kontext der jeweiligen Benutzerrechte ausgeführt werden kann.

Der daemonless-Betrieb bedeutet jedoch nicht, dass es keinen Daemon gibt, welcher die Container eines Pods überwacht. Zu diesem Zweck wird automatisch mit dem Start eines jeden Pods ein weiterer Prozess mitgestartet und dieser Prozess hört auf den Namen conmon [9]. conmon tägt dabei nicht nur die Verantwortung, die Container des Pods zu überwachen, vielmehr ist conmon das Verbindungsglied zwischen dem Container-Manager und der Container-Runtime (runc) und sorgt damit für die korrekten Startparameter für die Container-Runtime. Weiters muss conmon die drei Standarddatenströme (stdin, stdout und stderr) verwalten und ist ebenso zuständig für den Start von slirp4netns [10], welches die Weiterleitung der Netzwerkdatenströme zu und von den im Pod befindlichen Containern bewerkstelligt. Dies kann, wie in Abbbildung 2 dargestellt, durch das Ausführen des netstat-Befehls nach dem Start eines Pods mit entsprechender Host-Port-Weiterleitung einfach überprüft werden.

Abbildung 2: podman und slirp4netns

In der für diesen Artikel verwendeten Podman-Version (1.6.2) überwacht conmon leider nicht den slirp4netns-Prozess. Sollte slirp4netns aufgrund eines Fehlers abstürzen oder wird der Prozess bewusst beendet, so wird er leider nicht mehr neu gestartet und eine Netzwerkkommunikation mit dem im Pod befindlichen Service ist nicht mehr möglich (Abb. 3). Ein entsprechendes GitHub Issue für dieses Verhalten wurde eröffnet [11]; vielleicht erfolgt ja in einer der nächsten Podman-Versionen eine Überarbeitung.

Abbildung 3: kill slirp4netns

Da der slirp4netns-Prozess mit denselben Rechten ausgeführt wird wie der Container selbst, ist es im rootless-Modus nicht möglich, einen Low Port als Host-Port zu verwenden, da dies entsprechende administrative Rechte erfordern würde. Rootless Container sind eine weitere Stärke von Podman.

Der automatische Start von Podman Pods muss aufgrund des daemonless-Ansatzes mittels der systemd unit-Datei erfolgen. Wie Abbildung 4 zeigt, können diese Dateien können jedoch direkt mit dem Podman-Kommando generiert werden.

Abbildung 4: podman generate systemd

Rootless

Rootless-Container bedeuten, dass es möglich ist, einen Container ohne Root-Berechtigungen zu starten. Dank des Mappings der Benutzer-IDs in den Namespace des laufenden Containers besitzt der Container keine Root-Rechte. Wie in Abbildung 5 dargestellt, wird zuerst mittels Podman ein Alpine-Container gestartet, welcher wiederum einen sleep-Befehl startet. Nachdem der Container gestartet ist, kann im Host-Betriebssystem überprüft werden, mit welchem Benutzer der sleep-Befehl darin ausgeführt wird. Im gezeigten Beispiel ist dies m4r10k, mein eigener Linux-Benutzer ohne Root-Rechte. Der zweite Befehl, welcher innerhalb des laufenden Containers ausgeführt wird, zeigt, dass innerhalb des Containers der sleep-Befehl mit Root-Rechten gestartet wurde. Das Mapping der Benutzer-IDs funktioniert ausgezeichnet – innerhalb des Containers läuft ein Prozess mit Root-Rechten, welcher jedoch innerhalb des Host-Betriebssystems nur über normale Benutzer-Berechtigungen verfügt.

Abbildung 5: podman run-Kommando – Rootless

Diese Funktionalität stellt gegenüber der Vorgehensweise von Docker einen wesentlichen Fortschritt dar. Docker unterstützt die Ausführung des Docker Daemons in der rootless-Variante experimentell erst ab Version 19.03 [8]. Die Unterstützung von rootless-Containern steht hier jedoch erst am Anfang der Implementierung, da diese Veränderung innerhalb der Docker-Basis-Technologie viele Bereiche betrifft, unter anderem den Netzwerkbereich mit den Overlay-Netzwerken im Docker-Swarm-Betrieb.

Beim Betrieb von rootless-Containern gilt es ebenso zu beachten, dass keine Low Ports für das Host-Port-Mapping verwendet werden dürfen. Für den Start eines Pods mit manuellem Port Expose bedeutet dies, dass ein Port höher als 1024 verwendet werden muss. Wird dieser Umstand nicht beachtet, so bricht der Podman-Befehl mit einer eher kryptischen Fehlermeldung, wie in Abbildung 6 gezeigt, ab.

Abbildung 6: podman rootless Low-Port-Mapping

Fazit

Podman bietet mit den daemonless- und rootless-Möglichkeiten viel Potential, Container sicher am lokalen Rechner oder auf einem einzelnen Server betreiben zu können. Derzeit existieren noch ein paar kleinere Probleme, welche jedoch nicht weiter ins Gewicht fallen, sofern die üblichen Konfigurationsmöglichkeiten verwendet werden. Insbesondere im Kubernetes-Umfeld, bspw. bei Red Hats OpenShift, hat sich Podman bereits als Standard durchgesetzt. Die Aufteilung in mehrere verschiedene kleinere Dienste, podman, conmon und slirp4netns entspricht dabei der Unix-Philosophie: „Make each program do one thing well“. Ob sich dies jedoch auch langfristig durchsetzen lassen wird, wird sich erst erweisen, wie das Beispiel des „one-big-daemon“-systemd in den letzten Jahren gezeigt hat.

Verwandte Themen:

Geschrieben von
Mario Kleinsasser
Mario Kleinsasser
Mario Kleinsasser arbeitet als Cloud Solution Architect und SysOps bei der STRABAG BRVZ GmbH. Er hat mehr als 20 Jahre Linux-Erfahrung und setzt Container-Technologien seit 2006 produktiv ein. Sein Wissen und seine Erfahrungen gibt er immer gerne als Conference Speaker und Docker Community Leader weiter. Außerdem beteiligt er sich aktiv an verschiedenen Open-Source-Software-Projekten. Web: n0r1sk.com/mario Twitter: @m4r10k
Kommentare

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
4000
  Subscribe  
Benachrichtige mich zu: