Seite wählen

Kubernetes 101: Wie sichere ich Kubernetes ab?

von | Okt 9, 2023 | Cloud Native, Kubernetes

This entry is part 6 of 7 in the series Alles rund um Kubernetes

In meinen bisherigen Blogposts habe ich dir bereits einige wichtige Aspekte von Kubernetes näher gebracht. Von „Was ist Kubernetes“ über „Wie richte ich Kubernetes auf Linux ein“ zu „Wie installiere ich eine Anwendung in Kubernetes“. Im besten Fall hast du nun bereits Erfahrung in einer laufenden Umgebung sammeln können und bist bei der sogenannten „Day 2 Operation“, also dem alltäglichen, laufenden Betrieb angekommen. Eine Sache, die ich bisher allerdings ein wenig vernachlässigt habe, ist die Absicherung eines Kubernetes Clusters und der darin befindlichen Workloads.
In diesem Punkt ist Kubernetes‚ offener Standard Fluch und Segen gleichzeitig: Während die einfach erweiterbare Funktionalität ein großes Plus von Kubernetes ist, ist das Fehlen (fast) jeglicher Konfiguration von Security und Policing „out of the box“ definitiv ein großes Minus.
Deswegen zeige ich dir in diesem Beitrag, wie du in diesem Bereich nachbesserst!

Wie ist Kubernetes aufgebaut?

Wenn du meine bisherigen Blogposts verfolgt hast (oder bereits Erfahrung mit Kubernetes gesammelt hast) weißt du: Kubernetes ist ein verteiltes System.
Auf mehreren Nodes werden containerisierte Workloads geplant, die dann die eigentlichen Anwendungen ausführen. Hinzu kommen je nach Kubernetes-Distribution mehrere lokal auf dem jeweiligen Node laufende Services (bspw. Kubelet, kube-apiservercontainerd, etc.) sowie Container, die aufgrund ihrer Aufgaben manchmal erweiterte Privilegien auf den Nodes genießen. Etwa um den clusterinternen Netzwerkverkehr zu regeln oder im Cluster verteilten Speicher bereitstellen zu können.
Es gibt also eine einigermaßen klare Trennung zwischen Clusterinfrastruktur und Workloads, die in einer idealen Welt auch nur wenig miteinander zu tun haben (sollten). Eine Anwendung sollte schließlich in der Lage sein, ordnungsgemäß laufen zu können, ohne dass sie Kenntnisse über ihre Laufzeitumgebung (in diesem Fall Kubernetes) besitzt.
Wie klar diese Trennung in der Realität auch ausfallen mag, im Folgenden werde ich die zwei Welten und mögliche Ansatzpunkte für mehr Sicherheit getrennt voneinander betrachten.

Clustersicherheit bei Kubernetes

Wie bereits eingangs erwähnt, ist ein Kubernetes Cluster nach seiner Installation nicht unbedingt umfassend abgesichert – das beginnt bereits beim Zugriff auf das Kubernetes Cluster. Die kubeconfig, mit der du als Admin normalerweise arbeitest, ist mit maximalen Privilegien ausgestattet und sollte unter keinen Umständen verteilt oder von anderen Nutzern verwendet werden.
Deshalb solltest du weiteren User:innen einen alternativen Clusterzugriff (und damit verknüpfte Regeln) einrichten.

Zugriff und Zugriffskontrolle einrichten

Von Haus aus besitzt Kubernetes ein feingranular einstellbares RBAC-System (Role-based Access Control), mit dem sich grundsätzlich gut arbeiten lässt: Mittels (Cluster-)Roles, (Cluster-)RoleBindings und Usern bzw. ServiceAccounts lassen sich Verknüpfungen zwischen beliebigen Sets an Privilegien und einzelnen, im Cluster agierenden, Identitäten herstellen.
Für jede Gruppe an existierenden API-Objekten können durch Verbs die Privilegien entweder auf Namespace– oder auf Cluster-Ebene eingestellt werden. Die verfügbaren Verbs sind getlistcreateupdatepatchwatchdelete, und deletecollection und implizieren verschiedene Privilegien für die verknüpften API-Objekte.

