Authentifizierung zwischen Diensten

Sie können die Authentifizierung zwischen Diensten mithilfe eines Dienstkontos in einem gRPC-Dienst implementieren. Auf dieser Seite wird die Dienst-zu-Dienst-Authentifizierung erläutert. Anhand eines umfassenden Beispiels wird gezeigt, wie Sie den Extensible Service Proxy (ESP) in einem gRPC-Dienst für authentifizierte Anfragen konfigurieren und den Dienst über einen gRPC-Client aufrufen.

Damit ein Dienst authentifizierte Aufrufe bei einer Cloud Endpoints-API ausführen kann, benötigt der aufrufende Dienst ein Dienstkonto und muss während des Aufrufs ein Authentifizierungstoken senden. Der Aufrufer muss ein Google-ID-Token oder ein benutzerdefiniertes, nur vom Dienstkonto des Aufrufers signiertes JSON Web Token (JWT) verwenden. Der ESP überprüft, ob die Anforderung iss im JWT mit der Einstellung issuer in der Dienstkonfiguration übereinstimmt. Der ESP nimmt keine Prüfung der für das Dienstkonto festgelegten Berechtigungen für das Identity and Access Management vor.

In unserem Beispiel richten Sie die einfachste Form der Dienst-zu-Dienst-Authentifizierung ein. Dabei verwendet der Client das Google Cloud-Dienstkonto, um authentifizierende JWTs zu erzeugen. Andere Authentifizierungsmethoden verwenden einen ähnlichen Ansatz, wobei der clientseitige Prozess zum Anfordern gültiger Authentifizierungstoken von der verwendeten Authentifizierungsmethode abhängt.

Hinweis

In dieser Übersicht wird das Bookstore-Beispiel aus den Anleitungen verwendet.

  1. Klonen Sie das Git-Repository, in dem der gRPC-Beispielcode gehostet wird:

    git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git
    
  2. Wechseln Sie in das Arbeitsverzeichnis:

    cd python-docs-samples/endpoints/bookstore-grpc/
    
  3. Falls noch kein Projekt vorhanden ist, richten Sie anhand der Anweisungen in den Anleitungen ein Projekt ein.

In diesem Beispiel verwenden Sie die Bereitstellung auf Google Kubernetes Engine, wobei die Einrichtung der Authentifizierung für Compute Engine identisch ist.

Im Beispiel wird auf zwei Google Cloud Platform-Projekte verwiesen:

  • Das Herstellerprojekt, das Cloud Endpoints für den gRPC-Dienst verwaltet
  • Das Nutzerprojekt, das den gRPC-Client verwaltet

Benutzerdefiniertes Dienstkonto und Schlüssel erstellen

So erstellen Sie das Dienstkonto und den Schlüssel für das Nutzerprojekt:

  1. Rufen Sie in der Google Cloud Console zur Seite „APIs & Dienste” auf.

    APIs und Dienste

    Überprüfen Sie, ob Ihr Nutzerprojekt aktiv ist.
  2. Wählen Sie auf der Seite Anmeldedaten in der Drop-down-Liste Anmeldedaten erstellen die Option Dienstkontoschlüssel aus.
  3. Wenn auf der Seite Dienstkontoschlüssel erstellen das gewünschte Dienstkonto bereits vorhanden ist, wählen Sie es aus. Wählen Sie andernfalls in der Drop-down-Liste Dienstkonto die Option Neues Dienstkonto aus und geben Sie einen Kontonamen ein.

    Eine entsprechende Dienstkonto-ID wird für Sie erstellt. Notieren Sie sich die ID, sie wird in den folgenden Abschnitten benötigt. Beispiel:

    service-account-name@YOUR_PROJECT_ID.iam.gserviceaccount.com
    
  4. Wählen Sie in der Drop-down-Liste Rolle die folgenden Rollen aus:

    • Dienstkonten > Dienstkontonutzer
    • Dienstkonten > Ersteller von Dienstkonto-Token
  5. Als Schlüsseltyp muss JSON ausgewählt sein.

  6. Klicken Sie auf Erstellen. Die JSON-Schlüsseldatei für Ihr Dienstkonto wird auf Ihren lokalen Computer heruntergeladen. Notieren Sie sich den Speicherort und sorgen Sie dafür, dass die Datei sicher gespeichert wird, da Sie sie später verwenden, um Tokens zu generieren.

Authentifizierung für den Dienst konfigurieren

