Bereits Kunde? Jetzt einloggen.
Lesezeit ca. 10 Min.

Sicher auf Kubernetes zugreifen: Fallen vermeiden


Linux Magazin - epaper ⋅ Ausgabe 2/2020 vom 02.01.2020

Kubernetes bringt ein ausgeklügeltes System mit, um über den API -Server sichere Zugriffe von Nutzern und Systemkomponenten zu gewährleisten. Der Artikel wirft einen Blick auf die Optionen zur Authentisierung, Autorisierung und Zugriffskontrolle.


Artikelbild für den Artikel "Sicher auf Kubernetes zugreifen: Fallen vermeiden" aus der Ausgabe 2/2020 von Linux Magazin. Dieses epaper sofort kaufen oder online lesen mit der Zeitschriften-Flatrate United Kiosk NEWS.

Bildquelle: Linux Magazin, Ausgabe 2/2020

© Jarts, photocase.com


Kubernetes ist der neue Platzhirsch beim Ausrollen von Cloud-Applikationen. Die Steuerung des Clusters erfolgt in der Regel direkt via API und über das Kommandozeilen-Frontend »kubectl«. Den Zugriff auf die Steuerkomponente »kube-apiserver« halten die Kubernetes-Macher dabei so einfach möglich.
Kubernetes besitzt ein sehr fein abgestuftes ...

Weiterlesen
epaper-Einzelheft 5,99€
NEWS 14 Tage gratis testen
Bereits gekauft?Anmelden & Lesen
Leseprobe: Abdruck mit freundlicher Genehmigung von Linux Magazin. Alle Rechte vorbehalten.

Mehr aus dieser Ausgabe

Titelbild der Ausgabe 2/2020 von Die Alleswisser. Zeitschriften als Abo oder epaper bei United Kiosk online kaufen.
Die Alleswisser
Titelbild der Ausgabe 2/2020 von News. Zeitschriften als Abo oder epaper bei United Kiosk online kaufen.
News
Titelbild der Ausgabe 2/2020 von Zahlen & Trends. Zeitschriften als Abo oder epaper bei United Kiosk online kaufen.
Zahlen & Trends
Titelbild der Ausgabe 2/2020 von Bericht von der VMworld 2019 in Barcelona: In Richtung Pazifik. Zeitschriften als Abo oder epaper bei United Kiosk online kaufen.
Bericht von der VMworld 2019 in Barcelona: In Richtung Pazifik
Titelbild der Ausgabe 2/2020 von Kernel 5.4: ExFAT in Staging, Lockdown-Modus: Neues Sperrgebiet. Zeitschriften als Abo oder epaper bei United Kiosk online kaufen.
Kernel 5.4: ExFAT in Staging, Lockdown-Modus: Neues Sperrgebiet
Titelbild der Ausgabe 2/2020 von Zwei-Faktor-Authentifizierung in der Praxis: Doppelt hält besser. Zeitschriften als Abo oder epaper bei United Kiosk online kaufen.
Zwei-Faktor-Authentifizierung in der Praxis: Doppelt hält besser
Vorheriger Artikel
Kernel 5.4: ExFAT in Staging, Lockdown-Modus: Neues Sperrgebiet
aus dieser Ausgabe
Nächster Artikel Zwei-Faktor-Authentifizierung in der Praxis: Doppelt hält be…
aus dieser Ausgabe

Kubernetes ist der neue Platzhirsch beim Ausrollen von Cloud-Applikationen. Die Steuerung des Clusters erfolgt in der Regel direkt via API und über das Kommandozeilen-Frontend »kubectl«. Den Zugriff auf die Steuerkomponente »kube-apiserver« halten die Kubernetes-Macher dabei so einfach möglich.
Kubernetes besitzt ein sehr fein abgestuftes Berechtigungssystem, das sich sowohl auf die Rechte der Administratoren und Benutzer auswirkt als auch auf die Optionen für den Zugriff auf die laufenden Dienste und Komponenten innerhalb des Clusters. Das machen sich auch Entwickler zunutze, die komplexere Services in Kubernetes abbilden wollen.
Rollt der Admin mit Kubeadm den ersten Testcluster aus, gibt es einen Superuser, dessen zertifikatsbasierten Zugang sich der Admin nach »~/.kube/config« auf den Host kopiert und mit dem alles möglich ist. Unglücklicherweise arbeiten nicht selten auch die in der Produktion eingesetzten Cluster noch so. Sicherheitsbeauftragte in stärker regulierten Branchen würden eine solche Installation vermutlich sofort abschalten. Der Artikel will etwas Licht ins Dunkel bringen und Alternativen aufzeigen.