Möchtest du eine hierarchische Struktur mehrerer Rollen erreichen, kannst du mehrere Clusterrollen zu einer übergeordneten Clusterrolle aggregieren. Auf diese Weise behältst du den Überblick, auch wenn die Anzahl an verfügbaren Rollen mit der Zeit wachsen sollte.
Überblick ist zudem ein gutes Stichwort, denn ab einer gewissen Anzahl an Rollen, Nutzern und Verknüpfungen zwischen den beiden Konzepten leidet unweigerlich die Übersichtlichkeit des Berechtigungskonstrukts. Zwar liefert Kubernetes‘ CLI kubectl mit dem Cmdlet auth can-i eine Möglichkeit des Auditings mit (siehe Auflistung aller Berechtigungen des Standard-Adminusers für den Namespace default unten), das Gelbe vom Ei ist dieser Ansatz der Nutzerverwaltung ab einer gewissen Clustergröße nicht mehr.

Oder möchtest du regelmäßig für alle User deines Clusters manuell die Berechtigungen prüfen?

kubectl auth can-i --list --namespace=default
Resources                                       Non-Resource URLs   Resource Names   Verbs
*.*                                             []                  []               [*]
                                                [*]                 []               [*]
selfsubjectaccessreviews.authorization.k8s.io   []                  []               [create]
selfsubjectrulesreviews.authorization.k8s.io    []                  []               [create]
                                                [/api/*]            []               [get]
                                                [/api]              []               [get]
                                                [/apis/*]           []               [get]
                                                [/apis]             []               [get]
                                                [/healthz]          []               [get]
                                                [/healthz]          []               [get]
                                                [/livez]            []               [get]
                                                [/livez]            []               [get]
                                                [/openapi/*]        []               [get]
                                                [/openapi]          []               [get]
                                                [/readyz]           []               [get]
                                                [/readyz]           []               [get]
                                                [/version/]         []               [get]
                                                [/version/]         []               [get]
                                                [/version]          []               [get]
                                                [/version]          []               [get]

 

Eine bessere Verwaltungsmöglichkeit für unsere Kubernetes Cluster User ist also dringend angeraten, sowohl aus Gründen der Übersichtlichkeit als auch der Sicherheit. Bei immer mehr Nutzern und komplexeren Berechtigungsstrukturen ist es sonst nur eine Frage der Zeit, bis bei einem Nutzer die falschen Berechtigungen gesetzt werden. Schaut man sich das Angebot an verfügbaren Lösungen für Zugriffskontrolle auf Kubernetes an, stechen ein paar Projekte heraus:

Als SUSE Partner setzen wir bei mehreren Kunden erfolgreich Rancher ein, ein Open-Source Tool zur ganzheitlichen Verwaltung von Clustern in der Cloud und on premise. Rancher setzt mit seinem Berechtigungskonzept direkt auf Kubernetes‘ RBAC-Modell auf und erweitert es um Projekte, die eine Abstrahierung um einen oder mehrere Namespaces bilden.
Das Web-Frontend bietet die Möglichkeit, (Cluster-)Rollen mit einzelnen Usern oder Usergruppen aus der in Rancher integrierten Benutzerverwaltung zu verknüpfen, zu auditieren und sich einen schnellen Überblick darüber zu verschaffen, welcher User in welchem Cluster welche Berechtigungen hat.

Grundlage für das Berechtigungskonzept sowohl in Rancher’s WebUI als auch in den verwalteten Clustern bieten die vielen verschiedenen Authentifizierungsplugins: Rancher unterstützt LDAP (z.B. Active Directory, OpenLDAP, AzureAD), OAuth (z.B. Google, GitHub), OIDC (z.B. Keycloak) und SAML (z.B. Okta, Shibboleth, ADFS).

Ein weiteres bewährtes Tool zur Zugriffskontrolle für u.A. Kubernetes ist Teleport, das ebenfalls eine Open-Source Software ist und kostenlos genutzt werden kann. Teleport fungiert als vorgelagerter Proxy, der die Anfragen an das Cluster terminiert, den Nutzer authentifiziert und authorisiert und diese Anfragen dann weitergibt. Stärken von Teleport sind zum Beispiel Policy-as-Code, umfangreiche Auditierbarkeit aller Zugriffe und vergangenen Sitzungen sowie die Kompatibilität nicht nur mit Kubernetes, sondern auch anderen gemanagten oder selbst betriebenen Services und Cloudressourcen.

Netzwerkabsicherung bei Kubernetes

Die Zugriffskontrolle für Endnutzer und Serviceaccounts, also handelnde Identitäten, haben wir nun abgearbeitet. Doch wie steht es mit Zugriffsversuchen, die von Services inner- und außerhalb des Clusters stammen und anderweitig beschränkt werden müssen? Schließlich besteht je nach Architektur und genutzten Dritt-Tools selbst ein „leeres“ Cluster aus (bis zu) mehreren hundert Containern, die untereinander kommunizieren (wollen), sich Updates aus dem Internet ziehen oder andere Services innerhalb der Infrastruktur deines Unternehmens (z.B. AD-Server) erreichen müssen.

Per Default können all diese Container das auch erst einmal ungestört. Kubernetes besitzt out-of-the-box kein Konzept einer „Firewall“ oder ähnlicher Maßnahmen, die den Netzwerkverkehr einschränken könnte. Weder in Ost-West, noch in Nord-Süd-Richtung, übrigens. Hierfür bedarf es eines Container Network Interfaces (CNI), das in der Lage ist, NetworkPolicies zu interpretieren und umzusetzen.
NetworkPolicies sind ein natives Konzept der Kubernetes-API, deren Umsetzung aber an externe CNIs übergeben wird. Ohne passendes CNI also keine Einschränkung des Netzwerkverkehrs.

Das Angebot an verfügbaren CNIs ist recht groß, beliebte Lösungen mit Support für NetworkPolicies auf Namespaceebene und teilweise einer ganzen Bandbreite weiterer Features (BGP-Routing, Nodefirewall, Networkpolicies auf Clusterebene) sind bspw. Cilium bzw. Calico.

„Best Practices“ für das Einrichten von Networkpolicies in Kubernetes gibt es wie Sand am Meer. Welche Blaupause für dein Unternehmen sinnvoll und geeignet sein könnte, ist meist eine Einzelfallentscheidung. Ein oft gesehenes Muster ist eine „globale“ NetworkPolicy über alle Namespaces hinweg (das CNI muss dieses Feature mitbringen!), die keinerlei Ingress bzw. Egress bis auf Anfragen an den clusterinternen DNS-Service erlaubt.
Auf diese Weise sperrst du sämtlichen Netzwerkverkehr, der nicht innerhalb eines einzigen Namespaces stattfindet und etablierst ein „Opt-in“-Modell, bei dem die Operatoren der Anwendungen bei Deployment die benötigten Networkpolicies mitdeployen müssen. Einziges Caveat hierbei ist, dass eventuell bereits vorhandene Infrastruktur im Cluster natürlich ebenfalls von den Regelungen betroffen ist.  Es ist deshalb ratsam, dass also ein etwas genauerer Namespace-Filter als „alle Namespaces“ zum Einsatz kommt.

Ressourcenverwaltung in Kubernetes

Ein weiterer wichtiger Punkt neben Zugriffskontrolle und Netzwerkpolicies ist das Verwalten der im Cluster vorhandenen Ressourcen. Der Grund hierfür ist einfach – hat ein Pod die Möglichkeit, alle Ressourcen eines Nodes für sich zu beanspruchen, nimmt er allen anderen Workloads auf diesem Clusternode buchstäblich die Luft zum Atmen. Doch nicht nur „herkömmliche“ Ressourcen wie Arbeitsspeicher und CPUs sollten reguliert sein – auch abstraktere Dinge wie PIDs oder Sockets können zum Problem werden – die in Pods laufenden Container befinden sich schließlich nach wie vor in Namespaces des jeweiligen Nodes. Spielt ein Container verrückt, kann das unangenehme Nachwirkungen für den gesamten Node nach sich ziehen. So kann beispielsweise mit einer Fork Bomb ein Denial of Service (DoS) erreicht werden.

Fälle wie oben beschrieben gilt es natürlich zu unterbinden – für Arbeitsspeicher und CPUs lässt sich das entweder auf Container– oder Namespaceebene umsetzen: Für Container innerhalb eines Pods können unterhalb des Felds resources sowohl requests als auch limits gesetzt werden. Was hier konkret eingetragen werden sollte, um einen möglichst reibungslosen Betrieb bei möglichst kompletter Auslastung eines Nodes durch die dort geschedulten Workloads herzustellen, ist eine seit Jahren diskutierte Frage. Die inzwischen häufigste Antwort lautet:

Für CPUs sollten Requests, aber keine Limits gesetzt werden. Für Arbeitsspeicher sollten Requests==Limits gesetzt werden.

Die Gründe für diese Richtlinie mögen etwas unklar sein, ohne tiefer darauf einzugehen, deswegen verlinke ich an dieser Stelle zwei Blogposts – einen zum Thema CPU-Limits und einen zu Memory-Limits, inklusive lustiger Vergleiche 😉.

An dieser Stelle neben den gängigsten Verwaltungseinstellungen für CPUs und Arbeitsspeicher auf all die anderen zu berücksichtigenden Stellschrauben für sichere Workloads in Kubernetes einzugehen, würde den Rahmen dieses als Überblick gedachten Posts sprengen – deswegen hier nur eine kurze Auflistung einiger Dinge, die man im Hinterkopf behalten sollte:

  • PID Limits für Pods, Nodes und PID-based eviction
  • (un)privilegierte Container
  • Sicherheitskontexte für Container
  • zusätzliche Policies (bspw. Nutzung von ausschließlich signierten Images)
  • Handhabe von Secrets (bspw. Verschlüsselung at rest)

Nodesicherheit sicherstellen

Neben den einzelnen Workloads und Netzwerkverkehr im Cluster muss man natürlich auch die Nodes, aus denen das Cluster besteht, ordentlich absichern. Insbesondere gilt es, die Nodes des Controlplanes, auf denen typischerweise auch etcd mitläuft, abzusichern – Zugriff auf etcd ist gleichzusetzen mit unbegrenztem schreibenden Zugriff auf das Cluster selbst.

Für die Absicherung von Nodes können klassische Technologien wie lokale Firewalls, SELinux oder AppArmor und beliebige weitere Härtungsmaßnahmen zum Einsatz kommen. Doch auch hier gibt es einige Cloud-Native-Technologien, die helfen können:
So unterstützen einige CNIs das Einrichten von Networkpolicies auf Node-Level. Mit nur einem Tool kann also nicht nur Netzwerkverkehr im Cluster reguliert werden, sondern in erweitertem Rahmen auch schon auf den vorgelagerten Nodes selbst.

Darüber hinaus gibt es einige Tools, die in der Lage sind, die komplexen Abläufe, die im Kontext von Kubernetes im Kubelet, den Pods und der Containerlaufzeitumgebung vor sich gehen, zu durchleuchten und ungewünschtes Verhalten zu unterbinden. Oftmals kommt hierbei eBPF zum Einsatz, eine Technologie, die die modulare Erweiterung des Betriebssystemkernels mit kleinen Programmen ermöglicht.

Anwendungssicherheit erhöhen

Ist das Cluster erst einmal abgesichert, kann man sich den darauf betriebenen Anwendungen widmen – auch wenn einige Mechanismen aus dem vorherigen Abschnitt bereits greifen sollten, beispielsweise Netzwerkpolicies und Ressourcenverwaltung.
Damit ist es jedoch in vielen Organisationen nicht getan – vielleicht gibt es Richtlinien, welche Images genutzt werden dürfen, aus welchen Registries sie bezogen werden sollen, ob gewisse Scans laufen müssen oder ob gewisse Signaturen erforderlich sind. Und übrigens dürfen auch anfangs grob geschätzte Ressourcenlimits anhand der tatsächlichen Leistungsdaten angepasst werden 😉.

Für viele dieser Zwecke gibt es Werkzeuge, die das Einrichten individueller Policies im Cluster ermöglichen. Die am weitesten verbreiteten sind zum Einen der Open Policy Agent und zum Anderen Kubewarden. Mit solchen Tools lassen sich verschiedenste Policies entsprechend den Vorgaben eurer Organisationen und Prozesse definieren die -as-Code vorliegen, versioniert und deployed werden können.

Für das Scannen von Workloads und deren Konfiguration gibt es ebenfalls etablierte Lösungen zum Beispiel Trivy oder Kubescape.
Beide Lösungen ermöglichen die Integration von CI/CD-Pipelines, um Sicherheitsrisiken und Misskonfigurationen noch vor Deployment zu bemerken und darauf reagieren zu können.
Trivy bietet darüber hinaus einen Operator, der in regelmäßigen Abständen aus dem Cluster herausselbstständig nach Sicherheitslücken suchen kann.

Für das Finetuning von Ressourcenrequests oder -limits benötigst du hingegen in erster Linie Metriken. Diese können aus verschiedenen Quellen stammen, die sich unter dem Begriff Observability zusammenfassen lassen. Mit Hilfe von Prometheus können so zum Beispiel Performancedaten wie genutzter Arbeitsspeicher, CPU-Last, Netzwerkverkehr auf Anwendungsebene nachvollzogen werden, sofern die Anwendung entsprechend instrumentalisiert wurde.
Mithilfe dieser Daten lassen sich dann Rückschlüsse ziehen ob wir mit unseren initial gewählten Ressourcerequests/limits zu großzügig oder doch eher zu optimistisch umgegangen sind und nun nachbessern.

Ein weiteres Tool zur Erkennung von zu lockeren oder fehlenden Ressourcerequests/limits ist KRR (Kubernetes Resource Recommender), das ebenfalls anhand von Prometheus-Daten die bestehenden Konfigurationen für Pods analysiert und Verbesserungen vorschlägt.

Zu voll umfänglicher Anwendungssicherheit gehört jedoch auch die Absicherung und Überwachung der CI/CD-Pipeline mit der die Anwendung erstellt bzw. ausgeliefert wird. Hier empfiehlt sich die Orientierung an Frameworks wie SLSA (Supplychain Levels for Software Artifacts), eine von Google veröffentlichte Sammlung an Empfehlungen für Software-Entwickler, wie und wo sie ihre Anwendungen härten sollten.
Zusätzlich lassen sich Anwendungen auch im Betrieb in Kubernetes (automatisiert) überwachen. So erlauben Tools wie NeuVector, in Containern ausgeführte Prozesse und versuchte/erfolgte Netzwerkaufrufe zu protokollieren, gewünschtes Verhalten zu „erlernen“ und nicht vorgesehene Aufrufe in Echtzeit zu unterbinden.

Fazit

Am Ende eines weiteren Blogposts in unserer „Kubernetes 101“ Reihe angekommen stellen wir einmal mehr fest: Kubernetes ist komplex.

Eine Vielzahl an Tools können in Sachen Security in Betracht gezogen haben und welcher „Stack“ letzten Endes für eure Anforderungen ausreichend und passend ist muss sich oft erst einmal herausstellen. Nicht zur Diskussion steht, dass eben aufgrund dieser Komplexität eine voll umfängliche Absicherung des Clusters als Plattform und der darin laufenden Anwendungen (ab dem Moment ihrer Entwicklung) oberste Priorität haben sollte. Denn ist erst einmal „der Wurm drin“ kann es ansonsten schwierig werden, Sicherheitslücken klar zu benennen, „den Schuldigen“ zu finden und das Ausmaß der Kompromittierung einzuschätzen.

Sollte dir dieser Blogpost gefallen haben, schau doch gerne noch die anderen in dieser Serie erschienenen Artikel an.
Und solltest du auf den Geschmack gekommen sein, aber an manchen Stellen noch Hilfe in Sachen Kubernetes brauchen, schau entweder einmal bei unseren Trainings zum Einstieg in Kubernetes vorbei oder buche direkt Consulting zu (fast) Allem rund um Kubernetes bei mir oder meinen Kolleg:innen.

Daniel Bodky
Daniel Bodky
Platform Advocate

Daniel kam nach Abschluss seines Studiums im Oktober 2021 zu NETWAYS und beriet zwei Jahre lang Kunden zu den Themen Icinga2 und Kubernetes, bevor es ihn weiter zu Managed Services zog. Seitdem redet und schreibt er viel über cloud-native Technologien und ihre spannenden Anwendungsfälle und gibt sein Bestes, um Neues und Interessantes rund um Kubernetes zu vermitteln. Nebenher schreibt er in seiner Freizeit kleinere Tools für verschiedenste Einsatzgebiete, nimmt öfters mal ein Buch in die Hand oder widmet sich seinem viel zu großen Berg Lego. In der wärmeren Jahreszeit findet man ihn außerdem oft auf dem Fahrrad oder beim Wandern.

0 Kommentare

Einen Kommentar abschicken

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Mehr Beiträge zum Thema Cloud Native | Kubernetes

NWS-ID for your Managed Kubernetes!

First Cloud, now Kubernetes - the integration of NWS-ID with our portfolio continues! As a next step, we will merge your Managed Kubernetes clusters with NWS-ID. Credit goes to Justin for extending our cluster stack with OpenID-Connect (OIDC), the base for NWS-ID! How...