Trennungsangst: Ein Tutorial zum Isolieren Ihres Systems mit Linux-Namespaces

Veröffentlicht: 2022-03-11

Mit dem Aufkommen von Tools wie Docker, Linux Containers und anderen ist es super einfach geworden, Linux-Prozesse in ihre eigenen kleinen Systemumgebungen zu isolieren. Dies ermöglicht es, eine ganze Reihe von Anwendungen auf einer einzigen echten Linux-Maschine auszuführen und sicherzustellen, dass sich keine zwei von ihnen gegenseitig stören, ohne auf virtuelle Maschinen zurückgreifen zu müssen. Diese Tools waren ein großer Segen für PaaS-Anbieter. Aber was genau passiert unter der Haube?

Diese Tools stützen sich auf eine Reihe von Funktionen und Komponenten des Linux-Kernels. Einige dieser Funktionen wurden erst vor kurzem eingeführt, während Sie für andere immer noch den Kernel selbst patchen müssen. Aber eine der Schlüsselkomponenten, die Verwendung von Linux-Namespaces, ist seit der Veröffentlichung von Version 2.6.24 im Jahr 2008 ein Feature von Linux.

Jeder, der mit chroot vertraut ist, hat bereits eine grundlegende Vorstellung davon, was Linux-Namespaces können und wie Namespaces allgemein verwendet werden. Genauso wie chroot es Prozessen erlaubt, jedes beliebige Verzeichnis als Stammverzeichnis des Systems zu sehen (unabhängig von den übrigen Prozessen), erlauben Linux-Namespaces, dass auch andere Aspekte des Betriebssystems unabhängig modifiziert werden können. Dazu gehören der Prozessbaum, Netzwerkschnittstellen, Einhängepunkte, Kommunikationsressourcen zwischen Prozessen und mehr.

Warum Namespaces für die Prozessisolierung verwenden?

Auf einem Einzelbenutzercomputer kann eine einzelne Systemumgebung in Ordnung sein. Aber auf einem Server, auf dem Sie mehrere Dienste ausführen möchten, ist es für die Sicherheit und Stabilität unerlässlich, dass die Dienste so weit wie möglich voneinander isoliert sind. Stellen Sie sich einen Server vor, auf dem mehrere Dienste ausgeführt werden, von denen einer von einem Eindringling kompromittiert wird. In einem solchen Fall kann der Eindringling möglicherweise diesen Dienst ausnutzen und sich zu den anderen Diensten vorarbeiten und möglicherweise sogar den gesamten Server kompromittieren. Die Namespace-Isolierung kann eine sichere Umgebung bieten, um dieses Risiko zu eliminieren.

Durch die Verwendung von Namensräumen ist es beispielsweise möglich, beliebige oder unbekannte Programme sicher auf Ihrem Server auszuführen. In letzter Zeit gibt es eine wachsende Zahl von Programmierwettbewerben und „Hackathon“-Plattformen wie HackerRank, TopCoder, Codeforces und viele mehr. Viele von ihnen verwenden automatisierte Pipelines, um Programme auszuführen und zu validieren, die von den Teilnehmern eingereicht werden. Es ist oft unmöglich, die wahre Natur der Programme der Teilnehmer im Voraus zu kennen, und einige können sogar böswillige Elemente enthalten. Indem diese Programme im Namensraum vollständig isoliert vom Rest des Systems ausgeführt werden, kann die Software getestet und validiert werden, ohne den Rest der Maschine zu gefährden. In ähnlicher Weise rufen kontinuierliche Online-Integrationsdienste wie Drone.io automatisch Ihr Code-Repository ab und führen die Testskripte auf ihren eigenen Servern aus. Auch hier ist es die Namespace-Isolierung, die es ermöglicht, diese Dienste sicher bereitzustellen.

Namespace-Tools wie Docker ermöglichen auch eine bessere Kontrolle über die Nutzung von Systemressourcen durch Prozesse, was solche Tools für PaaS-Anbieter äußerst beliebt macht. Dienste wie Heroku und Google App Engine verwenden solche Tools, um mehrere Webserveranwendungen zu isolieren und auf derselben realen Hardware auszuführen. Mit diesen Tools können sie jede Anwendung ausführen (die möglicherweise von einer Reihe verschiedener Benutzer bereitgestellt wurde), ohne sich Gedanken darüber machen zu müssen, dass eine von ihnen zu viele Systemressourcen verwendet oder andere bereitgestellte Dienste auf demselben Computer stört und/oder in Konflikt gerät. Mit einer solchen Prozessisolierung ist es sogar möglich, für jede isolierte Umgebung völlig unterschiedliche Stapel von Abhängigkeitssoftware (und Versionen) zu haben!