Drei Schritte

Die offizielle Kubernetes-Dokumentation erweist sich tatsächlich als nützlicher Startpunkt [1], um zu verstehen, welche Konzepte zur Zugriffssteuerung Kubernetes mitbringt. Abbildung 1 zeigt die Komponenten und deren Zusammenspiel innerhalb eines Clusters. Alle Anfragen laufen über den API-Server und passieren drei Stationen: Authentisierung, Autorisierung und Zugangskontrolle.
Die Authentisierung prüft, ob Kubernetes den Benutzernamen mithilfe einer Authentisierungsmethode zu verifizieren vermag. Das kann eine Passwortdatei, ein Token oder ein Client-Zertifikat sein, wobei Kubernetes gewöhnliche Nutzer außerhalb des Clusters selbst verwaltet. Die Plattform erlaubt dabei mehrere Authentisierungsmodule und probiert diese so lange durch, bis eines die Authentisierung erlaubt oder alle fehlschlagen. War der erste Schritt erfolgreich, folgt die Autorisierung. Hier entscheidet sich, ob der authentisierte Benutzer aufgrund der Konfiguration das Recht erhält, Operationen auf einem Objekt auszuführen. Der Prozess klärt zum Beispiel, ob der Nutzer admin einen neuen Pod im Namespace »Development« anlegen darf, oder ob er für Pods im Namespace »Management« Schreibrechte besitzt.
Im letzten Schritt erfolgt schließlich die Zugangskontrolle. Dabei sehen die Zugangskontrollmodule auch in den Request selbst hinein. Zwar kann der User Bob das Recht besitzen, Pods im Namespace »Development« anzulegen, die aber beispielsweise maximal 1 GByte RAM belegen dürfen. Ein Request, der nach einem Container mit 2 GByte RAM verlangt, würde die ersten beiden Kontrollen passieren, aber an der Zugangskontrolle scheitern.

Auftrennung durch Namespaces

Ein Grundkonzept, das in Kubernetes eine weitere Klammer um alle anderen Ressourcen bildet, sind die Namespaces. Sie implementieren die Mandantenfähigkeit. Ohne besondere Berechtigungen, die der Admin im Bereich der Autorisierung festlegen müsste, sieht eine Komponente immer nur andere Komponenten im Bereich des eigenen Namespaces.

Möchte der Admin zum Beispiel mithilfe von Kubectl die laufenden Pods im Cluster auflisten, führt die Eingabe von »kubectl get pods« gewöhnlich zu einer leeren Ausgabe. Der Befehl fragt den Namespace »default« ab, in dem zunächst gewöhnlich kein Pod läuft.
Erst nach Zugabe der Option »-n kube-system « erscheint eine Ausgabe wie in Listing 1, die auch die Kubernetes-Komponenten selbst berücksichtigt, die im Namespace »kube-system « laufen. Um alle Pods sämtlicher Namespaces zu sehen, gibt der Admin Kubectl zusätzlich die Option »-A« mit auf den Weg.

Abbildung 1: Bei den Zugriffskontrollen unterscheidet Kubernetes kaum zwischen menschlichen Nutzern und Service-Accounts.


© https:// kubernetes.io/ docs/

Von Menschen und Diensten

Grundsätzlich unterscheidet Kubernetes kaum zwischen menschlichen Benutzern und Service-Accounts. Es behandelt beide nach der Authentisierung insofern gleich, als es für die Schritte zwei und drei (Autorisierung und Zugangskontrolle) keinen Unterschied macht, ob hans@test.com oder databaseservice etwas vom Cluster möchte. Kubernetes prüft Anfragen auf Basis von Rollenzugehörigkeiten und lässt sie passieren – oder auch nicht.
Über Service-Accounts kommunizieren in der Regel die Komponenten im Kubernetes-Cluster miteinander, redet also ein Pod zum Beispiel mit anderen Pods oder Services. Theoretisch könnte auch ein menschlicher Benutzer Service-Accounts verwenden. Ob das sinnvoll und gewollt ist, steht auf einem anderen Blatt. Service-Accounts legt der Admin über Kubectl und das Kubernetes-API an. Sie treten damit gewissermaßen als interne Nutzer des Clusters auf. Kubernetes liefert automatisch ein sogenanntes Secret mit, über das es die Anfrage authentisiert.

