Zugriff auf Cloud Run for Anthos-Dienste mit Istio autorisieren

In dieser Anleitung wird gezeigt, wie Sie mit Istio den Zugriff auf die Dienste autorisieren, die Sie in Cloud Run for Anthos bereitstellen.

Cloud Run bietet auf Entwickler ausgerichtete Tools für die Bereitstellung von Anwendungen und Funktionen. Cloud Run führt Dienste entweder in einer vollständig verwalteten Umgebung oder in einem GKE-Cluster (Google Kubernetes Engine) namens Cloud Run for Anthos aus.

Die Entwicklerumgebung ist in beiden Umgebungen gleich, aber die Funktionen der zugrunde liegenden Plattformen unterscheiden sich.

Cloud Run for Anthos verwendet nicht die Identitäts- und Zugriffsverwaltung (IAM, Identity and Access Management), um Berechtigungen zum Aufrufen von Diensten zu erteilen. Stattdessen verwenden Sie Istio, um die Authentifizierung und Autorisierung zu implementieren. In dieser Anleitung verwenden Sie Istio-Authentifizierungs- und -Autorisierungsrichtlinien, um einen Cloud Run for Anthos-Beispieldienst zu sichern.

Die Authentifizierungs- und Autorisierungsrichtlinien geben an, welche Identitäten (IAM-Dienstkonten und -Nutzer) den Beispieldienst aufrufen können.

In dieser Anleitung wird die Authentifizierung und Autorisierung für Clients implementiert, die außerhalb des GKE-Clusters ausgeführt werden.

Ziele

Kosten

In dieser Anleitung werden die folgenden kostenpflichtigen Komponenten von Google Cloud verwendet:

Mit dem Preisrechner können Sie eine Kostenschätzung für Ihre voraussichtliche Nutzung vornehmen. Neuen Google Cloud-Nutzern steht möglicherweise eine kostenlose Testversion zur Verfügung.

Nach Abschluss dieser Anleitung können Sie weitere Kosten vermeiden, indem Sie die erstellten Ressourcen löschen. Weitere Informationen finden Sie unter Bereinigen.

Hinweis

  1. Melden Sie sich bei Ihrem Google Cloud-Konto an. Wenn Sie mit Google Cloud noch nicht vertraut sind, erstellen Sie ein Konto, um die Leistungsfähigkeit unserer Produkte in der Praxis sehen und bewerten zu können. Neukunden erhalten außerdem ein Guthaben von 300 $, um Arbeitslasten auszuführen, zu testen und bereitzustellen.
  2. Wählen Sie in der Google Cloud Console auf der Seite der Projektauswahl ein Google Cloud-Projekt aus oder erstellen Sie eines.

    Zur Projektauswahl

  3. Die Abrechnung für das Cloud-Projekt muss aktiviert sein. So prüfen Sie, ob die Abrechnung für Ihr Projekt aktiviert ist.

  4. Aktivieren Sie die Google Kubernetes Engine API, die Cloud Run API und die Cloud APIs.
    APIs aktivieren
  5. Rufen Sie Cloud Shell in der Cloud Console auf.
    Zu Cloud Shell
    Unten in der Cloud Console wird eine Cloud Shell-Sitzung mit einer Befehlszeilen-Eingabeaufforderung geöffnet. Cloud Shell ist eine Shell-Umgebung, in der das Cloud SDK einschließlich des gcloud-Befehlszeilentools vorinstalliert ist. Die Werte sind bereits für Ihr aktuelles Projekt festgelegt. Das Initialisieren der Sitzung kann einige Sekunden dauern. Für alle Befehle in dieser Anleitung wird Cloud Shell verwendet.

