Kubernetes securityContext: Linux-Funktionen in Kubernetes
26. Januar 2021
0 Min. LesezeitIn grauer Vorzeit hatten Unix-Betriebssysteme ein relativ einfaches Berechtigungsmodell. Entweder waren Sie ein normaler Benutzer oder root – der Superuser mit allen Berechtigungen.
Normalen Benutzern konnten zwar erweiterte Berechtigungen für Dateien oder Verzeichnisse erteilt werden, doch fast alle Funktionen auf Kernel-Ebene waren dem root-Benutzer vorbehalten. Benötigte Ihre Anwendung einen einzigen Kernel-Aufruf, um zu funktionieren, musste sie mit SUID-Berechtigungen ausgestattet werden – dadurch erhielt der Prozess effektiv root-Berechtigungen, wenn er von einem normalen Benutzer gestartet wurde.
Das funktionierte in einer Zeit, in der physische Rechner von einer relativ kleinen Benutzergruppe verwendet wurden, recht gut. Diese grobe Abstufung war für die moderne Welt jedoch nicht wirklich geeignet. Um mehr Flexibilität und Sicherheit zu ermöglichen, entwickelten die Linux-Kernel-Entwickler eine deutlich feinere Lösung: Sie gruppierten Kernel-Aufrufe in Funktionen und erlaubten, diese einem einzelnen Prozess zuzuweisen. Benötigte Ihre Anwendung nun einen einzelnen Kernel-Aufruf, konnte ihr genau diese Funktion zugewiesen werden – und so die Sicherheitsrisiken für Ihr System begrenzen.
Berechtigungen in Kubernetes-Containern verwalten
Wie funktioniert das also in Containern?
Ein Container ist im Grunde ein Prozess, der auf dem System ausgeführt und mithilfe von cgroups und Namespaces im Kernel isoliert wird. Daher lassen sich einem Container Funktionen genauso zuweisen wie jedem anderen Prozess. Das übernimmt die Container-Runtime beim Erstellen des Containers.
Üblicherweise weist die Container-Runtime dem Container eine Reihe von Standardfunktionen zu (die von Docker bereitgestellte Standardauswahl finden Sie hier) und bietet außerdem die Möglichkeit, Funktionen hinzuzufügen oder zu entfernen. Starten Sie einen Container mit dem Flag --privileged, weisen Sie ihm alle Funktionen zu. Wenn der Container als root ausgeführt wird, kann dadurch ein ernstes Sicherheitsproblem entstehen – ein Thema, das ich in einem früheren Beitrag behandelt habe.
Funktionen für einen Container im Kubernetes-securityContext festlegen
Bei der Verwendung der Container-Runtime in Kubernetes nutzt die Kubernetes-Steuerungsebene diese Einstellungen, um festzulegen, mit welchen Funktionen unser Container gestartet werden soll. Die Konfiguration dieser Funktionen wird über verschiedene Einstellungen im Abschnitt securityContext der YAML-Datei eines Containers zugänglich gemacht. Sie sieht folgendermaßen aus:
In diesem Fall würden wir alle Funktionen entfernen und anschließend die Funktion CAP_NET_ADMIN hinzufügen. Ist der Abschnitt capabilities im securityContext leer, erhalten wir die von der Container-Runtime festgelegte Standardauswahl an Funktionen. Diese ist üblicherweise recht großzügig bemessen und umfasst möglicherweise deutlich mehr Funktionen, als unsere Anwendung benötigt. Starten wir einen Pod in Kubernetes und sehen uns an, welche Funktionen er erhält.
Zunächst erstellen wir eine YAML-Datei, um einen Pod bereitzustellen. In dieser Konfiguration verwenden wir ein Upstream-Image von Docker Hub. Das auf Alpine basierende Image enthält das Tool capsh, mit dem wir die Funktionen unseres Containers anzeigen können.
Beachten Sie, dass wir für diesen Container überhaupt keinen securityContext-Abschnitt definiert haben. Daher gelten für alle Einstellungen die Systemstandards. Denken Sie daran: Wenn in Kubernetes kein Benutzer angegeben ist, wird der Container als der Standardbenutzer ausgeführt, der in der Dockerfile festgelegt ist, aus der er erstellt wurde. Bei vielen Containern ist das root.
Sobald der Pod ausgeführt wird, können wir eine Shell darin öffnen und mit capsh die Funktionen überprüfen:
Funktionen im Kubernetes-securityContext entfernen
Wie wir sehen, wird der Container standardmäßig als root ausgeführt und verfügt über zahlreiche Funktionen. Diese werden standardmäßig von der Container-Runtime festgelegt. Versuchen wir nun, eine dieser Funktionen über die Einstellungen im securityContext zu entfernen: Wir entziehen dem Container die Möglichkeit, neue Dateisystemknoten anzulegen. Dafür ist CAP_MKNOD zuständig:
Im Vergleich zum ersten Pod sehen wir, dass bei diesem Pod nun die Funktion cap_mknod entfernt wurde. Neben einzelnen Funktionen können wir über den securityContext auch alle Funktionen entfernen:
Ohne jegliche Funktionen schlagen bestimmte Systemaktionen fehl. Versuchen wir beispielsweise, bash mit apk zu installieren:
Obwohl unser Container als root ausgeführt wird, schlägt die Installation des bash-Pakets fehl, da dafür Dateisystemberechtigungen festgelegt werden müssen. Diese Funktion haben wir dem Container entzogen. Wenn wir verhindern möchten, dass Software in unserem Container installiert wird, können wir die Berechtigungen also über Funktionen verwalten.
capsh bietet zwar eine übersichtliche Darstellung der Funktionen unseres Containers, ist aber nicht die einzige Möglichkeit, diese Informationen abzurufen. Wir können direkt auf das proc-Dateisystem zugreifen, ohne zusätzliche Software zu installieren:
In der Datei /proc/1/status werden die Funktionen als Bitmaske angezeigt. Da in diesem Container keine Funktionen aktiviert sind, bestehen die Werte nur aus Nullen. Sehen wir uns dieselbe Datei im ursprünglichen Container an, in dem alle Funktionen aktiviert sind:
Das ist nicht ganz so gut lesbar wie die Ausgabe von capsh. Jedes hier angezeigte Bit steht jedoch für eine bestimmte Linux-Capability, die im entsprechenden Kernel-Header definiert ist.
Erfahren Sie, wie Sie die Kubernetes-Sicherheit verbessern können, indem Sie Standardfunktionen für einen Container entfernen.
Linux-Capabilities im Kubernetes-securityContext hinzufügen
Neben dem Entfernen von Linux-Capabilities können wir sie auch wieder hinzufügen. Nehmen wir unser vorheriges Beispiel und fügen wir den Einstellungen im securityContext eine einzelne Capability hinzu: CAP_MKNOD.
Wie dieses Beispiel zeigt, ist nun nur diese eine Funktion aktiviert.
Das Prinzip der geringsten Berechtigungen im Kubernetes-securityContext
In diesem Beitrag haben wir gesehen, wie Funktionen in Containern arbeiten, wie sie im Kubernetes-securityContext konfiguriert und von der Container-Runtime gesteuert werden.
Gemäß dem Prinzip der geringsten Berechtigungen sollten aus Sicherheitssicht nur die Funktionen bereitgestellt werden, die unser Container tatsächlich benötigt. Dieses Thema habe ich in einem früheren Beitrag behandelt. Dabei mag es überraschen, dass die meisten Prozesse überhaupt keine Kernel-Funktionen benötigen. Denn selbst wenn sie erweiterte Berechtigungen erfordern, lassen sich diese meist über Berechtigungen auf Dateiebene steuern.
Entfernen Sie zunächst alle Funktionen im securityContext und fügen Sie anschließend nur die benötigten hinzu. Fehler können Sie beheben, indem Sie die Ausgabe von Tools wie SELinux prüfen, um herauszufinden, welche Funktionen möglicherweise die Ursache sind. Denken Sie auch daran, dass Kubernetes-Container möglicherweise als root ausgeführt werden, sofern Sie keinen anderen Benutzer festlegen.
Einstellungen für Kubernetes-securityContext-Funktionen durchsetzen
Wenn Sie sicherstellen möchten, dass securityContext-Einstellungen wie Funktionen und die Ausführung als Nicht-root festgelegt sind, können Sie Admission Controller in Ihrem Kubernetes-Cluster verwenden. Damit stellen Sie sicher, dass Container nur mit den richtigen Sicherheitseinstellungen gestartet werden. Kubernetes verfügt über den integrierten PodSecurityPolicy-Controller, mit dem sich securityContext-Einstellungen durchsetzen lassen.
Beachten Sie jedoch, dass diese Funktion mit Version 1.21 veraltet sein wird. Stattdessen kommen extern gepflegte Projekte wie Open Policy Agent zum Einsatz.
Wir sollten außerdem Transparenz und die Behebung solcher Sicherheitsprobleme direkt in unseren Entwicklungsprozess integrieren. Snyk kann Ihre Kubernetes-YAML-Dateien scannen, unsichere securityContext-Einstellungen für Funktionen und andere Konfigurationen erkennen und direkt in Entwickler-Workflows Empfehlungen zur Behebung bereitstellen. Diese Funktion ist über die Snyk CLI verfügbar und lässt sich außerdem direkt in Quellcodeverwaltungssysteme und Continuous-Integration-Systeme integrieren:

Möchten Sie diese Funktion selbst ausprobieren? Erstellen Sie noch heute ein kostenloses Konto!
Starten Sie mit Capture the Flag
Erfahren Sie in unserem virtuellen On-Demand-Workshop für Einsteiger, wie Sie Capture-the-Flag-Herausforderungen lösen.