Zertifikat oder Token

Für User gibt es die Möglichkeit, sich mit einem X.509-Zertifikat anzumelden, das die Certificate Authority (CA) des Kubernetes-Clusters unterschreibt. Benutzername und Gruppenzugehörigkeiten zieht Kubernetes dabei aus dem »CommonName« des Zertifikats. So meldet »CN=hdampf/o=Gruppe1/ o=Gruppe2« den Benutzer hdampf mit den Gruppenzugehörigkeiten Gruppe1 und Gruppe2 an. Auch diese Methode läuft im Prinzip intern in Kubernetes, da der Admin die Zertifikatsanfragen entsprechend generieren und mit der Kubernetes-CA signieren muss.
Daneben besteht noch die Möglichkeit, ein sogenanntes TokenFile in folgendem Format zu verwenden:

Token,Username,Userid, “Gruppe1, Gruppe2”

Dazu muss der Admin allerdings den Kubernetes-API-Server umkonfigurieren. Er ergänzt die Aufrufparameter für »kube-apiserver« mit der Option »--token-auth-file=Pfad_zur_Datei«. Da jedoch der API-Server als Teil von Kubernetes ebenfalls im Container läuft, ist das Hinzufügen der Option nicht ganz trivial. Die Aufrufparameter der Kubernetes-Komponenten findet der Admin im Verzeichnis »/etc/kubernetes/manifests/«. Die Datei »kube-apiserver.yaml« (Abbildung 2) konfiguriert den zugehörigen Container. In dieser finden sich sämtliche Aufrufparameter als YAML-Liste. Hier trägt der Admin die Zeile ein und platziert die Token-Datei in einem über den Container erreichbaren Ordner.
Statt Token-Dateien kann der Admin auf dieselbe Weise eine Passwortdatei verwenden. Deren Syntax ist ähnlich, mit dem Unterschied, dass in der ersten Zeile das Passwort steht. Statt der Option »--token-auth-file=Pfad_zur_Datei« fügt der Admin die Option »--basicauth-file= Pfad_zur_Datei« hinzu. Der API-Server verwendet dann die HTTPBasic-Auth-Methode, und Clients müssen ihre Anfragen damit senden.
Beide Varianten haben den Nachteil, dass der Admin des Clusters die Passwörter und Tokens nur über die Datei ändern kann. Zudem erfordern sie einen Neustart des »kube-apiserver«-Containers. Den stößt der Admin entweder via Kubelet auf dem Master-Node an oder über einen Neustart des Pods mittels Kubectl.

Besser: OpenID

Als zeitgemäßer erweisen sich OpenIDTokens. Bei OpenID handelt es sich um eine Variante von OAuth 2.0 [2],

Abbildung 2: Eine Beispielkonfiguration für den »kube-apiserver« unter »/etc/kubernetes/manifests/«.


die etwa Microsoft und Google in ihren Public Clouds anbieten. Der Dienst verteilt Tokens, die Kubernetes akzeptiert, sofern der Admin es darauf vorbereitet hat. In diesem Fall übernehmen also Cloudanbieter die Authentisierung.
Eine weitere externe Methode sind sogenannte Webhooks. Hier schickt Kubernetes eine JSON-Anfrage mit einem Token an einen externen Dienst und erwartet von diesem eine Antwort. Die muss im JSON-Block den Benutzernamen, die Gruppenzugehörigkeit(en) und gegebenenfalls Extrafelder enthalten, wenn es denn solche gibt.
Nicht zuletzt darf im Pfad zwischen Benutzer und Kubernetes noch ein Authentisierungsproxy stehen, der die Webrequests um X-Header erweitert, etwa in folgender Weise:

X-Remote-User: hans
X-Remote-Group: Dev1