Wenn Sie Tools wie Docker verwendet haben, wissen Sie bereits, dass diese Tools in der Lage sind, Prozesse in kleinen „Containern“ zu isolieren. Das Ausführen von Prozessen in Docker-Containern ist wie das Ausführen in virtuellen Maschinen, nur dass diese Container wesentlich leichter sind als virtuelle Maschinen. Eine virtuelle Maschine emuliert normalerweise eine Hardwareschicht auf Ihrem Betriebssystem und führt dann ein anderes Betriebssystem darauf aus. Auf diese Weise können Sie Prozesse innerhalb einer virtuellen Maschine ausführen, vollständig isoliert von Ihrem realen Betriebssystem. Aber virtuelle Maschinen sind schwer! Docker-Container hingegen verwenden einige Schlüsselfunktionen Ihres realen Betriebssystems, einschließlich Namespaces, und stellen ein ähnliches Maß an Isolation sicher, ohne jedoch die Hardware zu emulieren und ein weiteres Betriebssystem auf derselben Maschine auszuführen. Dadurch sind sie sehr leicht.

Prozess-Namespace

In der Vergangenheit hat der Linux-Kernel einen einzelnen Prozessbaum verwaltet. Der Baum enthält einen Verweis auf jeden Prozess, der derzeit in einer Eltern-Kind-Hierarchie läuft. Ein Prozess, der über ausreichende Privilegien verfügt und bestimmte Bedingungen erfüllt, kann einen anderen Prozess inspizieren, indem er ihm einen Tracer anhängt, oder ihn möglicherweise sogar beenden.

Mit der Einführung von Linux-Namespaces wurde es möglich, mehrere „verschachtelte“ Prozessbäume zu haben. Jeder Prozessbaum kann einen vollständig isolierten Satz von Prozessen haben. Dadurch kann sichergestellt werden, dass Prozesse, die zu einem Prozessbaum gehören, Prozesse in anderen gleichgeordneten oder übergeordneten Prozessbäumen nicht untersuchen oder beenden können – tatsächlich nicht einmal von der Existenz von Prozessen wissen können.

Jedes Mal, wenn ein Computer mit Linux hochfährt, startet er mit nur einem Prozess, mit der Prozesskennung (PID) 1. Dieser Prozess ist die Wurzel des Prozessbaums und initiiert den Rest des Systems, indem er die entsprechenden Wartungsarbeiten durchführt und startet die richtigen Dämonen/Dienste. Alle anderen Prozesse beginnen unterhalb dieses Prozesses im Baum. Der PID-Namespace ermöglicht es, einen neuen Baum mit seinem eigenen PID-1-Prozess abzuspalten. Der Prozess, der dies tut, verbleibt im übergeordneten Namensraum im ursprünglichen Baum, macht das Kind jedoch zur Wurzel seines eigenen Prozessbaums.

Bei der PID-Namespace-Isolierung haben Prozesse im untergeordneten Namespace keine Möglichkeit, die Existenz des übergeordneten Prozesses zu erfahren. Prozesse im übergeordneten Namespace haben jedoch eine vollständige Ansicht der Prozesse im untergeordneten Namespace, als ob es sich um einen beliebigen anderen Prozess im übergeordneten Namespace handeln würde.

Dieses Namespace-Tutorial skizziert die Trennung verschiedener Prozessbäume mithilfe von Namespace-Systemen in Linux.

Es ist möglich, einen verschachtelten Satz untergeordneter Namensräume zu erstellen: Ein Prozess startet einen untergeordneten Prozess in einem neuen PID-Namensraum, und dieser untergeordnete Prozess erzeugt einen weiteren Prozess in einem neuen PID-Namensraum und so weiter.

