Kubernetes Dashboard Login mit OpenID-Connect

Dev Diary

Das Kubernetes Dashboard ist ein beliebtes Tool, um die Ressourcen in einem Kubernetes-Cluster zu verwalten sowie deren Fehleranalyse und Fehlerbehebung. Doch wie kann man die Dashboard Authentifizierung und Autorisierung sicher und wartbar gestalten? In diesem Blogpost gebe ich einen Überblick und zeige, wie wir die Kubernetes API (und somit das Dashboard) über OpenID Connect (OIDC) authentifizieren und die Benutzer:innen mit Kubernetes Rollen autorisieren.

Autor
David Roth
Datum
12. September 2024
Lesedauer
7 Minuten

Möglichkeiten der Authentifizierung

Die Authentifizierung am Kubernetes API-Server kann auf verschiedene Weisen erfolgen. Oft wird eine cloud-spezifische Authentifizierung verwendet, wie bei AWS EKS über IAM-User, oder es werden Service-Account-Tokens genutzt. Der Nachteil dieser Methoden ist, dass die Benutzer:innen oder Tokens nochmals separat verwaltet werden müssen und keine vorhandenen Benutzer:innen im Unternehmen authentifiziert werden können. In unserem Fall nutzen wir GitLab als DevOps-Plattform, und alle Entwickler:innen haben bereits einen GitLab-Benutzer. Wäre es nicht ideal, wenn sich die Benutzer:innen direkt über GitLab am Dashboard authentifizieren können? Immerhin sind es ja genau die Entwickler:innen, welche für die Fehleranalyse auf das Dashboard zugreifen möchten. Glücklicherweise unterstützt der Kubernetes API-Server eine Authentifizierung über OpenID Connect Tokens, die genau das ermöglicht.

OpenID Connect Tokens

OpenID Connect (OIDC) ist eine Erweiterung von OAuth2, welcher standardmäßig von vielen großen Cloud-Diensten angeboten wird. Der wesentliche Unterschied zu OAuth2 ist der, dass OIDC neben dem OAuth-Access-Token einen zusätzlichen ID-Token definiert. Dieser ID-Token wird als signierter JWT (Json Web-Token) ausgehändigt und enthält verschiedene Informationen über die Benutzer:innen, wie z.B. die E-Mail-Adresse. Nun kann dieser ID-Token verwendet werden, um sich an einem System zu authentifizieren, indem der Token als »Bearer«-Token an den API-Server übergeben wird. Wie es im Fall von Kubernetes aussieht, sehen wir uns im nächsten Abschnitt an.

Kubernetes OIDC Authentifizierungs Flow