Ist die Funktion aktiviert, akzeptiert Kubernetes die Daten auf diesem Weg. Damit sich hier nicht einfach ein Angreifer dazwischenschummelt, weist sich der Proxy selbst mit einem Client-Zertifikat gegenüber Kubernetes aus.

Service-Accounts einrichten

Die einfachste Variante einer Authentisierungsmethode ist das Anlegen eines Service-Accounts, etwa über den Befehl aus der ersten Zeile von Listing 2. Er erzeugt einen neuen Service-Account namens testserviceaccount im Namespace »default«.
Der besitzt zwar noch keine Berechtigungen, das Kommando legt aber neben dem Account auch ein Secret an. Die Ausgabe von »kubectl get secret« enthält nun einen Eintrag wie in Abbildung 3. Dort zeigt sich das Secret für den neu angelegten Service-Account »testserviceaccount «.
Das kann der Admin nun über das Kommando aus der zweiten Zeile von Listing 2 näher in Augenschein nehmen (Zeile 3 bis 15). Der Eintrag unter »token:« (Zeile 15) dient zur Authentisierung. Ist das Kubernetes-Dashboard im Einsatz, kann der Admin dieses Zeichenwirrwarr kopieren und als Token zum Anmelden verwenden.
Allerdings sieht der Anwender im Dashboard noch nicht besonders viel, da noch sämtliche Berechtigungen fehlen. In »~/.kube/config« platziert der Admin daher unter dem Eintrag »users:« die Zeilen aus Listing 3.

Accounts für Menschen

Einen menschlichen Benutzer per Zertifikat zu authentisieren, funktioniert ähnlich. Zunächst erzeugt der Admin einen Certificate Signing Request (CSR). Dabei ist es wichtig, den Benutzernamen als »CommonName« (CN) zu hinterlegen und alle angestrebten Gruppenzugehörigkeiten über Organisationen abzubilden. Mit OpenSSL funktioniert das, wie in Listing 4 gezeigt.
Nun unterschreibt der Admin die Anfrage mit dem CA-Zertifikat und dem Schlüssel des Kubernetes-Clusters. Verwendet er das »ca«-Kommando von OpenSSL, muss er dazu meist die »openssl.cnf« anpassen, um im Standard eingestellte Pflichtfelder wie »country« zu entfernen.
Das Zertifikat samt Schlüssel packt der Anwender dann in seine Datei »~/.kube/ config«. An die Stelle des Felds »token:« beim Service-Account gehören in den User-Kontext nun die Felder »client-certificate-data « und »client-key-data«. Sie enthalten jeweils die Base64-kodierte Version des Zertifikats und des Private Keys.

Dabei gilt es, zu beachten, dass die YAML-Konfigurationsdatei dies in einer Zeile erwartet. Kommt OpenSSL beim Generieren des Zertifikats zum Einsatz, ist nur der Block zwischen »BEGIN CERTIFICATE« und »END CERTIFICATE« relevant und einzupacken.

Rollen und Autorisierung

