NET_ADMIN in einem VPN-Container: Was sie ändern kann und wo
Die Capability erlaubt Änderungen an Schnittstellen, Routen und Firewall innerhalb eines Netzwerk-Namespace. Mit Host-Netzwerk ist dieser Namespace der eigene Stack des Hosts.
NET_ADMIN ist die Linux-Capability, die einen Prozess Netzwerkadministration ausführen lässt, und in einem Container tut sie genau das in dem Netzwerk-Namespace, in dem der Prozess läuft. Sie ist keine Lizenz zum Öffnen eines Sockets und kein Ersatz für ein Gerät oder eine Route. Die interessante Frage ist nie, ob die Capability erteilt wurde, sondern welcher Namespace die Änderung erhält.
Was die Capability abdeckt
Innerhalb ihres Namespace kann ein Prozess mit NET_ADMIN Schnittstellen hoch- und herunterfahren, Adressen vergeben, Routen hinzufügen und entfernen und Firewallregeln schreiben, also die Gruppe von Operationen, die capabilities(7) unter dieser Capability sammelt. Er kann außerdem Einstellungen auf Schnittstellenebene anpassen, die steuern, wie der Kernel Verkehr einreiht und filtert. Ein VPN-Client braucht davon typischerweise einige beim Start und danach nichts weiter. Die meisten Images fordern daher mehr Reichweite an, als sie nutzen.
Operationen außerhalb dieses Namespace sind nicht abgedeckt, und benachbarte Privilegien sind getrennt. Lesen und Schreiben roher Pakete gehört zu NET_RAW, und das Anlegen eines tun-Geräts braucht sowohl die Capability als auch Zugriff auf den Geräteknoten. Ein Prozess ohne das Gerät erhält einen Berechtigungsfehler, der wie ein Netzwerkproblem aussieht und keines ist.
Der Namespace bestimmt den Radius
Ein Container im eigenen Bridge-Netz erreicht sein eigenes Loopback, seine eigenen Schnittstellen und seine eigene Routing-Tabelle. Wird dort NET_ADMIN erteilt, kann der VPN-Client einen Tunnel bauen, ohne die Routing-Entscheidungen des Hosts zu berühren, was die Anordnung ist, die die meisten Container-Images annehmen.
Schalten Sie diesen Container auf Host-Netzwerk um, und dieselbe Erteilung bearbeitet nun die Routen und die Firewall des Hosts, weil kein getrennter Namespace mehr übrig ist. Der Client, der einen einzigen Tunnel verwalten sollte, kann dann den Verkehr der ganzen Maschine umleiten, und ein Fehler in seinem Teardown-Skript hinterlässt Regeln auf dem Host, die niemand geschrieben hat. An der Capability selbst hat sich nichts geändert; der Kontext hat sich geändert. Deshalb verhält sich dieselbe Compose-Datei auf zwei Hosts unterschiedlich.
Drei Voraussetzungen, die zu einer verschmelzen
Startfehler in VPN-Containern betreffen meist eines von drei getrennten Dingen: die Capability, das Tunnelgerät und die Kernel-Unterstützung für das Protokoll. Alle drei auf einmal zu erteilen funktioniert, und es macht zugleich den nächsten Fehler unzuordenbar. Ein minimales Compose-Fragment erteilt die Capability und übergibt den Geräteknoten ausdrücklich:
services:
vpn:
image: example/vpn-client:latest
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun
sysctls:
- net.ipv4.conf.all.src_valid_mark=1Lesen Sie in der Dokumentation des Images nach, welche sysctls es braucht, denn ein sysctl in einem Container kann ein zusätzliches Privileg oder eine Einstellung auf Host-Ebene erfordern. Wo das Image einen reinen Userspace-Modus unterstützt, ist überhaupt kein Gerät nötig, und die Capability-Liste schrumpft entsprechend.
Erteilen Sie die enge Operation, nicht den Modus
Der privilegierte Modus übergibt jede Capability und erweitert gleichzeitig den Gerätezugriff, sodass er verbirgt, welche der drei Voraussetzungen fehlte. Er macht auch die Fehlerbilder des Containers von Host-Änderungen ununterscheidbar. Bevorzugen Sie eine ausdrückliche Liste, auch wenn sie länger zu schreiben ist.
Rootless- und User-Namespace-Deployments verändern, was eine Capability in der Praxis bedeutet, da der Prozess sie nur innerhalb eines gemappten Namespace halten kann. Eine Capability-Liste aus einem rootful Beispiel in eine rootless Laufzeit zu kopieren und dieselbe Reichweite zu erwarten, ist ein üblicher Weg, einen Nachmittag zu verlieren.
Geheimnisse gehören zu einer eigenen Entscheidung. NET_ADMIN zu halten sagt nichts darüber, welche Peer-Identität der Client vorzeigt, welche Anmeldedaten er lädt oder wie diese rotiert werden. Netzwerkadministration liefert kein Vertrauen.
Prüfen Sie die Änderung und notieren Sie, warum die Erteilung existiert
Bestätigen Sie, was der Container tatsächlich erhalten hat, bevor Sie den Tunnel debuggen. Die folgende Inspektion meldet die hinzugefügten Capabilities und den Netzwerkmodus und zeigt dann, wie die Schnittstellenliste von innen aussieht:
docker inspect --format '{{.HostConfig.CapAdd}} {{.HostConfig.NetworkMode}} {{.HostConfig.Privileged}}' vpn
docker exec vpn sh -c 'grep CapEff /proc/self/status'
docker exec vpn ip -br addr
docker exec vpn ip routeIst die Capability vorhanden und die Schnittstellenliste weiterhin leer, ist der Client gescheitert, bevor er etwas anlegen konnte, und die Container-Logs sind die nächste Stelle zum Nachsehen. Existiert der Tunnel, aber es fließt kein Verkehr, beantwortet die Routing-Tabelle im Container die nächste Frage.
Testen Sie auch den Teardown-Pfad, denn ein Client, der Firewallregeln schreibt, kann sie hinterlassen. Stoppen Sie den Container und prüfen Sie das Regelset des Hosts erneut, wenn Host-Netzwerk verwendet wird. Eine Erteilung, die nur zwei Sekunden beim Start nötig ist, sollte in der Compose-Datei nicht als dauerhafte Anforderung beschrieben werden, ohne dass eine Notiz das erklärt.
Schreiben Sie den Grund neben jede Capability: Welche Operation sie brauchte und was ohne sie scheitert. Diese Notiz überlebt Image-Updates, und sie ist der Unterschied zwischen einer geprüften Privilegienliste und einer, die durch Kopieren und Einfügen gewachsen ist.