Verwenden Sie das Herstellerprojekt für alle Schritte in diesem Abschnitt.

Authentifizierung in der gRPC API-Konfiguration einrichten

Die Authentifizierung für ESP wird in der YAML-Datei für die gRPC API-Konfiguration im Abschnitt authentication festgelegt. Die Konfiguration mit Authentifizierung für diesen Beispieldienst befindet sich in api_config_auth.yaml.

authentication:
  providers:
  - id: google_service_account
    # Replace SERVICE-ACCOUNT-ID with your service account's email address.
    issuer: SERVICE-ACCOUNT-ID
    jwks_uri: https://www.googleapis.com/robot/v1/metadata/x509/SERVICE-ACCOUNT-ID
  rules:
  # This auth rule will apply to all methods.
  - selector: "*"
    requirements:
      - provider_id: google_service_account

Der Abschnitt providers gibt die zu verwendenden Authentifizierungsanbieter an. In diesem Fall möchten Sie ein Google-Dienstkonto als Authentifizierungsanbieter verwenden. Der Abschnitt rules gibt an, dass Sie für den Zugriff auf alle Methoden Ihres Dienstes Tokens dieses Anbieters benötigen.

Führen Sie in Ihrer eigenen Kopie dieser Datei aus dem geklonten Repository folgende Schritte aus:

  • Ersetzen Sie MY_PROJECT_ID durch die ID des Erstellerprojekts.
  • Ersetzen Sie SERVICE-ACCOUNT-ID im Abschnitt authentication (in den Werten für issuer und jwks_uri) durch die ID des Nutzerdienstkontos, die Sie im vorherigen Abschnitt notiert haben. Der ESP wird dadurch angewiesen, Nutzern mit gültigen Tokens von diesem speziellen Dienstkonto Zugriff auf Ihren Dienst zu gewähren.
  • Fügen Sie optional jwt_locations unter dem Element providers hinzu. Sie können diesen Wert verwenden, um einen benutzerdefinierten JWT-Speicherort zu definieren. Die JWT-Standardspeicherorte sind die Metadaten Authorization (mit dem Präfix "Bearer ") und die Metadaten X-Goog-Iap-Jwt-Assertion.

Speichern Sie die Datei für den nächsten Schritt.

Konfiguration und Dienst bereitstellen

Diese Schritte sind dieselben wie in Erste Schritte mit gRPC auf GKE:

  1. Stellen Sie Ihre Dienstkonfiguration für Endpoints bereit: Dieser Schritt ist auch nötig, wenn Sie ihn in der Anleitung ausgeführt haben, da es sich um verschiedene Konfigurationen handelt. Notieren Sie sich den zurückgegebenen Dienstnamen:

    gcloud endpoints services deploy api_descriptor.pb api_config_auth.yaml --project PRODUCER_PROJECT
    
  2. Erstellen Sie einen Containercluster und authentifizieren Sie kubectl beim Cluster, wenn nicht bereits geschehen.

  3. Stellen Sie die Beispiel-API und den ESP im Cluster bereit. Wenn Sie separate Ersteller- und Nutzerprojekte verwenden, achten Sie darauf, das entsprechende Projekt im gcloud-Befehlszeilentool festzulegen:

    gcloud config set project PRODUCER_PROJECT
    

Authentifizierte Methoden von einem gRPC-Client aus aufrufen

Auf der Clientseite können Sie schließlich mit dem Dienstkontoschlüssel ein JWT-Token generieren und anschließend mit dem Token eine authentifizierte Bookstore-Methode aufrufen. Installieren Sie zuerst die entsprechenden Python-Anforderungen, um sowohl das Token zu generieren als auch den Beispielclient auszuführen. Prüfen Sie, ob Sie sich im Ordner python-docs-samples/endpoints/bookstore-grpc Ihres geklonten Clients befinden, und gehen Sie dann folgendermaßen vor:

virtualenv bookstore-env
source bookstore-env/bin/activate
pip install -r requirements.txt

JWT generieren

Der Bookstore in unserem Beispiel verwendet die Dienst-zu-Dienst-Authentifizierung. Der Aufrufdienst wird dabei nur vom Dienstkonto authentifiziert, sodass sich das Token für die Anfrage einfach erstellen lässt. Auch eine strengere Dienst-zu-Dienst-Authentifizierung ist möglich, bei der das generierte Token zusätzlich von Google (Google-ID-Token) authentifiziert werden muss.