Um User zu authentifizieren, wird der id_token des OAuth2 Token Response als Bearer Token an den API Server übergeben. Die einzelnen Schritte aus Sicht des Users sehen wie folgt aus:

  1. User öffnet den Browser und öffnet die Dashboard-URL. (z.b. https://dashboard.com)
  2. OAuth2 Proxy leitet zum Gitlab Identity Provider um und der User meldet sich dort an
  3. Gitlab gibt den access_token, id_token und refresh_token an den OAuth2 Proxy zurück
  4. Oauth2 Proxy leitet den Request an das Dashboard weiter und inkludiert den id_token als Bearer Header
  5. Das Dashboard führt API Server Calls durch indem es den Bearer Token durchschleift
  6. Der K8s API Server validiert den JWT Token
  7. Der K8s API Server prüft ob der User für die Aktion autorisiert ist 
  8. Wenn der User für die Aktion autorisiert ist, wird ein gültiges API Result zurückgegeben
  9. Das Dashboard zeigt die Daten an

In einer Flow-Grafik visualisiert würde das ungefähr so aussehen:

oauth-proxy-overview-cropped (1)

Im echten User Flow mit dem Browser gestaltet sich der Ablauf wie folgt:

Schritt 1:  Der User öffnet im Browser die Url des Dashboards (z.b. https://dashboard.com).

Login with GitLab

Schritt 2: Der OAuth2 Proxy leitet den User auf Gitlab um welcher als Identity Server agiert und die Freigabe für den Zugriff auf die Email + OpenID Connect Infos des Users möchte.

Kubernetes Dashboard OAuth2 Proxy

Schritt 3: OAuth2 Proxy leitet die Infos an das Dashboard weiter welches damit auf den Kubernetes API Server zugreift.

dashboard-overview

Es folgen die einzelnen Schritte, die notwendig sind, um so ein Setup zu konfigurieren.

OAuth2 Proxy

Im vorherigen Abschnitt wurde bereits der OAuth2 Proxy namentlich erwähnt. In diesem Abschnitt möchte ich dessen Einsatz und Notwendigkeit nochmals kurz erklären. Das Kubernetes Dashboard unterstützt selbst nativ keinen OAuth Flow. Deshalb wird auf die Hilfe des Open-Source-Projektes OAuth2 Proxy ein Proxy vorgeschaltet, welcher den OIDC Flow abbildet.

Der Aufbau im Kubernetes Cluster sieht also wie folgt aus:

oauth-proxy-overview

Der Ingress für das Dashboard läuft also auf den OAuth2 Proxy, welcher die Authentifizierung am Identity Provider vornimmt. Nach erfolgreicher Authentifizierung leitet der OAuth2Proxy den id_token an das Dashboard mittels »Authorization: Bearer« Header weiter.

OIDC Identity Provider konfigurieren

Um einen gültigen OIDC-Token zu erhalten, benötigen wir zunächst einen Identity Provider, der uns diesen Token ausstellt. Da wir GitLab als DevSecOps-Plattform nutzen, war GitLab als Identity Provider die naheliegendste Wahl: Alle unsere Entwickler:innen und Product Owner haben bereits einen registrierten Account bei GitLab, da wir diese täglich verwenden. Genau diesen Benutzer:innen möchten wir einen unkomplizierten Zugriff auf spezielle Ressourcen im Kubernetes-Cluster ermöglichen.

Also konfigurieren wir Gitlab als Identity Provider und erstellen im ersten Schritt eine sogenannte Group-Owned-Application

Ein spezielles Augenmerk darf hier auf die definierten »Redirect URIs« gelegt werden. Die erste URL benötigen wir, um das Dashboard über einen Oauth2-Proxy zu authentifizieren. Dazu später mehr im Blogpost. Die beiden »localhost« Redirect-Urls werden verwendet, um einen unkomplizierten lokalen Zugriff mittels kubectl und kubelogin zu erhalten.

Nachdem wir die Applikation erstellt haben, erhalten wir zwei Credentials: Application ID + Secret. Das Naming ist hier nicht wichtig, aber falls es verwirrt: In anderen Identity Providern werden gerne die Begriffe »Client ID« und »Client Secret« verwendet.

Trust-Relationship zw. Kubernetes und OIDC Issuer 

Im nächsten Schritt müssen wir eine sogenannte Trust-Relationship zwischen Kubernetes und dem Identity Provider herstellen. Konkret geht es darum, dass Kubernetes die ausgegebenen JWT-Tokens auf ihre Korrektheit überprüfen kann, um eine sichere Authentifizierung zu gewährleisten. So stellen wir sicher, dass nur Benutzer:innen authentifiziert werden, die sich über den GitLab Identity Provider mit unserer konfigurierten »Application« angemeldet haben. Diese Beziehung wird hauptsächlich über die »Issuer URL« und die »Client ID« hergestellt.

Zusätzlich können wir in diesem Schritt konfigurieren, aus welchen Eigenschaften des JWT-Tokens die für die nachfolgende Autorisierung wichtigen Claims wie »Username« oder »Gruppen« extrahiert werden. Da wir in unserem Fall die GitLab-E-Mail-Adresse als »Username« verwenden möchten, spezifizieren wir »email« als Eigenschaft für den »Username«-Claim.

Da wir in unserem Fall AWS EKS (Elastic Kubernetes Service) verwenden, sieht die Konfiguration im UI wie folgt aus.

oidc-configuration-aws

Mittels Infrastructure-As-Code (Pulumi) Konfiguration wird dies wie folgt konfiguriert:

if (settings.oauth2Proxy.clientID !== undefined) { new aws.eks.IdentityProviderConfig(resourceName("oidc-identity"), { clusterName: cluster.eksCluster.name, oidc: { identityProviderConfigName: "Gitlab", clientId: settings.oauth2Proxy.clientID, issuerUrl: "https://gitlab.com", groupsClaim: "groups_direct", usernameClaim: "email", } }); }

Autorisierung über Rollen und Rollen-Mappings

Authentifizierung und Autorisierung sind zwei unterschiedliche Konzepte. Wenn sich User über GitLab bei unserem Cluster authentifizieren, bedeutet das nicht automatisch, dass User auf Daten zugreifen oder sie bearbeiten können. Durch die Authentifizierung mittels id_token wird also erstmal nur die Identität des Users bekannt gegeben. Im nächsten Schritt wollen wir aber, dass User entsprechende Ressourcen im Cluster lesen können oder ggf. auch Ressourcen bearbeiten/hinzufügen können.

Dies wird über das standardmäßige Kubernetes Role-Subject Mapping geregelt. Einer Kubernetes-Rolle können durch Subject-Mapping ein:e oder mehrere Benutzer:innen zugewiesen werden. Ein Beispiel dafür sieht folgendermaßen aus:

apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: labels: app.kubernetes.io/managed-by: pulumi name: oauth-cluster-readonly-ns-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: oauth-cluster-readonly-ns-role subjects: - apiGroup: rbac.authorization.k8s.io kind: User name: david.roth@fusonic.net

In diesem Beispiel definieren wir also, dass die selbst erstellte Rolle »oauth-cluster-readonly-ns-rolebinding« unter anderem dem Benutzer »david.roth@fusonic.net« zugewiesen wird.

Hier wird auch gleich ersichtlich, wieso die »Username« claim Konfiguration im Abschnitt weiter oben so wichtig war. Der E-Mail Claim wird als »Username« extrahiert und wird somit bei der Autorisierung als User verwendet.

Fazit

Durch den Einsatz von OIDC haben wir den Zugriff auf das Kubernetes Dashboard erheblich vereinfacht. Die Verwaltung der Rechte erfolgt über das standardmäßige Rollensystem von Kubernetes. Beim Onboarding und Offboarding von Benutzer:innen muss lediglich ein Subject-Mapping angepasst werden. Die User loggen sich bequem über den OIDC-Flow im Browser ein, sodass keine langlebigen Tokens mehr erforderlich sind, die im Passwort-Manager gespeichert werden müssen.

Mit Pulumi, unserem bevorzugten Tool für Infrastructure as Code, können wir komplexe Rollen-Konfigurationen einfach verwalten und behalten die volle Kontrolle.

Mehr davon?

Email-Templating with Blazor_B
Dev Diary
Email-Templating with Blazor
31. Oktober 2024 | 7 Min.
Cron Monitoring with Sentry_B
Dev Diary
Automating Job Monitoring with Sentry and Symfony
7. August 2024 | 3 Min.

Kontaktformular

*Pflichtfeld
*Pflichtfeld
*Pflichtfeld
*Pflichtfeld

Wir schützen deine Daten

Wir bewahren deine persönlichen Daten sicher auf und geben sie nicht an Dritte weiter. Mehr dazu erfährst du in unseren Datenschutzbestimmungen.