Blog

OAuth2-basierte Authentifizierung auf Kubernetes-Clustern, die von Istio betrieben werden

Mariusz Strzelecki

Aktualisiert Oktober 20, 2025
9 Minuten

Sie haben gerade Ihren ersten Kubernetes-Cluster installiert und Istio installiert, um die Vorteile von Service Mesh voll auszuschöpfen. Dank der wirklich großartigen Quickstarts war der Prozess viel einfacher, als Sie erwartet hatten. Als nächstes haben Sie den ersten Dienst installiert, entweder den Nginx-Webserver oder einen Echo-Server. Nachdem Sie das Gateway und den VirtualService von Istio eingerichtet haben, ist er plötzlich in Ihrem Webbrowser verfügbar. Aber der Browser warnt Sie vor unverschlüsselten Verbindungen. Ein paar Suchanfragen bei Google und Sie haben cert-manager mit Let's Encrypt als Aussteller installiert. Gute Arbeit! Ihre Webseite ist auf https://myservice.example.com verfügbar , der Browser meldet Ihnen, dass die Verbindung verschlüsselt ist und Sie können sich ein Lächeln nicht verkneifen, wenn Sie sehen, wie der Dienst mesh die Anfragen weiterleitet, nachdem Sie immer wieder F5 gedrückt haben.

Plötzlich stellen Sie fest, dass Sie beim Zugriff auf den Dienst kein Passwort eingegeben haben. Moment, ist es ein öffentlicher Endpunkt? Sie schicken den Link an Ihre Freunde, die alle auf die URL zugreifen können. Ihr privates kleines Dienstnetz ist in Wirklichkeit ein öffentliches, und je mehr Endpunkte Sie erstellen, desto öffentlicher wird es. Sie wären überrascht, wenn Sie wüssten, wie viele Dienste es im Internet gibt, die durch nichts anderes als einen DNS-Eintrag geschützt sind, den andere nicht kennen. Ihr Dienstnetz verdient etwas Besseres als das!

Kowalski? Optionen!

Es gibt mehrere Möglichkeiten, Ihre Dienste auf einem öffentlichen Cluster zu authentifizieren, aber nur einige wenige Methoden nutzen die nativen Funktionen von Istio und Envoy:

  • WebAssembly-Module bieten integrierte Filter, die "Basic Auth" implementieren. Wenn Sie in der Konfiguration einen Benutzernamen und ein Passwort angeben, werden diese bei der Abfrage der auf Istio installierten Dienste verwendet. Allerdings wird Ihre Konfiguration dadurch in Git nicht mehr versionierbar (Sie würden ja auch keine Passwörter im Repo speichern, oder?)
  • Eine OpenID Connect-Implementierung wie Dex(die von Kubeflow verwendet wird) leitet unberechtigte Benutzer auf ein schönes Anmeldeformular mit mehreren Anmeldeoptionen um: Benutzername+Passwort, LDAP, OAuth2 und mehr. Aber es fehlt das Kontrollkästchen "Remember me", so dass es nicht sehr benutzerfreundlich ist. Kubeflow verwendet einen zusätzlichen Authentifizierungsdienst, nur um Sitzungen zu ermöglichen. Moment, zwei separate Dienste, nur um die Authentifizierung zu ermöglichen? Das ist zu umständlich.
  • oauth2-proxy ist eine wirklich gute Lösung, insbesondere mit dem großartigen Quickstart von Luke Addison. Istio verfügt über einen Filter, um authentifizierungsbezogene Teilströme an den externen Dienst zu delegieren. In diesem Fall ist der Dienst oauth2-proxy, der nicht authentifizierte Clients an den OAuth2 Upstream (wie Google, Facebook oder Github) weiterleitet. Dort authentifizieren Sie sich (oder auch nicht, wenn Sie es vorher getan haben) und dann tauscht die Komponente den Code gegen Ihr Zugriffstoken aus, das nicht nur beweist, dass Sie authentifiziert sind, sondern auch einige grundlegende Informationen wie Name, E-Mail oder Foto-URL enthält (Istio kann diese als Header an Ihren Dienst weitergeben!). Aber diese Methode erfordert eine separate Komponente, nur um einen http-Aufruf (für ein Token) zu tätigen. Es muss einen einfacheren Weg geben!
  • Es gibt sie. Ab Envoy 1.16.0 (Istio >= 1.8) gibt es einen neuen Filter namens OAuth2. Er führt eine Token-Anfrage durch (genau wie oauth2-proxy), aber er macht sie intern (direkt von der Envoy-Komponente), so dass kein zusätzliches Tooling erforderlich ist. Diese Funktion ist ziemlich neu und es gibt nicht viele Anleitungen, wie man sie auf dem Istio-Cluster einsetzt. Lassen Sie uns also zur Sache kommen!

