Das ist unter anderem so, weil Container schlussendlich normale Prozesse sind, die auf einem Linux-Host laufen. Sie sind nicht virtualisiert, sondern nur isoliert. Erreicht wird das durch Funktionen des Linux Kernels zur Isolation von Prozessen, wie Kernel-Namespaces, Chroots, uid_map, gid_map oder cgroups. Dennoch verwenden Container, trotz Isolation, direkt den Kernel des Hosts. Sollte der in einem Container ausgeführte Code also bösartig oder kompromittiert worden sein, könnte er zum Beispiel Schwachstellen des Kernels ausnutzen, um sich mehr Privilegien als erlaubt zu verschaffen.

Ein Container ist aber auch eine Applikation, die zusammen mit ihrer Laufzeitumgebung ausgeliefert wird. Verwendet man dazu eine der üblichen Linux Distributionen, könnten auch dort Schwachstellen existieren. Je nachdem, welche Teile daraus von der eigenen Applikation wirklich verwendet werden, ist dies mehr oder weniger problematisch.

Verwendet man den Container Manager Kubernetes, kommen weitere Dinge hinzu, die im Kontext Sicherheit betrachtet werden müssen. Dies sind einerseits die Prozesse von Kubernetes selbst, andererseits aber auch die Konfiguration dessen, was man Container auf dem Hostsystem erlaubt und was nicht.

Allgemein lassen sich Fragen nach der Sicherheit auf einem Kubernetes Cluster in folgende Bereiche unterteilen:

Host / Kubernetes

Container

Applikation

Dieser Post soll eine kurze Übersicht über einige der wichtigsten Punkte geben.

K8s Process Security

Ein Kubernetes Cluster besteht aus 5 Prozessen (kubelet, kube-proxy, API Server, Scheduler, Controller Manager ) und einem ETCD Cluster. Die Nutzer kommunizieren mit dem API-Server über die CLI kubectl. Verwendet man keinen managed Cluster sondern setzt den Cluster selbst auf und/oder implementiert eine eigene Nutzerverwaltung, sollten folgende Punkte beachtet werden:

Role Based Access Control

Zur Autorisierung mit dem API-Server besitzt Kubernetes ein integriertes Role-Based Access Control (RBAC), das Benutzer oder eine Gruppe mit einer Reihe von Berechtigungen in Rollen zusammenfasst. Diese Berechtigungen kombinieren Verben (get, create, delete, update, …) mit Ressourcen (Pods, Ingress, Deployment, …) und können sich auf einen Namespace oder den gesamten Cluster beziehen. RBAC regelt nicht nur die Rechte der Nutzer, sondern auch die von PODs die, z.B. nach dem Operator bzw. Controller Pattern, mit dem API-Server kommunizieren.

Wann ist RBAC notwendig?

Ohne RBAC hat jeder Nutzer mit einer gültigen kubeconfig vollen Zugriff, d.h. Cluster-Admin Rechte, auf dem Cluster. Aber nicht nur das, denn jeder deployte POD im System hat dieselben vollen Rechte, wenn er mit dem API-Server kommuniziert und könnte andere PODs überwachen, dort z.B. weitere Prozesse starten oder Daten auslesen. Cluster ohne RBAC sind somit allenfalls für Demo- oder Testinstallationen empfehlenswert.

Network Policies

Eine Network Policy ist eine Spezifikation, wie Gruppen von Pods miteinander und mit anderen Netzwerk-Endpunkten kommunizieren dürfen. Dabei realisieren sie keine Firewall im klassischen Sinne auf Node Ebene, sondern auf logischer Ebene für PODs und Namespaces. Üblicherweise werden Network Policies durch das VLAN realisiert, dass bei der Installation des Clusters durch ein Netzwerk-Plugin bestimmt wird (z.B. Canal)

Eine sehr gute Übersicht über verschiedene UseCases und die Rezepte dazu bietet das Projekt Kubernetes Network Policy Recipes. Zum Beispiel lässt sich durch die folgende Policy der Netzzugriff von anderen Namespaces (z.B. “other”) auf alle PODs im Namespace “my-app” verhindern.

Network Policies
Network Policies

Wann sind Network Policies notwendig?

Kubernetes lässt sich um so gewinnbringender einsetzten, je weniger verschiedene Cluster man verwendet. Dadurch lassen sich Ressourcen, wie Hardware oder Personal für die Cluster-Administration am effektivsten nutzen. Folgt man diesem Ansatz, muss man die laufenden PODs vor Zugriffen schützen oder sie ggf. am Aufbau von Verbindungen nach aussen hindern.

Use Cases für den Einsatz von Network Policies können unter anderem sein:

Pod Security Policies

Container sind nichts anderes als auf einem Host laufende, allerdings isolierte, Prozesse. Was ein solcher Container darf und was nicht, wird wesentlich von den Einstellungen der Pod-Spezifikation (der YAML) bestimmt. So kann man dort einem Container Zugriff auf das Host-Dateisystem und sämtliche Devices geben. Da für eine solche YAML normalerweise der Softwareentwickler verantwortlich ist, kann man die Einstellungen dort nicht immer kontrollieren. Dies trifft insbesondere auf die Installation von fremden Komponenten per HELM Charts oder ksonnet zu.