Nun liegen zwar zwei Zugänge zum Cluster vor, die den anfangs erwähnten ersten Schritt der Authentisierung hinter sich haben, doch sie besitzen im Cluster noch keine Rechte. Daher geht die Konfiguration nun in den Bereich der Autorisierung über.
Das erste hier relevante Konzept ist die Rolle (»Role«), die das Ziel einer Aktion beschreibt. Zu den Attributen einer Rolle gehören:
■ der zugehörige Namespace, in dem die Rolle agiert,
■ die Funktion, die sie ausführt (Lesen, Schreiben, etwas Erzeugen und so weiter),
■ die Art der Objekte, auf die sie zugreift (Pods, Services), sowie
■ die API-Gruppen, zu denen sie gehört und die das Kubernetes API erweitern.
Neben der normalen Rolle, die innerhalb eines Namespaces gilt, gibt es auch »Cluster-Rollen (»ClusterRole«), die für den »gesamten Kubernetes-Cluster gelten. Rollen »verwaltet Kubernetes per API und damit auf der Kommandozeile mit Kubectl. YAML-Dateien beschreiben die Rollen. Listing 5 zeigt ein solches File, das es erlaubt, laufende Pods auszulesen. Will der Admin eine Cluster-Rolle anlegen, trägt er im Feld »kind« alternativ eine »ClusterRole« ein und löscht den »namespace«-Eintrag aus »metadata«. Nun speichert er seine Definition als »pod-reader.yml« und erzeugt dann die Rolle über »kubectl apply -f pod-reader. yml«. Auch hier muss er darauf achten, in welchem Namespace das erfolgt.
Nun stellt sich aber noch die Frage, wer denn mit den Rechten dieser Rolle arbeiten darf. Hier kommen die sogenannten RoleBindings« beziehungsweise ClusterRoleBindings ins Spiel. Sie ordnen Benutzer und Service-Accounts den vorhandenen Rollen zu.
Konkret stellt Kubernetes bei einem eingehenden API-Request im ersten Schritt fest, ob sich der User überhaupt authentifizieren darf. Ist das der Fall, dann versucht es, dem User anhand der »RoleBindings« eine Rolle zuzuordnen. Klappt auch das, erfährt Kubernetes auf Basis der gefundenen Rolle, über welche Rechte der Nutzer verfügt, und prüft dann, ob sich seine Anfrage im Rahmen des Erlaubten bewegt.
Um ganz konkret den angelegten testserviceaccount und den testuser mit der »pod-reader«-Rolle zu verknüpfen, erzeugt der Admin das »RoleBinding« aus Listing 6. Nach dem Anlegen dieses Bindings darf der testuser schließlich über »kubectl get pods« die Pods im Namespace »default« auslesen.

Abbildung 3: Über Kubectl (hier gezeigt in der Microk8s-Variante) lässt sich recht einfach ein Service-Account anlegen und anschließend das zu diesem erzeugte Secret abfragen.


Rollenverhalten

Will der Admin steuern, worauf ein User Zugriff erhält, schränkt er die Rolle ein. Manche Ressourcen sind zum Beispiel hierarchisch angeordnet. Soll der testuser nur Zugriff auf die Unterressourcen »logs« der Ressource »pods« erhalten, muss der Eintrag unter »resources« in der Rollendefinition in Listing 5 »“pods/ logs”« heißen.
Hat der Admin seinen Kubernetes-Cluster mit Kubeadm angelegt, gibt es eine »ClusterRole« namens »cluster-admin«, die vollen Zugriff erlaubt. Weist er diese Rolle einem Benutzer zu, erhält dieser sämtliche Zugriffsrechte.
Um zumindest auf der Ebene des kompletten Clusters eine Inflation von Einzelrollen zu vermeiden, gibt es unter den »ClusterRoles« aggregierte Rollen. In diesem Fall erhalten die einzelnen Rollen ein Feld »label« mit einem bestimmten Wert. Die aggregierte Rolle versammelt dann alle Rollen, die ein Label mit diesem Wert besitzen, und bildet so eine Vereinigungsmenge all dieser Rollen. Die so erzeugte »ClusterRole« lässt sich wieder im »ClusterRoleBinding« einsetzen, und ein ihr zugeordneter Benutzer bekommt dann alle ihr zugeordneten Rechte.
Neben der beschriebenen rollenbasierten Autorisierung gibt es noch die attributbasierte, die ein komplizierteres Regelwerk auf die API-Zugriffe der Kubelets anwendet. Der Node Authorizer bewertet zum Beispiel die Anfragen je nach Absender. Im Webhook Mode schickt Kubernetes alle API-Anfragen der Nutzer zunächst im JSON-Format an einen externen REST-Service, der dann mit »True« oder »False« antwortet.

Abbildung 4: Die Online-Dokumentation zum Admission Controller »PodSecurityPolicy « erklärt ausführlich die mit ihm verbundenen Attribute.


Zugriffskontrolle

Der letzte Aspekt besteht darin, die Inhalte einer Anfrage zu kontrollieren. Kubernetes liefert dazu eine lange Liste von Admission Controllern mit, die sehr unterschiedliche Angelegenheiten überwachen beziehungsweise steuern. Ohne Änderungen an der Konfiguration sind die Controller aus dem Kasten „Admission Controller“ aktiv. In der Kubernetes-Dokumentation findet sich eine Beschreibung, welcher Controller welche Logik implementiert und wie die Konfiguration dazu aussieht [3]. Einige der Plugins benötigen eine eigene Konfigurationsdatei oder einen YAMLImages