Wie funktioniert der OAuth2 Envoy Filter?

Wenn Sie zum ersten Mal auf einen Dienst mit OAuth2-Filter zugreifen, werden Sie zu authorization_endpoint weitergeleitet - das ist die URL des externen Dienstes, im Fall von Google ist es das Modal, das Sie wahrscheinlich schon oft gesehen haben:
Komplexe Ereignisse Verarbeitung Autorisierung
Sie können einen Anwendungsnamen (in meinem Fall ML Ops Platform Sandbox) und die Liste der Attribute festlegen, die in das Token aufgenommen werden sollen: Name, E-Mail und Profilbild. Dann wählen Sie ein Konto aus und Google leitet Sie auf die redirect_uri um (in der Filterspezifikation konfiguriert) und fügt dort einen geheimen, temporären Autorisierungscode hinzu. Diese Anfrage wird vom Filter abgefangen und er stellt eine Anfrage an , wobei er den Code gegen ein JWT-Token austauscht. Schließlich setzt der Filter 3 Cookies:

  • BearerToken - mit einem Token-Wert,
  • OauthExpires - ein Zeitstempel, der das Auslaufen des Tokens anzeigt,
  • OauthHMAC - ein Fingerabdruck der oben genannten, um eine Manipulation der Cookies zu verhindern.

Wenn alles klappt, werden Sie auf die ursprüngliche URL umgeleitet (diejenige, auf die Sie zugreifen wollten, bevor die Anfrage von einem Filter abgefangen wurde) und jede nachfolgende Anfrage wird nur schnell validiert (um zu überprüfen, ob die Cookies korrekt sind) und an den nachgeschalteten Dienst weitergeleitet.

Es empfiehlt sich, den Filter auf der allerersten Schicht für die externe Konnektivität zu Ihrem Netz zu installieren, d.h. Istio Ingressgateway. Eine Beispielkonfiguration sieht wie folgt aus:

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: oauth2-ingress
  namespace: istio-system
spec:
  workloadSelector:
    labels:
      istio: ingressgateway
  configPatches:
  - applyTo: CLUSTER
    match:
      cluster:
        service: oauth
    patch:
      operation: ADD
      value:
        name: oauth
        dns_lookup_family: V4_ONLY
        type: LOGICAL_DNS
        connect_timeout: 10s
        lb_policy: ROUND_ROBIN
        transport_socket:
          name: envoy.transport_sockets.tls
          typed_config:
            "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
            sni: oauth2.googleapis.com
        load_assignment:
          cluster_name: oauth
          endpoints:
          - lb_endpoints:
            - endpoint:
                address:
                  socket_address:
                    address: oauth2.googleapis.com
                    port_value: 443
  - applyTo: HTTP_FILTER
    match:
      context: GATEWAY
      listener:
        filterChain:
          filter:
            name: "envoy.http_connection_manager"
            subFilter:
              name: "envoy.filters.http.jwt_authn"
    patch:
      operation: INSERT_BEFORE
      value:
       name: envoy.filters.http.oauth2
       typed_config:
         "@type": type.googleapis.com/envoy.extensions.filters.http.oauth2.v3alpha.OAuth2
         config:
          token_endpoint:
            cluster: oauth
            uri: https://oauth2.googleapis.com/token
            timeout: 3s
          authorization_endpoint: https://accounts.google.com/o/oauth2/v2/auth
          redirect_uri: "https://%REQ(:authority)%/_oauth2_callback"
          redirect_path_matcher:
            path:
              exact: /_oauth2_callback
          signout_path:
            path:
              exact: /signout
          credentials:
            client_id: myclientid.apps.googleusercontent.com
            token_secret:
              name: token
              sds_config:
                path: "/etc/istio/config/token-secret.yaml"
            hmac_secret:
              name: hmac
              sds_config:
                path: "/etc/istio/config/hmac-secret.yaml"

