Kubernetes ist inzwischen der Standard um sich unabhängig von public Cloud Anbietern wie AWS oder Azure zu machen, um nur zwei zu nennen. Kubernetes bietet die Möglichkeit innerhalb eines Unternehmens eine private Cloud zu etablieren, um zu verhindern Daten oder Applikationen auf Systemen externer Dienstleister zu hosten. Damit hat man den kompletten Betrieb unter eigener Sicherheitskontrolle.
Per se erhöht die Nutzung einer privaten Cloud mit Kubernetes, im Vergleich zur Nutzung einer öffentlichen Cloud, aber nicht einen Sicherheitsstandard. Auch eine AWS Umgebung kann, richtig konfiguriert, privat sein, während ein Kubernetes auf der eigenen Infrastruktur bei unaufmerksamer Konfiguration Sicherheitslecks hat, die Server, Applikationen und Systeme sehr öffentlich machen.
Die Konfiguration von Kubernetes verdient vor allem deswegen an Aufmerksamkeit, wenn man sich die Möglichkeiten anschaut, die einem Kubernetes unterhalb des Offensichtlichen auch per-default anbietet. Wir konnten alle hier beschriebenen Verhalten in unserem eigenen Test-Cluster nachstellen.
Das Bundesamt für Sicherheit in der Informationstechnik (BSI), hat zu diesen Themen in deren Grundschutzkompendium unter “APP 4.4 Kubernetes” eine Liste von Hinweisen erstellt, die bei einem Betrieb zu beachten sind. Auch wir haben uns daran orientiert, um unseren eigenen Service zur Validierung der Sicherheit eines Kubernetesclusters zu entwickeln.
Themen der DevSecOps bei Kubernetes sind:
Privilegierter Zugriff auf die Host-Server von einem Pod
Es ist einfach über eine Kubernetes-Pod auf einen Host-Server auch mit privilegierten Root-Rechten zuzugreifen. Auch in einem Multi-Tenancy Betrieb, in dem Teams auf eigene Kubernetes-Namespaces beschränkt sind, ist dies per-default möglich.
Damit kann im Prinzip auf die Kommunikation aller auf dem Server laufenden Applikationen zugegriffen werde oder auch der Server an sich manipuliert werden.
Wir haben das auf unseren Test-Cluster mit einem einzelnen, einfach zu konfigurierenden Pod nachgestellt und unsern Test-Server so “gehackt”.
Einfache Einrichtung eines privilegierten Zugriffs
Die Vergabe von privilegierten Rechten in Pods erfolgt nicht über RBAC-Richtlinien, sondern über die Konfiguration des Pods selbst. Per-default kann man auch mit beschränkten RBAC Rechten über eine Änderung an einen eigenen Pod privilegierten Zugriff auf einen Server-Node erlangen.
Unbeschränkte Kommunikation zwischen den Pods
Per-Default wird die Kommunikation zwischen den Pods von den Container Network Interface (CNI) wie Calico oder Cilium innerhalb eines Kubernetes-Clusters nicht beschränkt.
Die Kommunikation zwischen den Pods ist möglich,
- egal, ob die Pods auf unterschiedlichen Nodes laufen,
- egal, ob diese in unterschiedlichen Namespaces laufen,
- egal, ob diese über einen Kubernetes-Service verfügen oder nicht.
Ungesicherte Kommunikation zwischen den Pods
Für die Control-Plane von Kubernetes ist eine TLS-gesicherte Kommunikation mit HTTPs zwischen den Komponenten der Control-Plane, wie die zwischen der Api und den Kubelets, gesetzt. Für die Kommunikation zwischen den Kubernetes-Pods über das Overlay Network ist der Default aber eher eine ungesicherte Kommunikation ohne SSL/TLS.
Gerade wenn eine TLS-Terminierung für eingehende Requests am IngressController erfolgt, ist die Kommunikation zu einem Pod und zwischen den Pods per-default ungesichert.
Da ein Kubernetes-Cluster aus verschieden Node-Servern besteht, die über ein überlagertes Netzwerk miteinander verbunden sind, findet zwischen den Pods über das Netzwerk eine ungesicherte Kommunikation statt, immer mit der Gefahr des Abgreifens von unverschlüsselter Informationen, wie Passwörter.
Zugriff auf erweiterte Rechten über die Pods
Pods haben immer Kubernetes-ServiceAccounts angebunden, auch wenn diese in einem Deployment o.ä. nicht explizit angegeben sind. Jeder Namespace in Kubernetes verfügt beispielsweise über einen default-ServiceAccount in Kubernetes. Ein ServiceAccount verfügt über eigene Berechtigungen in Kubernetes.
Damit wird einer Applikation auf einem Pod die Möglichkeit gegeben, innerhalb von Kubernetes Änderungen durchzuführen, wie es beispielsweise bei MutatingAdmissionWebhooks notwendig ist, damit diese überhaupt funktionieren.
Die Authentifizierungstoken sind als Dateien auf einem Pod abgelegt und können von dort auch von jedem mit Shellzugriff kopiert werden. Mit dem Token ist es dann relativ einfach, sich selbst die Rechte dieser Pods zu geben, man muss diese einfach nur in die eigene Kubernetes-“Config” eintragen.
Rechtevergabe um Sicherheitsrichtlinien zu verwässern
Im Prinzip können die RBAC-Berechtigungen (egal ob eigene oder die für die Pods) auch dafür genutzt werden, eigene, die anderer oder die Berechtigungen von Pods zu ändern. Somit ergibt sich die Möglichkeit, sich selbst, jemand anderem oder einem Pod mehr Rechte einzuräumen, als gewünscht.
Damit stünde die Möglichkeit offen, Sicherheitsrichtlinien zu verwässern oder auch sich über einen Pod privilegierten Zugriff auf einen Cluster-Node zu verschaffen.
Mix aus Control-Plane und Worker Nodes
Kubernetes erlaubt es, auf einem Server einen Node der Control-Plane mit ETCD, ApiServer, Scheduler, und Controller-Manager und einen Node eines Workers mit Kubelet und Proxy zu betreiben. Per-default wird so über normale Pods ein Vollzugriff auf die Control-Plane von Kubernetes mit allen Möglichkeiten der Manipulation von Kubernetes gegeben.
Zudem können in den Container auch die Schlüssel und Zertifikate aus der Public Key Infrastruktur (PKI) für eine gesicherte und authentifizierte Kommunikation zwischen den Komponenten der Control-Plane (z.B. zw. Api und ETCD) abgelegt sein, auf die man dann damit Zugriff gewährt.
Container können die Host-Server überlasten
Die Pods, oder besser die in den Kubernetes Pods zusammengefassten Container, haben keine von Kubernetes oder der ContainerRuntime (CRI) vorgegebenen Ressourcenbeschränkung wie CPU oder Memory. Per-default stehen allen Container immer die gesamten Ressourcen der Worker-Nodes zur Verfügung. Damit besteht immer die Möglichkeit einen Host zu überladen.
Wenn geplant ist auf einem Worker-Nodes ein Macro-Service, wie eine Datenbank, laufen zu lassen, muss der Worker-Node immer über ausreichend Ressourcen verfügen, um eine Ausfallsicherheit zu gewährleisten. Dabei ist zu beachten, dass dann für andere Pods keine Ressourcen mehr zur verfügung stehen. Ein Update eines Macro-Containers mit eventuellem Wechsel eines Nodes während eines updates würde eine umfangreiche Verschiebung von Pods im Kubernetes-Cluster bedeuten.
Auch die Möglichkeit der Ressourcen-Limitierung in einem Container führt nicht dazu, dass ein Pod weniger Ressourcen beantragt, sondern nur, dass die Ressourcen von der CRI verzögert zur Verfügung gestellt wird, also der Pod langsamer wird.
Wir validieren Kubernetes zum Schutz ihres Kubernetes-Clusters
Wir haben ein Tool geschrieben, mit dem wir mit Ihnen die Ressourcen wie Kubernetes-Pods in Ihrem Kubernetes-Cluster auf Sicherheitslücken hin validieren können (Link).