Block in der Konfiguration des Clusters. Ein interessantes Beispiel ist der »LimitRanger «, der die Ressourcen für einen Namespace begrenzt, etwa die CPU- und die Arbeitsspeichernutzung. Dieses Plugin modifiziert laufende Anfragen, um sie mit Standardwerten zu versehen. Das geschieht beispielsweise, wenn eine Pod-Definition keine Angaben darüber enthält, wie viel CPUs oder Arbeitsspeicher der Pod anfordern darf. So behält der Cluster-Admin die Kontrolle darüber, wie viele Ressourcen welcher Kunde respektive Namespace verbraucht.

Optional lässt sich zusätzlich ein Admission Controller namens Pod Security Policy einspannen [4]. Der erhöht die Sicherheit, indem er darauf pocht, dass vom System über Replica-Sets und Deployments erzeugte Pods bestimmte Sicherheitsrichtlinien einhalten. Dazu kann er unter anderem die Linux-Capabilities einschränken, SE-Linux-Kontexte und AppArmor-Profile für Container festlegen oder den Umgang mit Privilegien regulieren (Abbildung 4).

Fazit

Weil Kubernetes alle Operationen per API verfügbar macht, bietet es Admins maximale Flexibilität. Das Schema setzt sich auch in der Sicherheitsinfrastruktur fort. Das Ausrollen selbst komplexer Umgebungen mit dedizierten Sicherheitskonfigurationen lässt sich über YAML-Dateien einfach und klar regeln. Diese Tatsache entbindet den Entwickler allerdings nicht davon, sich zuvor Gedanken darüber zu machen, wie er diese Möglichkeiten sinnvoll nutzen will. Er erhält dann die Möglichkeit, an zentraler Stelle effektive Standards zu setzen.
Die im Artikel gezeigten Zugriffskontrollen bilden dabei lediglich den ersten Schritt in Sachen Sicherheit. Ein Augenmerk sollten Cluster-Admins zudem auf Integrität und Sicherheit der verwendeten YAMLImages legen: Sie können Sicherheitslücken enthalten und verwandeln sich dann in potenzielle Einfallstore für Rootkits. Auch die Möglichkeiten für Container-Benutzer, ihre Privilegien zu eskalieren, stellen ein permanentes Sicherheitsthema dar.
Was passiert in der Praxis, wenn das Deployment eines ausgerollten Projekts an den Sicherheitseinstellungen scheitert? Das unterscheidet sich nicht unbedingt von dem, was bei anderen Sicherheitskomponenten der IT (etwa Firewalls) in den letzten 20 Jahren passierte. IT-Sicherheitsverantwortliche müssen sich daher in die teils komplexen Sicherheitszusammenhänge von Kubernetes einarbeiten. Das Design erlaubt einen sicheren Betrieb, entbindet aber den Administrator nicht vom Nachdenken.

(kki)

Autor

Konstantin Agouros arbeitet als Head of Open Source & AWS Projects bei der Matrix Technology AG und berät dort zusammen mit seinem Team Kunden zu Open-Source-, Sicherheits- und Cloudthemen. Sein Buch „Software Defined Networking: Praxis mit Controllern und OpenFlow“ ist bei De Gruyter erschienen.

Infos

[1] Zugriffskontrolle in Kubernetes: [https:// kubernetes.io/docs/reference/access-authn-authz/controlling-access/]
[2] OAuth 2.0: [https:// oauth.net/2/]
[3] Admission Controller: [https:// kubernetes.io/docs/reference/access-authn-authz/admission-controllers/]
[4] PodSecurityPolicy: [https:// kubernetes.io/docs/concepts/policy/pod-security-policy/]

Admission Controller

■ NamespaceLifecycle
■ LimitRanger
■ ServiceAccount
■ TaintNodesByCondition
■ Priority
■ DefaultTolerationSeconds
■ DefaultStorageClass
■ StorageObjectInUseProtection
■ PersistentVolumeClaimResize
■ MutatingAdmissionWebhook
■ ValidatingAdmissionWebhook
■ RuntimeClass
■ ResourceQuota