Mit der Einführung von PID-Namensräumen können einem einzelnen Prozess nun mehrere PIDs zugeordnet werden, eine für jeden Namensraum, unter den er fällt. Im Linux-Quellcode können wir sehen, dass eine Struktur namens pid , die früher nur eine einzelne PID verfolgte, jetzt mehrere PIDs durch die Verwendung einer Struktur namens upid :

 struct upid { int nr; // the PID value struct pid_namespace *ns; // namespace where this PID is relevant // ... }; struct pid { // ... int level; // number of upids struct upid numbers[0]; // array of upids };

Um einen neuen PID-Namensraum zu erstellen, muss man den Systemaufruf clone() mit einem speziellen Flag CLONE_NEWPID . (C stellt einen Wrapper bereit, um diesen Systemaufruf verfügbar zu machen, ebenso wie viele andere gängige Sprachen.) Während die anderen unten besprochenen Namensräume auch mit dem unshare() erstellt werden können, kann ein PID-Namensraum nur gleichzeitig mit einem neuen erstellt werden Prozess wird mit clone() erzeugt. Sobald clone() mit diesem Flag aufgerufen wird, startet der neue Prozess sofort in einem neuen PID-Namensraum unter einem neuen Prozessbaum. Dies kann mit einem einfachen C-Programm demonstriert werden:

 #define _GNU_SOURCE #include <sched.h> #include <stdio.h> #include <stdlib.h> #include <sys/wait.h> #include <unistd.h> static char child_stack[1048576]; static int child_fn() { printf("PID: %ld\n", (long)getpid()); return 0; } int main() { pid_t child_pid = clone(child_fn, child_stack+1048576, CLONE_NEWPID | SIGCHLD, NULL); printf("clone() = %ld\n", (long)child_pid); waitpid(child_pid, NULL, 0); return 0; }

Kompilieren Sie dieses Programm und führen Sie es mit Root-Rechten aus, und Sie werden eine Ausgabe bemerken, die dieser ähnelt:

 clone() = 5304 PID: 1

Die PID, wie sie innerhalb von child_fn gedruckt wird, ist 1 .

Obwohl dieser Namespace-Tutorial-Code in einigen Sprachen nicht viel länger ist als „Hello, world“, hat sich hinter den Kulissen viel getan. Die Funktion clone() hat erwartungsgemäß einen neuen Prozess erstellt, indem sie den aktuellen Prozess geklont und die Ausführung am Anfang der Funktion child_fn() hat. Dabei löste es jedoch den neuen Prozess vom ursprünglichen Prozessbaum und erstellte einen separaten Prozessbaum für den neuen Prozess.

Versuchen Sie, die static int child_fn() durch Folgendes zu ersetzen, um die übergeordnete PID aus der Perspektive des isolierten Prozesses zu drucken:

 static int child_fn() { printf("Parent PID: %ld\n", (long)getppid()); return 0; }

Wenn Sie das Programm diesmal ausführen, erhalten Sie die folgende Ausgabe:

 clone() = 11449 Parent PID: 0

Beachten Sie, dass die übergeordnete PID aus Sicht des isolierten Prozesses 0 ist, was darauf hinweist, dass es keinen übergeordneten Prozess gibt. Versuchen Sie, dasselbe Programm erneut auszuführen, aber entfernen Sie dieses Mal das Flag CLONE_NEWPID aus dem Aufruf der Funktion clone() :

 pid_t child_pid = clone(child_fn, child_stack+1048576, SIGCHLD, NULL);

Dieses Mal werden Sie feststellen, dass die übergeordnete PID nicht mehr 0 ist:

 clone() = 11561 Parent PID: 11560

Dies ist jedoch nur der erste Schritt in unserem Tutorial. Diese Prozesse haben weiterhin uneingeschränkten Zugriff auf andere gemeinsame oder gemeinsam genutzte Ressourcen. Zum Beispiel die Netzwerkschnittstelle: Wenn der oben erstellte untergeordnete Prozess auf Port 80 lauschen würde, würde dies verhindern, dass jeder andere Prozess auf dem System darauf lauschen kann.

Linux-Netzwerk-Namespace

Hier wird ein Netzwerk-Namespace nützlich. Ein Netzwerk-Namespace ermöglicht es jedem dieser Prozesse, einen völlig anderen Satz von Netzwerkschnittstellen zu sehen. Sogar die Loopback-Schnittstelle ist für jeden Netzwerk-Namespace unterschiedlich.

Das Isolieren eines Prozesses in seinem eigenen Netzwerk-Namensraum erfordert das Einfügen eines weiteren Flags in den Funktionsaufruf clone() : CLONE_NEWNET ;

 #define _GNU_SOURCE #include <sched.h> #include <stdio.h> #include <stdlib.h> #include <sys/wait.h> #include <unistd.h> static char child_stack[1048576]; static int child_fn() { printf("New `net` Namespace:\n"); system("ip link"); printf("\n\n"); return 0; } int main() { printf("Original `net` Namespace:\n"); system("ip link"); printf("\n\n"); pid_t child_pid = clone(child_fn, child_stack+1048576, CLONE_NEWPID | CLONE_NEWNET | SIGCHLD, NULL); waitpid(child_pid, NULL, 0); return 0; }

Ausgabe:

 Original `net` Namespace: 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: enp4s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000 link/ether 00:24:8c:a1:ac:e7 brd ff:ff:ff:ff:ff:ff New `net` Namespace: 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

Was ist denn hier los? Das physische Ethernet-Gerät enp4s0 gehört zum globalen Netzwerk-Namespace, wie durch das „ip“-Tool angezeigt wird, das von diesem Namespace ausgeführt wird. Die physische Schnittstelle ist jedoch im neuen Netzwerknamensraum nicht verfügbar. Darüber hinaus ist das Loopback-Gerät im ursprünglichen Netzwerk-Namensraum aktiv, aber im untergeordneten Netzwerk-Namensraum „ausgefallen“.

Um eine nutzbare Netzwerkschnittstelle im untergeordneten Namensraum bereitzustellen, müssen zusätzliche „virtuelle“ Netzwerkschnittstellen eingerichtet werden, die sich über mehrere Namensräume erstrecken. Sobald dies erledigt ist, ist es dann möglich, Ethernet-Bridges zu erstellen und sogar Pakete zwischen den Namespaces zu routen. Damit das Ganze funktioniert, muss schließlich ein „Routing-Prozess“ im globalen Netzwerk-Namespace ausgeführt werden, um Datenverkehr von der physischen Schnittstelle zu empfangen und ihn durch die entsprechenden virtuellen Schnittstellen zu den richtigen untergeordneten Netzwerk-Namespaces zu leiten. Vielleicht können Sie sehen, warum Tools wie Docker, die all diese schwere Arbeit für Sie erledigen, so beliebt sind!

Der Linux-Netzwerk-Namespace besteht aus einem Routing-Prozess zu mehreren untergeordneten Netz-Namespaces.

Um dies manuell zu tun, können Sie ein Paar virtueller Ethernet-Verbindungen zwischen einem übergeordneten und einem untergeordneten Namespace erstellen, indem Sie einen einzigen Befehl aus dem übergeordneten Namespace ausführen:

 ip link add name veth0 type veth peer name veth1 netns <pid>

Hier sollte <pid> durch die Prozess-ID des Prozesses im untergeordneten Namensraum ersetzt werden, wie sie vom übergeordneten Prozess beobachtet wird. Durch Ausführen dieses Befehls wird eine Pipe-ähnliche Verbindung zwischen diesen beiden Namespaces hergestellt. Der übergeordnete Namespace behält das veth0 -Gerät und übergibt das veth1 -Gerät an den untergeordneten Namespace. Alles, was an einem der Enden eintritt, kommt am anderen Ende heraus, genau wie Sie es von einer echten Ethernet-Verbindung zwischen zwei echten Knoten erwarten würden. Dementsprechend müssen beiden Seiten dieser virtuellen Ethernet-Verbindung IP-Adressen zugewiesen werden.

Mount-Namespace

Linux verwaltet auch eine Datenstruktur für alle Einhängepunkte des Systems. Es enthält Informationen darüber, welche Festplattenpartitionen eingehängt sind, wo sie eingehängt sind, ob sie schreibgeschützt sind usw. Mit Linux-Namespaces kann man diese Datenstruktur klonen lassen, sodass Prozesse unter verschiedenen Namespaces die Einhängepunkte ändern können, ohne sich gegenseitig zu beeinflussen.

Das Erstellen eines separaten Mount-Namensraums hat einen ähnlichen Effekt wie das Ausführen von chroot() . chroot() ist gut, aber es bietet keine vollständige Isolierung, und seine Auswirkungen sind nur auf den Root-Einhängepunkt beschränkt. Durch das Erstellen eines separaten Mount-Namensraums kann jeder dieser isolierten Prozesse eine völlig andere Ansicht der Mountpoint-Struktur des gesamten Systems als die ursprüngliche haben. Auf diese Weise können Sie für jeden isolierten Prozess einen anderen Stamm sowie andere Mountpoints haben, die für diese Prozesse spezifisch sind. Bei sorgfältiger Verwendung in diesem Lernprogramm können Sie es vermeiden, Informationen über das zugrunde liegende System preiszugeben.

Das Erlernen der korrekten Verwendung von Namespaces hat mehrere Vorteile, wie in diesem Namespace-Tutorial beschrieben.

Das dazu erforderliche Flag clone() ist CLONE_NEWNS :

 clone(child_fn, child_stack+1048576, CLONE_NEWPID | CLONE_NEWNET | CLONE_NEWNS | SIGCHLD, NULL)

Anfänglich sieht der untergeordnete Prozess genau die gleichen Einhängepunkte wie sein übergeordneter Prozess. Da sich der untergeordnete Prozess jedoch unter einem neuen Mount-Namespace befindet, kann er beliebige Endpunkte mounten oder unmounten, und die Änderung wirkt sich weder auf den Namespace seines übergeordneten Prozesses noch auf einen anderen Mount-Namespace im gesamten System aus. Wenn zum Beispiel der übergeordnete Prozess eine bestimmte Festplattenpartition als Root gemountet hat, sieht der isolierte Prozess am Anfang genau dieselbe Festplattenpartition, die als Root gemountet ist. Der Vorteil des Isolierens des Mount-Namespace wird jedoch offensichtlich, wenn der isolierte Prozess versucht, die Root-Partition in etwas anderes zu ändern, da die Änderung nur den isolierten Mount-Namespace betrifft.

Interessanterweise macht es das eigentlich zu einer schlechten Idee, den untergeordneten Zielprozess direkt mit dem Flag CLONE_NEWNS zu spawnen. Ein besserer Ansatz besteht darin, einen speziellen „init“-Prozess mit dem Flag CLONE_NEWNS zu starten, diesen „init“-Prozess das „/“, „/proc“, „/dev“ oder andere Einhängepunkte wie gewünscht ändern zu lassen und dann den Zielprozess zu starten . Dies wird am Ende dieses Namespace-Tutorials etwas ausführlicher erläutert.

Andere Namensräume

Es gibt andere Namensräume, in denen diese Prozesse isoliert werden können, nämlich Benutzer, IPC und UTS. Der Benutzernamensraum erlaubt einem Prozess, Root-Privilegien innerhalb des Namensraums zu haben, ohne ihm diesen Zugriff auf Prozesse außerhalb des Namensraums zu gewähren. Das Isolieren eines Prozesses durch den IPC-Namensraum gibt ihm seine eigenen Interprozess-Kommunikationsressourcen, zum Beispiel System V IPC- und POSIX-Nachrichten. Der UTS-Namensraum isoliert zwei spezifische Bezeichner des Systems: nodename und domainname .

Ein kurzes Beispiel, um zu zeigen, wie der UTS-Namespace isoliert wird, ist unten dargestellt:

 #define _GNU_SOURCE #include <sched.h> #include <stdio.h> #include <stdlib.h> #include <sys/utsname.h> #include <sys/wait.h> #include <unistd.h> static char child_stack[1048576]; static void print_nodename() { struct utsname utsname; uname(&utsname); printf("%s\n", utsname.nodename); } static int child_fn() { printf("New UTS namespace nodename: "); print_nodename(); printf("Changing nodename inside new UTS namespace\n"); sethostname("GLaDOS", 6); printf("New UTS namespace nodename: "); print_nodename(); return 0; } int main() { printf("Original UTS namespace nodename: "); print_nodename(); pid_t child_pid = clone(child_fn, child_stack+1048576, CLONE_NEWUTS | SIGCHLD, NULL); sleep(1); printf("Original UTS namespace nodename: "); print_nodename(); waitpid(child_pid, NULL, 0); return 0; }

Dieses Programm liefert die folgende Ausgabe:

 Original UTS namespace nodename: XT New UTS namespace nodename: XT Changing nodename inside new UTS namespace New UTS namespace nodename: GLaDOS Original UTS namespace nodename: XT

Hier gibt child_fn() den nodename , ändert ihn in etwas anderes und gibt ihn erneut aus. Die Änderung erfolgt natürlich nur innerhalb des neuen UTS-Namensraums.

Weitere Informationen darüber, was alle Namespaces bieten und isolieren, finden Sie im Tutorial hier

Namensraumübergreifende Kommunikation

Oft ist es notwendig, eine Art Kommunikation zwischen dem übergeordneten und dem untergeordneten Namensraum herzustellen. Dies kann für die Durchführung von Konfigurationsarbeiten in einer isolierten Umgebung oder einfach dazu dienen, die Möglichkeit zu behalten, den Zustand dieser Umgebung von außen einzusehen. Eine Möglichkeit, dies zu tun, besteht darin, einen SSH-Daemon in dieser Umgebung laufen zu lassen. Sie können in jedem Netzwerk-Namespace einen separaten SSH-Daemon haben. Wenn jedoch mehrere SSH-Daemons ausgeführt werden, werden viele wertvolle Ressourcen wie Arbeitsspeicher verbraucht. Auch hier erweist sich ein spezieller „init“-Prozess als sinnvoll.

Der „init“-Prozess kann einen Kommunikationskanal zwischen dem Eltern-Namensraum und dem Kind-Namensraum aufbauen. Dieser Kanal kann auf UNIX-Sockets basieren oder sogar TCP verwenden. Um einen UNIX-Socket zu erstellen, der zwei verschiedene Mount-Namespaces umfasst, müssen Sie zuerst den untergeordneten Prozess erstellen, dann den UNIX-Socket erstellen und dann den untergeordneten Prozess in einem separaten Mount-Namespace isolieren. Aber wie können wir den Prozess zuerst erstellen und ihn später isolieren? Linux bietet unshare() . Dieser spezielle Systemaufruf ermöglicht es einem Prozess, sich selbst vom ursprünglichen Namensraum zu isolieren, anstatt dass der Elternteil das Kind überhaupt erst isolieren muss. Der folgende Code hat beispielsweise genau dieselbe Wirkung wie der zuvor im Abschnitt Netzwerk-Namespace erwähnte Code:

 #define _GNU_SOURCE #include <sched.h> #include <stdio.h> #include <stdlib.h> #include <sys/wait.h> #include <unistd.h> static char child_stack[1048576]; static int child_fn() { // calling unshare() from inside the init process lets you create a new namespace after a new process has been spawned unshare(CLONE_NEWNET); printf("New `net` Namespace:\n"); system("ip link"); printf("\n\n"); return 0; } int main() { printf("Original `net` Namespace:\n"); system("ip link"); printf("\n\n"); pid_t child_pid = clone(child_fn, child_stack+1048576, CLONE_NEWPID | SIGCHLD, NULL); waitpid(child_pid, NULL, 0); return 0; }

Und da der „init“-Prozess etwas ist, das Sie entwickelt haben, können Sie ihn zuerst alle notwendigen Arbeiten erledigen lassen und sich dann vom Rest des Systems isolieren, bevor er das Ziel-Kind ausführt.

Fazit

Dieses Tutorial ist nur ein Überblick über die Verwendung von Namespaces in Linux. Es sollte Ihnen eine grundlegende Vorstellung davon vermitteln, wie ein Linux-Entwickler mit der Implementierung der Systemisolierung beginnen könnte, die ein wesentlicher Bestandteil der Architektur von Tools wie Docker oder Linux-Containern ist. In den meisten Fällen ist es am besten, einfach eines dieser vorhandenen Tools zu verwenden, die bereits bekannt und erprobt sind. Aber in einigen Fällen kann es sinnvoll sein, einen eigenen, angepassten Mechanismus zur Prozessisolierung zu haben, und in diesem Fall wird Ihnen dieses Namespace-Tutorial enorm helfen.

Unter der Haube passiert viel mehr, als ich in diesem Artikel behandelt habe, und es gibt mehr Möglichkeiten, wie Sie Ihre Zielprozesse für zusätzliche Sicherheit und Isolierung einschränken möchten. Aber hoffentlich kann dies jemandem, der mehr darüber wissen möchte, wie Namespace-Isolierung mit Linux wirklich funktioniert, als nützlicher Ausgangspunkt dienen.