Umgebung einrichten

  1. Definieren Sie Umgebungsvariablen und das gcloud-Standardtool für die Compute Engine-Zone, die Sie für diese Anleitung verwenden möchten:

    ZONE=us-central1-f
    gcloud config set compute/zone $ZONE
    

    Sie können die Zone ändern.

  2. GKE-Cluster mit dem Cloud Run-Add-On erstellen:

    CLUSTER=cloud-run-gke-invoker-tutorial
    
    gcloud beta container clusters create $CLUSTER \
        --addons HorizontalPodAutoscaling,HttpLoadBalancing,CloudRun \
        --enable-ip-alias \
        --enable-stackdriver-kubernetes \
        --machine-type e2-standard-2 \
        --release-channel regular
    

    Für diese Anleitung sind GKE-Versionen 1.15.11-gke.9 und höher, 1.16.8-gke.7 und höher oder 1.17.4-gke.5 und höher erforderlich. Neue GKE-Cluster, die die Release-Version regular verwenden, erfüllen die Versionseinschränkungen.

Beispieldienst bereitstellen

  1. Erstellen Sie in Cloud Shell im GKE-Cluster einen Namespace namens tutorial:

    kubectl create namespace tutorial
    

    Sie können den Namen des Namespace ändern.

  2. Stellen Sie einen Dienst namens sample für Cloud Run for Anthos im Namespace tutorial bereit:

    gcloud run deploy sample \
        --cluster $CLUSTER \
        --cluster-location $ZONE \
        --namespace tutorial \
        --image gcr.io/knative-samples/simple-api \
        --platform gke
    

    Durch diesen Befehl wird ein Knative Serving-Dienstobjekt erstellt.

  3. Cloud Run for Anthos stellt Dienste für die externe IP-Adresse des Istio-Ingress-Gateways bereit. Rufen Sie die externe IP-Adresse ab und speichern Sie sie in einer Umgebungsvariablen namens EXTERNAL_IP und einer Datei namens external-ip.txt:

    export EXTERNAL_IP=$(./get-external-ip.sh | tee external-ip.txt)
    
    echo $EXTERNAL_IP
    
    get_external_ip () {
        external_ip=$(kubectl -n gke-system get svc istio-ingress \
            -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    }
    get_external_ip
    while [ -z "$external_ip" ]; do
        sleep 2
        get_external_ip
    done
    echo "$external_ip"
  4. Prüfen Sie, ob der Beispieldienst mit HTTP/1.1 200 OK antwortet:

    curl -siH "Host: sample.tutorial.example.com" $EXTERNAL_IP | head -n1
    

Dienstkonto erstellen

  1. Erstellen Sie ein IAM-Dienstkonto. Sie werden diesem in einem späteren Schritt Zugriff auf den Beispieldienst gewähren. Speichern Sie die E-Mail-Adresse des Dienstkontos in einer Umgebungsvariablen:

    export SERVICE_ACCOUNT_EMAIL=$(gcloud iam service-accounts create \
        cloudrun-gke-invoker-tutorial \
        --display-name "Cloud Run for Anthos authorization tutorial service account" \
        --format "value(email)")
    

    In dieser Anleitung wird der Dienstkontoname cloudrun-gke-invoker-tutorial verwendet. Sie können ihn aber ändern.

Istio-Authentifizierung und -Autorisierung konfigurieren

  1. Erstellen Sie eine Istio-Authentifizierungsrichtlinie:

    kubectl apply -f authenticationpolicy.yaml
    
    apiVersion: authentication.istio.io/v1alpha1
    kind: Policy
    metadata:
      name: istio-ingress-jwt
      namespace: gke-system
    spec:
      targets:
      - name: istio-ingress
        ports:
        - name: http2
        - name: https
      origins:
      - jwt:
          issuer: https://accounts.google.com
          jwksUri: https://www.googleapis.com/oauth2/v3/certs
      originIsOptional: true # use authorization policy to allow or deny request
      principalBinding: USE_ORIGIN

    Diese Authentifizierungsrichtlinie validiert JSON-Web-Tokens (JWTs) im Header Authorization der eingehenden Anfragen an die Ports http2 und https des Istio Ingress Gateway. Um die Richtlinie zu erfüllen, müssen die JWTs von Google ausgegebene und signierte OpenID Connect (OIDC)-ID-Tokens sein. Weitere Informationen zur Validierung von ID-Tokens finden Sie in der Google Identity Platform-Dokumentation.

    Das Attribut originIsOptional: true bedeutet, dass die Authentifizierungsrichtlinie auch Anfragen akzeptiert, die kein die angegebenen Einschränkungen erfüllendes JWT enthalten. Sinn der Authentifizierungsrichtlinie ist es, Attribute der JWT für die im nächsten Schritt zu erstellende Autorisierungsrichtlinie verfügbar zu machen. Die Autorisierungsrichtlinie bestimmte, welche Anfragen akzeptiert werden, und welche nicht.

  2. Erstellen Sie eine Istio-Autorisierungsrichtlinie unter dem Namen invoke-tutorial:

    envsubst < authorizationpolicy-invoker.tmpl.yaml | kubectl apply -f -
    

    Diese Autorisierungsrichtlinie bietet Zugriff auf alle Arbeitslasten mit Hostnamen, die mit *.tutorial.example.com übereinstimmen und eine beliebige HTTP-Methode und einen beliebigen URL-Pfad verwenden, auf alle Anfragen, die ein von Google ausgegebenes und signiertes JWT (https://accounts.google.com) enthalten, und zu dem durch $SERVICE_ACCOUNT_EMAIL identifizierten Dienstkonto mit der Zielgruppenanforderung (aud) http://example.com.

    Wenn Sie die Standarddomain von Cloud Run for Anthos in Ihrer eigenen Umgebung ändern müssen Sie den Wert des Felds hosts auf Ihre Domain setzen.

    apiVersion: security.istio.io/v1beta1
    kind: AuthorizationPolicy
    metadata:
      name: invoke-tutorial
      namespace: gke-system
    spec:
      action: ALLOW
      rules:
      - to:
        - operation:
            hosts:
            - '*.tutorial.example.com'
        when:
        - key: request.auth.claims[aud]
          values:
          - http://example.com
        - key: request.auth.claims[email]
          values:
          - $SERVICE_ACCOUNT_EMAIL
        - key: request.auth.claims[iss]
          values:
          - https://accounts.google.com
      selector:
       matchLabels:
         istio: ingress-gke-system

    Wichtig: Die Verwendung von AuthorizationPolicy mit Istio und der Einstellung des Pfadtyps für die Zugriffssteuerung hat eine bekannte Sicherheitslücke. Weitere Informationen finden Sie in der Dokumentation zu Cloud Run for Anthos unter Best Practices für die Sicherheit.

  3. Es kann einen Moment dauern, bis die Authentifizierungs- und Autorisierungsrichtlinien wirksam werden. Führen Sie den folgenden Befehl aus und warten Sie, bis in der Ausgabe HTTP/1.1 403 Forbidden angezeigt wird:

    while sleep 2; do
        curl -siH "Host: sample.tutorial.example.com" $EXTERNAL_IP | head -n1
    done
    

    Zuerst sehen Sie, dass die Ausgabe zwischen HTTP/1.1 200 OK und HTTP/1.1 403 Forbidden wechselt, da Richtlinienänderungen in Envoy und Istio Eventual Consistency haben.

    Wenn Sie wiederholt HTTP/1.1 403 Forbidden sehen, drücken Sie Ctrl+C, um nicht länger warten zu müssen.

Auf den Dienst zugreifen

Die von Ihnen erstellten Authentifizierungs- und Autorisierungsrichtlinien erfordern von Google ausgestellte und signierte ID-Tokens. Tokens können auf verschiedene Arten abgerufen werden. Im Folgenden werden vier Möglichkeiten in der Reihenfolge aufgeführt, in der sie zu bevorzugen sind:

  1. Verwenden Sie den Compute Engine-Metadatenserver, wenn Sie über eine Compute Engine-Instanz, einen GKE-Cluster oder eine andere Umgebung, in der Sie Zugriff auf den Metadatenserver haben, auf den geschützten Dienst zugreifen.
  2. Verwenden Sie eine Authentifizierungs-Clientbibliothek von Google, sofern eine für Ihre Programmiersprache verfügbar ist.
  3. Wenn Sie keine Authentifizierungs-Clientbibliothek von Google verwenden können, nutzen Sie direkt die IAM Service Account Credentials API.
  4. Wenn Sie Ihren Dienst interaktiv testen möchten, z. B. während der Implementierung, verwenden Sie das gcloud-Befehlszeilentool.

Compute Engine-Metadatenserver verwenden

  1. Erstellen Sie in Cloud Shell eine Compute Engine-Instanz (VM) und hängen Sie das zuvor erstellte Dienstkonto an:

    VM=cloudrun-gke-invoker-tutorial-vm
    
    gcloud compute instances create $VM \
        --scopes cloud-platform \
        --service-account $SERVICE_ACCOUNT_EMAIL
    

    In dieser Anleitung wird der Instanzname cloudrun-gke-invoker-tutorial-vm verwendet. Sie können ihn aber ändern.

  2. Kopieren Sie die Datei mit der öffentlichen IP-Adresse des Istio-Ingress-Gateways auf die VM:

    gcloud compute scp external-ip.txt $VM:~
    
  3. Stellen Sie eine SSH-Verbindung zur VM her:

    gcloud compute ssh $VM
    
  4. Rufen Sie in der SSH-Sitzung ein ID-Token vom Compute Engine-Metadatenserver ab:

    ID_TOKEN=$(curl -s -H Metadata-Flavor:Google \
        --data-urlencode format=full \
        --data-urlencode audience=http://example.com \
        http://metadata/computeMetadata/v1/instance/service-accounts/default/identity)
    
  5. Senden Sie eine Anfrage mit dem ID-Token an den Cloud Run for Anthos-Beispieldienst:

    curl -s -w"\n" -H "Host: sample.tutorial.example.com" \
        -H "Authorization: Bearer $ID_TOKEN" $(cat external-ip.txt)
    

    Die Ausgabe sieht so aus:

    OK
    
  6. Verlassen Sie die SSH-Sitzung:

    exit
    

Clientbibliothek verwenden

Mit den Google-Authentifizierungs-Clientbibliotheken für Python, Java, Go und Node.js können Sie ID-Tokens mithilfe von praktischen APIs abrufen. Befolgen Sie diese Schritte, um die Python-Bibliothek und die Klasse IDTokenCredentials zu verwenden:

  1. Erstellen Sie in Cloud Shell die Anmeldedaten des Dienstkontos und laden Sie sie herunter:

    gcloud iam service-accounts keys create service-account.json \
        --iam-account $SERVICE_ACCOUNT_EMAIL
    
  2. Erstellen Sie eine virtuelle Python-Umgebung:

    virtualenv -p python3 .venv
    
  3. Aktivieren Sie die virtuelle Python-Umgebung für diese Terminalsitzung:

    source .venv/bin/activate
    
  4. Installieren Sie die Abhängigkeiten:

    pip install -r requirements.txt
    
  5. Rufen Sie ein ID-Token ab und senden Sie eine Anfrage an den Cloud Run for Anthos-Beispieldienst:

    python id_token_request.py http://$EXTERNAL_IP http://example.com \
        Host:sample.tutorial.example.com
    

    Die Ausgabe sieht so aus:

    OK
    
    import os
    import sys
    from google.auth.transport.requests import AuthorizedSession
    from google.oauth2 import service_account
    
    def request(method, url, target_audience=None, service_account_file=None,
                data=None, headers=None, **kwargs):
        """Obtains a Google-issued ID token and uses it to make a HTTP request.
    
        Args:
          method (str): The HTTP request method to use
                ('GET', 'OPTIONS', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE')
          url: The URL where the HTTP request will be sent.
          target_audience (str): Optional, the value to use in the audience
                ('aud') claim of the ID token. Defaults to the value of 'url'
                if not provided.
          service_account_file (str): Optional, the full path to the service
                account credentials JSON file. Defaults to
                '<working directory>/service-account.json'.
          data: Optional dictionary, list of tuples, bytes, or file-like object
                to send in the body of the request.
          headers (dict): Optional dictionary of HTTP headers to send with the
                request.
          **kwargs: Any of the parameters defined for the request function:
                https://github.com/requests/requests/blob/master/requests/api.py
                If no timeout is provided, it is set to 90 seconds.
    
        Returns:
          The page body, or raises an exception if the HTTP request failed.
        """
        # Set target_audience, if missing
        if not target_audience:
            target_audience = url
    
        # Set service_account_file, if missing
        if not service_account_file:
            service_account_file = os.path.join(os.getcwd(),
                                                'service-account.json')
    
        # Set the default timeout, if missing
        if 'timeout' not in kwargs:
            kwargs['timeout'] = 90  # seconds
    
        # Obtain ID token credentials for the specified audience
        creds = service_account.IDTokenCredentials.from_service_account_file(
            service_account_file, target_audience=target_audience)
    
        # Create a session for sending requests with the ID token credentials
        session = AuthorizedSession(creds)
    
        # Send a HTTP request to the provided URL using the Google-issued ID token
        resp = session.request(method, url, data=data, headers=headers, **kwargs)
        if resp.status_code == 403:
            raise Exception('Service account {} does not have permission to '
                            'access the application.'.format(
                                creds.service_account_email))
        elif resp.status_code != 200:
            raise Exception(
                'Bad response from application: {!r} / {!r} / {!r}'.format(
                    resp.status_code, resp.headers, resp.text))
        else:
            return resp.text

Die Dokumentation zu Identity-Aware Proxy enthält Beispielcode, der zeigt, wie Sie von Google ausgestellte ID-Tokens mit anderen Programmiersprachen abrufen.

IAM Service Account Credentials API verwenden

Ist für Ihre Programmiersprache keine Google-Authentifizierungs-Clientbibliothek mit der Möglichkeit zur Erstellung von ID-Tokens verfügbar, so können Sie die IAM Service Account Credentials API direkt verwenden:

  1. Erstellen Sie eine benutzerdefinierte Cloud IAM-Rolle mit der Berechtigung zum Erstellen von ID-Tokens für Dienstkonten:

    gcloud iam roles create serviceAccountIdTokenCreator \
        --project $GOOGLE_CLOUD_PROJECT \
        --description "Impersonate service accounts to create OpenID Connect ID tokens" \
        --permissions "iam.serviceAccounts.getOpenIdToken" \
        --stage GA \
        --title "Service Account ID Token Creator"
    

    Sie können die vordefinierte Rolle Ersteller von Dienstkonto-Tokens (iam.serviceAccountTokenCreator) verwenden, statt eine benutzerdefinierte Rolle zu erstellen. Diese Rolle bietet allerdings zusätzliche Berechtigungen, die zum Erstellen von ID-Tokens mit der Service Account Credentials API nicht erforderlich sind.

  2. Erstellen Sie eine Richtlinienbindung, um sich selbst die benutzerdefinierte Rolle für das zuvor erstellte Dienstkonto zuzuweisen:

    gcloud iam service-accounts add-iam-policy-binding $SERVICE_ACCOUNT_EMAIL \
        --member user:$(gcloud config get-value account) \
        --role projects/$GOOGLE_CLOUD_PROJECT/roles/serviceAccountIdTokenCreator
    

    Es kann einen Moment dauern, bis die Richtlinienbindung wirksam wird. Mit dem folgenden Befehl wird alle fünf Sekunden geprüft, ob sie in Kraft getreten ist. Ist dies der Fall, wird die Prüfung beendet und Sie gelangen zur Terminal-Eingabeaufforderung zurück:

    status_code=""
    while [ "$status_code" != "200" ] ; do
        sleep 5
        echo "Checking permissions"
        status_code=$(curl -s -w "%{http_code}" -o /dev/null -X POST \
            -H "Authorization: Bearer $(gcloud auth print-access-token)" \
            --data-urlencode audience=http://example.com \
            --data-urlencode includeEmail=true \
            "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/${SERVICE_ACCOUNT_EMAIL}:generateIdToken")
    done
    
  3. Verwenden Sie die Methode generateIdToken der IAM Service Account Credentials API, um ein ID-Token für das Dienstkonto zu generieren:

    ID_TOKEN=$(curl -s -X POST \
        -H "Authorization: Bearer $(gcloud auth print-access-token)" \
        --data-urlencode audience=http://example.com \
        --data-urlencode includeEmail=true \
        "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/${SERVICE_ACCOUNT_EMAIL}:generateIdToken" | jq -r '.token')
    
  4. Senden Sie eine Anfrage mit dem ID-Token an den Cloud Run for Anthos-Beispieldienst:

    curl -s -w"\n" -H "Host: sample.tutorial.example.com" \
        -H "Authorization: Bearer $ID_TOKEN" $EXTERNAL_IP
    

    Die Ausgabe sieht so aus:

    OK
    

gcloud-Befehlszeilentool verwenden

Um von Google ausgestellte ID-Tokens zu erhalten, können Sie das gcloud-Befehlszeilentool verwenden. Mit diesen Tokens identifizieren Sie sich selbst oder ein Dienstkonto, was für das interaktive Testen von Diensten hilfreich sein kann.

  1. Gewähren Sie sich selbst für das zuvor erstellte Dienstkonto die vordefinierte Rolle Ersteller von Dienstkonto-Tokens:

    gcloud iam service-accounts add-iam-policy-binding $SERVICE_ACCOUNT_EMAIL \
        --member user:$(gcloud config get-value account) \
        --role roles/iam.serviceAccountTokenCreator
    

    Es kann einen Moment dauern, bis die Richtlinienbindung wirksam wird. Mit dem folgenden Befehl wird alle fünf Sekunden geprüft, ob sie in Kraft getreten ist. Ist dies der Fall, wird die Prüfung beendet und Sie gelangen zur Terminal-Eingabeaufforderung zurück:

    status_code=""
    while [ "$status_code" != "200" ] ; do
        sleep 3
        echo "Checking permissions"
        status_code=$(curl -s -w "%{http_code}" -o /dev/null -X POST \
            -H "Authorization: Bearer $(gcloud auth print-access-token)" \
            -H "Content-Type: application/json" \
            -d '{"payload": "{}"}' \
            "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/${SERVICE_ACCOUNT_EMAIL}:signJwt")
    done
    
  2. Nehmen Sie die Identität des Dienstkontos an und rufen Sie ein ID-Token ab:

    ID_TOKEN=$(gcloud auth print-identity-token \
        --audiences http://example.com \
        --impersonate-service-account $SERVICE_ACCOUNT_EMAIL \
        --include-email)
    
  3. Senden Sie eine Anfrage mit dem ID-Token an den Cloud Run for Anthos-Beispieldienst:

    curl -s -w"\n" -H "Host: sample.tutorial.example.com" \
        -H "Authorization: Bearer $ID_TOKEN" $EXTERNAL_IP
    

    Die Ausgabe sieht so aus:

    OK
    

Bereinigen

Damit Ihrem Google Cloud-Konto die in dieser Anleitung verwendeten Ressourcen nicht in Rechnung gestellt werden, löschen Sie entweder das Projekt, das die Ressourcen enthält, oder Sie behalten das Projekt und löschen die einzelnen Ressourcen.

Projekt löschen

  1. Wechseln Sie in der Cloud Console zur Seite Ressourcen verwalten.

    Zur Seite „Ressourcen verwalten“

  2. Wählen Sie in der Projektliste das Projekt aus, das Sie löschen möchten, und klicken Sie dann auf Löschen.
  3. Geben Sie im Dialogfeld die Projekt-ID ein und klicken Sie auf Shut down (Beenden), um das Projekt zu löschen.

Ressourcen löschen

Wenn Sie das in dieser Anleitung verwendete Google Cloud-Projekt beibehalten möchten, löschen Sie die einzelnen Ressourcen:

  1. Löschen Sie den GKE-Cluster:

    gcloud container clusters delete $CLUSTER --async --quiet
    
  2. Löschen Sie das Dienstkonto:

    gcloud iam service-accounts delete $SERVICE_ACCOUNT_EMAIL --quiet
    
  3. Löschen Sie die Datei mit den Anmeldedaten für das Dienstkonto:

    rm -f service-account.json
    
  4. Löschen Sie die Compute Engine-Instanz:

    gcloud compute instances delete $VM --quiet
    

Nächste Schritte