Der erste Teil des Filters erstellt die Konfiguration des oauth Clusters, da der Filter standardmäßige Envoy Proxy-Funktionen verwendet, um HTTP-Anfragen zu stellen. Der zweite Teil fügt einen Filter selbst hinzu. Sie sehen einige der oben erwähnten Konfigurationsoptionen und 3, die wir noch nicht kennen:

  • client_id - dies ist die öffentliche ID Ihres OAuth2-Anbieters, die für die Umleitung des Autorisierungsendpunkts und den Token-Austausch verwendet wird
  • token_secret - auch bekannt als "client_secret", ist ein privater Teil der OAuth2-Einrichtung, der für den Token-Austausch erforderlich ist. Sie sollten es nicht in Git speichern. Selbst der Filter verlangt, dass es als sichere lokale Datei auf dem istio ingressgateway pod verfügbar ist.
  • hmac_secret - Wert, der für den Fingerabdruck des Tokens verwendet wird und auf die gleiche Weise sicher gespeichert wird wie token_secret.

Die Beispieldatei mit den Geheimnissen kann mithilfe von configmap in den ingressgateway-Pod injiziert werden:

apiVersion: v1
kind: ConfigMap
metadata:
  name: istio-oauth2
  namespace: istio-system
data:
  token-secret.yaml: |-
    resources:
      - "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret"
        name: token
        generic_secret:
          secret:
            inline_string: "..."
  hmac-secret.yaml: |-
    resources:
      - "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret"
        name: hmac
        generic_secret:
          secret:
            # generated using `head -c 32 /dev/urandom | base64`
            inline_bytes: XYJ7ibKwXwmRrO/yL/37ZV+T3Q/WB+xfhmVlio+wmc0=

Google OAuth2 Fall

Wenn Sie die Google-basierte Authentifizierung verwenden möchten, gibt es zwei weitere Dinge zu beachten.

Die erste ist, dass v1.17 von Envoy bei der Umleitung zum Autorisierungsendpunkt den statischen Bereich "user" verwendet. Dies ist kein gültiger OAuth2-Bereich für Google, so dass die Umleitung mit einer Fehlermeldung von Google fehlschlägt. Wenn Sie Envoy v1.18 verwenden, kann dies mit dem Parameter auth_scopes außer Kraft gesetzt werden. Wenn Sie jedoch noch 1.17 verwenden, können Sie ein kleines Lua-Skript einfügen, das den Parameter ändert:

 - applyTo: HTTP_FILTER
    match:
      context: GATEWAY
      listener:
        filterChain:
          filter:
            name: "envoy.http_connection_manager"
            subFilter:
              name: "envoy.router"
    patch:
      operation: INSERT_BEFORE
      value:
       name: envoy.filters.http.lua
       typed_config:
         "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
         inline_code: |
            function envoy_on_response(response_handle)
               if (response_handle:headers():get("location") ~= nil and response_handle:headers():get("location"):sub(1,44) == "https://accounts.google.com/o/oauth2/v2/auth") then
                location = response_handle:headers():get("location")
                location = location:gsub("scope=user", "scope=profile openid email")
                response_handle:headers():replace("location", location)
              end
            end

Zweitens gibt der Google Token Exchange Endpunkt zwei Token zurück:

  • id_token - JWT-Token mit allen angeforderten Attributen des Benutzers
  • access_token - beginnend mit ya29, ermöglicht den Zugriff auf Google-Dienste (liefert aber keine Benutzerdetails ohne zusätzlichen Aufruf)

Der Envoy OAuth2-Filter kopiert den access_token nur, damit er für die Authentifizierung verwendet werden kann, nicht aber für die Autorisierung des jeweiligen Benutzers.

Was ist mit CI/CD?

Mit der aktuellen Einrichtung haben wir den Zugriff auf die auf Istio installierten Dienste gesichert, während sie über den Webbrowser aufgerufen wurden. Wir haben jedoch immer noch keine gute Möglichkeit, auf die APIs im browserlosen Modus zuzugreifen, z.B. um sie über curl oder von CI/CD-Prozessen aus aufzurufen.