Für dieses Beispiel kann das bereitgestellte Python-Skript ein Token aus der zuvor heruntergeladenen JSON-Schlüsseldatei generieren. Dabei werden eine Dummy-Nutzer-ID und eine Dummy-E-Mail-Adresse verwendet.

#!/usr/bin/env python

# Copyright 2016 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Example of generating a JWT signed from a service account file."""

import argparse
import json
import time

import google.auth.crypt
import google.auth.jwt

"""Max lifetime of the token (one hour, in seconds)."""
MAX_TOKEN_LIFETIME_SECS = 3600


def generate_jwt(service_account_file, issuer, audiences):
    """Generates a signed JSON Web Token using a Google API Service Account."""
    with open(service_account_file) as fh:
        service_account_info = json.load(fh)

    signer = google.auth.crypt.RSASigner.from_string(
        service_account_info["private_key"], service_account_info["private_key_id"]
    )

    now = int(time.time())

    payload = {
        "iat": now,
        "exp": now + MAX_TOKEN_LIFETIME_SECS,
        # aud must match 'audience' in the security configuration in your
        # swagger spec. It can be any string.
        "aud": audiences,
        # iss must match 'issuer' in the security configuration in your
        # swagger spec. It can be any string.
        "iss": issuer,
        # sub and email are mapped to the user id and email respectively.
        "sub": issuer,
        "email": "user@example.com",
    }

    signed_jwt = google.auth.jwt.encode(signer, payload)
    return signed_jwt


if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
    )
    parser.add_argument("--file", help="The path to your service account json file.")
    parser.add_argument("--issuer", default="", help="issuer")
    parser.add_argument("--audiences", default="", help="audiences")

    args = parser.parse_args()

    signed_jwt = generate_jwt(args.file, args.issuer, args.audiences)
    print(signed_jwt.decode("utf-8"))

So generieren Sie mithilfe des Skripts ein Token:

  • Generieren Sie ein JWT-Token und weisen Sie es der Variablen $JWT_TOKEN zu:

    JWT_TOKEN=$(python jwt_token_gen.py \
        --file=[SERVICE_ACCOUNT_FILE] \
        --audiences=[SERVICE_NAME] \
        --issuer=[SERVICE-ACCOUNT-ID])
    

    Dabei gilt:

    • [SERVICE_ACCOUNT_FILE] ist die heruntergeladene JSON-Schlüsseldatei des Nutzerdienstkontos.
    • [SERVICE_NAME] ist der Name des Bookstore-Diensts, der zurückgegeben wurde, als Sie die aktualisierte Dienstkonfiguration für Endpoints bereitgestellt haben.
    • [SERVICE-ACCOUNT-ID] ist die vollständige Nutzerdienstkonto-ID bei der Erstellung des Dienstkontos.

Authentifizierten gRPC-Anruf tätigen

In diesem letzten Schritt wird wie in den Anleitungen der Client bookstore_client.py verwendet. Für einen authentifizierten Aufruf übergibt der Client das JWT als Metadaten mit dem Methodenaufruf.

def run(
    host, port, api_key, auth_token, timeout, use_tls, servername_override, ca_path

So führen Sie das Beispiel aus:

  1. Rufen Sie mit kubectl get services die externe IP-Adresse für den bereitgestellten Bookstore ab:

    #kubectl get services
    NAME                 CLUSTER-IP      EXTERNAL-IP      PORT(S)           AGE
    echo                 10.11.246.240   104.196.186.92   80/TCP            10d
    endpoints            10.11.243.168   104.196.210.50   80/TCP,8090/TCP   10d
    esp-grpc-bookstore   10.11.254.34    104.196.60.37    80/TCP            1d
    kubernetes           10.11.240.1     <none>           443/TCP           10d
    

    In diesem Fall ist es der Dienst esp-grpc-bookstore und die externe IP-Adresse lautet 104.196.60.37.

  2. Weisen Sie die IP-Adresse der Variablen EXTERNAL_IP zu:

    EXTERNAL_IP=104.196.60.37
    
  3. Listen Sie alle Shelves des Bookstore-Diensts auf:

    python bookstore_client.py --port=80 --host=$EXTERNAL_IP --auth_token=$JWT_TOKEN
    

    Der Dienst gibt alle Shelves im Bookstore zurück. Zur Kontrolle können Sie entweder kein Token zur Verfügung stellen oder beim Generieren des JWT eine falsche Dienstkonto-ID angeben. Der Befehl sollte dann fehlschlagen.

Nächste Schritte