Dem Zweck solche Deployments sicherer zu gestalten dienen die Pod Security Policies. Sie kontrollieren sicherheitsrelevante Aspekte der Pod-Spezifikation und definieren eine Reihe von Bedingungen (für eine gute Übersicht der Sicherheitsfunktionen des Linux Kernels siehe auch hier), unter denen ein Pod laufen muss, um in das System aufgenommen zu werden.

Realisiert wird das durch spezielle Pod Security Policy Ressourcen, die über Role- und Rolebinding-Objekte an Service Accounts gebunden werden. Damit werden die über die Service Accounts erstellten PODs durch Policies geprüft.

Pod Security Policies
Pod Security Policies

Eine Pod Security Policy erlaubt eine sehr umfangreiche und feingranulare Konfiguration der Möglichkeiten eines Containers, z.B. durch die folgenden Parameter:

Die wichtigsten Security Policies

Weitere Informationen zum Thema Pod Security Policies sind hier zu finden.

Wann sind Security Policies notwendig?

Erlaubt man einem Container uneingeschränktem Zugriff auf das Dateisystem eines Hosts, ergeben sich viele Möglichkeiten zur Eskalation von Privilegien. So können sensible Daten (z.B. Logs, Anmeldeinformationen etc.) von anderen Containern bzw. Prozessen oder des Hosts selbst ausgelesen werden. HostPath-Volumes generell auszuschließen oder Pfade zumindest zu whitelisten ist sicher eine gute Idee.

Ebenso wie das Host-Dateisystem, wird man die Devices des Hosts und das Host Network (PODs dürfen nur das VLAN des Network Plugins verwenden) schützen wollen. Dies nur spezifischen PODs zu erlauben, z.B. allen PODs im Namespace kube-system, ist immer empfehlenswert.

Viele existierende Container laufen als Root oder benötigen die Capabilities die Docker standardmäßig für seine Container hinzugefügt hat (siehe auch die Docker Dokumentation). Dazu gehören zum Beispiel NET_BIND_SERVICE (erlaubt das Binden eines Sockets an einen privilegierten Port unter 1024) oder SETUID (erlaubt das beliebige Manipulieren der Prozess UIDs). Wann es sinnvoll ist, einem Container eine Capability zu entziehen oder ihm einzelne hinzuzufügen, hängt natürlich stark von der Applikation ab, die betrieben werden soll. Generell gilt aber, daß weniger Capabilities immer besser sind als mehr davon. Pod Security Policies sind ein gutes Mittel einen ausgewählten Satz von Capabilities als verwendbar vorzuschreiben oder sie generell zu verbieten (wobei in diesem radikalen Fall, die meisten komplexeren Applikationen nicht mehr starten dürften).

Zusammenfassung

Kubernetes Prozesse untereinander abzusichern ist ebenso üblich, wie eine zuverlässige Authentifizierung (sei es über einfache Zertifikate, Token oder einen OIDC Server) oder Autorisierung (RBAC für Nutzer oder PODs) bezüglich des API-Servers sicherzustellen. Vergessen wird dabei jedoch oft, dass Container ihre Laufzeitumgebung beinhalten und es sich dabei um normale Prozesse handelt, die ohne Virtualisierung auf einem Hostsystem laufen. Nicht nur die Container Images müssen kontinuierlichen erneuert werden, sondern auch der Kernel bzw. die Distribution des Hosts, um Schwachstellen zu beseitigen.

Kubernetes (resp. Docker) erlaubt es die Rechte eines Containers in der POD-Spezifikation zu erweitern. Je weniger Kontrolle man als Verantwortlicher über die POD-Spezifikationen oder die deployten Container hat, desto mehr möchte man hier generelle Einschränkungen machen. Dazu sind Pod Security Policies eine recht einfache Möglichkeit das sicherzustellen.

Aber auch die Applikationen selbst müssen gesichert werden. Wer möchte schon, dass sich die Applikationen eines Test-Namespaces mit der Produktionsdatenbank unterhält oder dass die HTTP UI von Mandant A versehentlich auf den Backend Service von Mandant B zugreift. Allerdings, für jeden Mandanten, jedes Team, jedes Projekt, für jeden Test/QA/Staging einen eigenen Cluster zu verwenden ist nicht immer ein praktikabler und produktiver Weg. Die Network Policies erlauben es hier, die notwendigen Einschränkungen für Egress oder Ingress zu konfigurieren, um nicht notwendigerweise in den Aufwand mehrerer Cluster investieren zu müssen.

Docker und Kubernetes vollbringen keine Wunder und noch immer sind zum Thema Sicherheit einige Überlegungen zu investieren. Beide Tools bieten aber genügend Möglichkeiten Container und Cluster auf verschiedenen Ebenen so sicher wie möglich zu machen.