Glücklicherweise unterstützt Istio die Authentifizierung (und Autorisierung!) mit dekodierten Werten von JWT-Tokens. Die einzige Voraussetzung ist, das Token zu generieren und es als HTTP-Header mit dem Schlüssel "Authorization" und dem Wert "Bearer" zu übergeben. Anfragen wie diese sollten den OAuth2-Filter, den wir gerade konfiguriert haben, überspringen, da er vom Parameter pass_through_matcher unterstützt wird:

 pass_through_matcher:
          - name: authorization
            prefix_match: Bearer

Jetzt müssen wir den Token validieren. Zunächst müssen wir sicherstellen, dass er ordnungsgemäß signiert ist. Wir generieren Token mit dem Befehl gcloud auth print-identity-token(mit injiziertem Service-Konto-Schlüssel), und diese werden für eine bestimmte Zielgruppe ausgestellt. Mit dem folgenden Setup können Sie überprüfen, ob das JWT-Token mit diesem Befehl ausgestellt wurde:

apiVersion: security.istio.io/v1beta1
kind: RequestAuthentication
metadata:
  name: jwt-authentication
  namespace: istio-system
spec:
  selector:
    matchLabels:
      app: istio-ingressgateway
  jwtRules:
  - issuer: https://accounts.google.com
    jwksUri: https://www.googleapis.com/oauth2/v3/certs
    forwardOriginalToken: true
    audiences:
    - 32555940559.apps.googleusercontent.com # google token generator

Jetzt müssen wir eine Autorisierungsrichtlinie erstellen, die nur Token zulässt, die für ein bestimmtes Dienstkonto (oder das Fehlen dieses Tokens) generiert wurden.

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: known-user
  namespace: istio-system
spec:
  selector:
    matchLabels:
      app: istio-ingressgateway
  rules:
  - when: # Lack of Authorization header will push user to oauth2 filter
    - key: request.headers[Authorization]
      notValues:
      - 'Bearer*'
  - when: # CI/CD
    - key: request.auth.audiences
      values: ['32555940559.apps.googleusercontent.com']
    - key: request.auth.presenter
      values:
      - ml-ops-ci@gid-ml-ops-sandbox.iam.gserviceaccount.com

Bei der obigen Einrichtung haben wir ausdrücklich festgelegt, dass wir 2 Arten von Anfragen zulassen:

  • die ohne Authorization-Header (die bereits durch den OAuth2-Filter validiert wurden)
  • die mit einem gültigen Autorisierungs-Header, der vom gcloud-Befehl für ein bestimmtes Dienstkonto generiert wurde.

Zusammenfassung

Ab Envoy 1.17 müssen Sie für die Authentifizierung und Autorisierung bei Istio-Clustern keine externen Dienste mehr einrichten, wenn Sie sich für OAuth2 entscheiden. Es handelt sich um eine sichere Methode, da Sie keine Passwort-Hashes speichern, keine MFA pflegen und keine Benutzerdaten aufbewahren müssen. Sie müssen lediglich dem Token Ihres OAuth2-Anbieters vertrauen, der bestätigt, dass der Benutzer ordnungsgemäß authentifiziert wurde.

Wichtig zu erwähnen ist, dass diese Methode sowohl bei öffentlichen als auch bei privaten Clustern funktioniert. Diese Methode wird häufig zur Absicherung von öffentlichen Clustern verwendet, aber wenn Sie einen internen Istio-Cluster haben, können Sie Benutzer mit bereits verfügbaren Identitätsanbietern wie Active Directory(über ADFS) oder LDAP(über Ory Hydra) authentifizieren, ohne nach spezifischen Istio-Filtern suchen zu müssen.

Ich muss zugeben, dass die Menge an YAMLs, die in den Kubernetes-Cluster eingefügt werden müssen, riesig ist und die Einrichtung sehr umfangreich ist - alle Codeauflistungen in diesem Blogbeitrag umfassen über 150 Zeilen Code! Aber einmal angewandt und getestet, erfordert es keine zusätzliche Arbeit beim Hinzufügen neuer Dienste oder Endpunkte, so dass Sie sich nie wieder Gedanken über die Authentifizierung für Ihr Servicenetz machen müssen.

Und wenn Sie den Code zum Kopieren suchen, folgen Sie bitte diesem Link: https://szczeles.github.io/OAuth2-based-authentication-on-Istio-powered-Kubernetes-clusters/"

Verfasst von

Mariusz Strzelecki

Contact

Let’s discuss how we can support your journey.