Von App Identity zu OIDC-ID-Tokens migrieren

Wenn eine Anwendung, die in der Python 2-Laufzeit ausgeführt wird, eine Anfrage an eine andere App Engine-Anwendung sendet, kann mit der App Engine App Identity API ihre Identität bestätigt werden. Die Anwendung, die die Anfrage empfängt, kann anhand dieser Identität feststellen, ob sie die Anfrage verarbeiten soll.

Wenn Ihre Python 3-Anwendungen ihre Identität beim Senden von Anfragen an andere App Engine-Anwendungen bestätigen müssen, können Sie dazu OIDC-ID-Tokens (OpenID Connect) verwenden, die von den OAuth 2.0 APIs von Google ausgegeben und decodiert werden.

Hier erfahren Sie, wie Sie mit OIDC-ID-Tokens eine Identität bestätigen bzw. verifizieren können:

  1. Eine App Engine-Anwendung mit dem Namen "App A" ruft ein ID-Token aus der Google Cloud-Laufzeitumgebung ab.
  2. App A fügt dieses Token unmittelbar vor dem Senden der Anfrage an App B, eine andere App Engine-Anwendung, in den Anfrageheader ein.
  3. App B verwendet die OAuth 2.0 APIs von Google, um die Token-Nutzlast zu verifizieren. Die decodierte Nutzlast enthält die verifizierte Identität von App A in Form der E-Mail-Adresse des Standarddienstkontos von App A.
  4. App B vergleicht die Identität in der Nutzlast mit einer Liste von Identitäten, auf die sie reagieren darf. Wenn die Anfrage von einer zugelassenen Anwendung stammt, verarbeitet App B die Anfrage und antwortet.

OAuth 2.0-Prozess

In diesem Leitfaden wird beschrieben, wie Sie Ihre App Engine-Anwendungen mit ID-Tokens von OpenID Connect (OIDC) aktualisieren, um die Identität zu bestätigen, und wie Sie Ihre anderen App Engine-Anwendungen aktualisieren, um ID-Tokens zum Verifizieren der Identität vor dem Verarbeiten einer Anfrage zu verwenden.

Die wichtigsten Unterschiede zwischen den App Identity und OIDC APIs

  • Anwendungen in der Python 2-Laufzeit müssen die Identität nicht explizit bestätigen. Wenn eine Anwendung die Python-Bibliotheken httplib, urllib oder urllib2 oder den App Engine URL-Abrufdienst zum Senden ausgehender Anfragen verwendet, nutzt die Laufzeit den App Engine-URL-Abrufdienst, um die Anfrage zu stellen. Wenn die Anfrage an die Domain appspot.com gesendet wird, bestätigt der URL-Abrufdienst automatisch die Identität der anfragenden Anwendung, indem der Anfrage der Header X-Appengine-Inbound-Appid hinzugefügt wird. Dieser Header enthält die Anwendungs-ID der Anwendung (auch Projekt-ID genannt).

    Anwendungen in der Python 3-Laufzeit müssen die Identität explizit bestätigen. Dazu wird ein OIDC-ID-Token aus der Google Cloud-Laufzeitumgebung abgerufen und dem Anfrageheader hinzugefügt. Sie müssen den gesamten Code, der Anfragen an andere App Engine-Anwendungen sendet, aktualisieren, sodass die Anfragen ein OIDC-ID-Token enthalten.

  • Der Header X-Appengine-Inbound-Appid in einer Anfrage enthält die Projekt-ID der Anwendung, von der die Anfrage gesendet wurde.

    Die Nutzlast des OIDC-ID-Tokens von Google identifiziert die Projekt-ID der Anwendung nicht direkt. Stattdessen identifiziert das Token das Dienstkonto, unter dem die Anwendung ausgeführt wird, durch Angabe der E-Mail-Adresse dieses Dienstkontos. Sie müssen Code hinzufügen, um den Nutzernamen aus der Token-Nutzlast zu extrahieren.

    Wenn dieses Dienstkonto das App Engine-Standarddienstkonto auf Anwendungsebene für das Projekt ist, finden Sie die Projekt-ID in der E-Mail-Adresse des Dienstkontos. Der Nutzername in der Adresse ist mit der Projekt-ID identisch. In diesem Fall kann der empfangende Anwendungscode diesen in der Liste der Projekt-IDs ermitteln, von denen Anfragen zugelassen werden.

    Wenn die anfragende Anwendung aber ein nutzerverwaltetes Dienstkonto anstelle des standardmäßigen App Engine-Dienstkontos verwendet, kann die empfangende Anwendung nur die Identität dieses Dienstkontos verifizieren. Damit wird nicht notwendigerweise die Projekt-ID der anfragenden Anwendung definiert. In diesem Fall muss die empfangende Anwendung eine Liste der zulässigen Dienstkonto-E-Mails anstelle einer Liste der zulässigen Projekt-IDs verwalten.

  • Die Kontingente für Aufrufe über den URL-Abrufdienst unterscheiden sich von den Kontingenten der OAuth 2.0 APIs von Google zum Bereitstellen von Tokens. Auf dem OAuth-Zustimmungsbildschirm der Google Cloud Console finden Sie die maximale Anzahl an Tokens, die Sie pro Tag bereitstellen können. Für den URL-Abrufsdienst, die App Identity API und die OAuth 2.0 APIs von Google fallen keine Kosten an.

Übersicht über den Migrationsprozess

So migrieren Sie Ihre Python-Anwendungen so, dass OIDC APIs verwendet werden, um die Identität zu bestätigen und zu verifizieren:

  1. Bei Anwendungen, die beim Senden von Anfragen an andere App Engine-Anwendungen die Identität bestätigen müssen, ist Folgendes zu beachten:

    1. Warten Sie für die Migration zu ID-Tokens, bis Ihre Anwendung in einer Python 3-Umgebung ausgeführt wird.

      Es ist zwar möglich, ID-Tokens in der Python 2-Laufzeit zu verwenden, die Schritte in Python 2 sind jedoch sehr komplex und werden nur vorübergehend benötigt, bis Sie die Anwendung zum Ausführen in der Python 3-Laufzeit aktualisieren.

    2. Sobald Ihre Anwendung in Python 3 ausgeführt wird, aktualisieren Sie die Anwendung, um ein ID-Token anzufordern und das Token einem Anfrage-Header hinzuzufügen.

  2. Gehen Sie bei Anwendungen, die vor der Verarbeitung einer Anfrage die Identität verifizieren müssen, so vor:

    1. Aktualisieren Sie zuerst Ihre Python 2-Anwendungen, damit sowohl die ID-Tokens als auch die App Identity API-Identitäten unterstützt werden. Dadurch können Ihre Anwendungen Anfragen von Python 2-Anwendungen, die die App Identity API verwenden, oder Python 3-Anwendungen, die ID-Tokens verwenden, verifizieren und verarbeiten.

    2. Sobald Ihre aktualisierten Python 2-Anwendungen stabil sind, migrieren Sie sie zur Python 3-Laufzeit. Die ID-Tokens und die App Identity API-Identitäten sollten so lange unterstützt werden, bis Sie sicher sind, dass die Anwendungen keine Anfragen von Legacy-Anwendungen mehr unterstützen müssen.

    3. Wenn Sie keine Anfragen von App Engine-Legacy-Anwendungen mehr verarbeiten müssen, entfernen Sie den Code, mit dem die App Identity API-Identitäten verifiziert werden.

  3. Stellen Sie nach dem Testen der Anwendungen die Anwendung bereit, die Anfragen zuerst verarbeitet. Stellen Sie anschließend die aktualisierte Python 3-Anwendung bereit, die die Identität mithilfe von ID-Tokens bestätigt.

Identität bestätigen

Warten Sie, bis Ihre Anwendung in einer Python 3-Umgebung ausgeführt wird. Führen Sie dann die folgenden Schritte aus, um die Anwendung so zu aktualisieren, dass die Identität mit ID-Tokens bestätigt wird:

  1. google-auth-Clientbibliothek installieren

  2. Fügen Sie Code hinzu, um von den OAuth 2.0 APIs von Google ein ID-Token anzufordern und das Token vor dem Senden einer Anfrage einem Anfrageheader hinzuzufügen.

  3. Testen Sie Ihre Aktualisierungen.

google-auth-Clientbibliothek für Python 3-Anwendungen installieren

Erstellen Sie im Ordner mit der Datei app.yaml eine requirements.txt-Datei und fügen Sie folgende Zeile hinzu, um die google-auth-Clientbibliothek für Ihre Python 3-Anwendung verfügbar zu machen:

     google-auth

Wenn Sie Ihre Anwendung bereitstellen, lädt App Engine alle Abhängigkeiten herunter, die in der Datei requirements.txt definiert sind.

Für die lokale Entwicklung empfehlen wir, Abhängigkeiten in einer virtuellen Umgebung wie venv zu installieren.

Code zum Bestätigen der Identität hinzufügen

Suchen Sie im Code nach allen Instanzen, die Anfragen an andere App Engine-Anwendungen senden. Aktualisieren Sie diese Instanzen so, dass vor dem Senden der Anfrage Folgendes ausgeführt wird:

  1. Fügen Sie die folgenden Importe hinzu:

    from google.auth.transport import requests as reqs
    from google.oauth2 import id_token
    
  2. Rufen Sie mit google.oauth2.id_token.fetch_id_token(request, audience) ein ID-Token ab. Nehmen Sie in den Methodenaufruf folgende Parameter auf:

    • request: Übergeben Sie das Anfrageobjekt, das Sie senden möchten.
    • audience: Übergeben Sie die URL der Anwendung, an die Sie die Anfrage senden. Damit wird das Token an die Anfrage gebunden und kann nicht mehr von einer anderen Anwendung verwendet werden.

      Aus Gründen der Übersichtlichkeit und Spezifität sollten Sie die URL appspot.com übergeben, die App Engine für den jeweiligen Dienst erstellt hat, der die Anfrage empfängt, auch wenn Sie für die Anwendung eine benutzerdefinierte Domain verwenden.

  3. Geben Sie im Anfrageobjekt den folgenden Header an:

    'Authorization': 'ID {}'.format(token)
    

Beispiel:

# Copyright 2020 Google LLC
#
# 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.

from flask import Flask, render_template, request
from google.auth.transport import requests as reqs
from google.oauth2 import id_token
import requests

app = Flask(__name__)

@app.route("/", methods=["GET"])
def index():
    return render_template("index.html")

@app.route("/", methods=["POST"])
def make_request():
    url = request.form["url"]
    token = id_token.fetch_id_token(reqs.Request(), url)

    resp = requests.get(url, headers={"Authorization": f"Bearer {token}"})

    message = f"Response when calling {url}:\n\n"
    message += resp.text

    return message, 200, {"Content-type": "text/plain"}

Updates zur Identitätsbestätigung testen

So führen Sie Ihre Anwendung lokal aus und testen, ob die Anwendung erfolgreich ID-Tokens senden kann:

  1. Führen Sie diese Schritte aus, um die Anmeldedaten des App Engine-Standarddienstkontos in Ihrer lokalen Umgebung zur Verfügung zu stellen. Für die Google OAuth APIs sind diese Anmeldedaten erforderlich, um ein ID-Token zu generieren:

    1. Geben Sie den folgenden gcloud-Befehl ein, um den Dienstkontoschlüssel für das App Engine-Standardkonto Ihres Projekts abzurufen:

      gcloud iam service-accounts keys create ~/key.json --iam-account project-ID@appspot.gserviceaccount.com

      Ersetzen Sie project-ID durch die ID Ihres Google Cloud-Projekts.

      Die Dienstkontoschlüsseldatei wird jetzt auf Ihren Computer heruntergeladen. Sie können diese Datei beliebig verschieben und umbenennen. Bewahren Sie die Datei sicher auf, da sie zur Authentifizierung als Dienstkonto verwendet werden kann. Wenn sie verloren geht oder für nicht autorisierte Nutzer zugänglich ist, löschen Sie den Dienstkontoschlüssel und erstellen Sie einen neuen Schlüssel.

    2. Geben Sie den folgenden Befehl ein:

      <code>export GOOGLE_APPLICATION_CREDENTIALS=<var>service-account-key</var></code>
      

    Ersetzen Sie service-account-key durch den absoluten Pfadnamen der Datei, die den heruntergeladenen Dienstkontoschlüssel enthält.

  2. Starten Sie in derselben Shell, in die Sie die Umgebungsvariable GOOGLE_APPLICATION_CREDENTIALS exportiert haben, Ihre Python-Anwendung.

  3. Senden Sie eine Anfrage von der Anwendung und prüfen Sie, ob sie erfolgreich war. Wenn Sie noch keine Anwendung haben, die Anfragen empfangen und ID-Tokens zum Prüfen von Identitäten verwenden kann, gehen Sie so vor:

    1. Laden Sie die Beispielanwendung "incoming" herunter.
    2. Fügen Sie in der Datei main.py des Beispiels die ID Ihres Google Cloud-Projekts zu allowed_app_ids hinzu. Beispiel:

       allowed_app_ids = [
          '<APP_ID_1>',
          '<APP_ID_2>',
          'my-project-id'
        ]
      
    3. Führen Sie das aktualisierte Beispiel auf dem lokalen Entwicklungsserver von Python 2 aus.

Anfragen prüfen und verarbeiten

So aktualisieren Sie Ihre Python 2-Anwendungen, damit vor der Verarbeitung von Anfragen entweder ID-Tokens oder App Identity API-Identitäten verwendet werden:

  1. Installieren Sie die google-auth-Clientbibliothek.

  2. Aktualisieren Sie den Code, damit Folgendes möglich ist:

    1. Wenn die Anfrage den Header X-Appengine-Inbound-Appid enthält, verwenden Sie diesen Header, um die Identität zu verifizieren. Anwendungen, die in einer Legacy-Laufzeit wie Python 2 ausgeführt werden, enthalten diesen Header.

    2. Wenn die Anfrage nicht den Header X-Appengine-Inbound-Appid enthält, suchen Sie nach einem OIDC-ID-Token. Wenn das Token vorhanden ist, prüfen Sie die Nutzlast des Tokens und die Identität des Absenders.

  3. Testen Sie Ihre Aktualisierungen.

Google-Auth-Clientbibliothek für Python 2-Anwendungen installieren

So stellen Sie die google-auth-Clientbibliothek für Ihre Python 2-Anwendung zur Verfügung:

  1. Erstellen Sie im Ordner mit der Datei app.yaml die Datei requirements.txt und fügen Sie die folgende Zeile hinzu:

     google-auth==1.19.2
    

    Wir empfehlen die Verwendung der Version 1.19.2 der Cloud Logging-Clientbibliothek, da diese Python 2.7-Anwendungen unterstützt.

  2. Geben Sie in der app.yaml-Datei Ihrer Anwendung unter libraries die SSL-Bibliothek an, falls noch nicht angegeben:

    libraries:
    - name: ssl
      version: latest
    
  3. Erstellen Sie ein Verzeichnis, in dem Sie die Bibliotheken von Drittanbietern speichern können, beispielsweise lib/. Dann verwenden Sie pip install zum Installieren der Bibliotheken in das Verzeichnis. Beispiel:

    pip install -t lib -r requirements.txt
    
  4. Erstellen Sie eine appengine_config.py-Datei im selben Ordner wie Ihre app.yaml-Datei. Fügen Sie der Datei appengine_config.py Folgendes hinzu:

    # appengine_config.py
    import pkg_resources
    from google.appengine.ext import vendor
    
    # Set path to your libraries folder.
    path = 'lib'
    # Add libraries installed in the path folder.
    vendor.add(path)
    # Add libraries to pkg_resources working set to find the distribution.
    pkg_resources.working_set.add_entry(path)
    

    Für die Datei appengine_config.py im obigen Beispiel wird davon ausgegangen, dass sich der Ordner lib im aktuellen Arbeitsverzeichnis befindet. Wenn Sie nicht garantieren können, dass sich lib immer im aktuellen Arbeitsverzeichnis befindet, geben Sie den vollständigen Pfad zum Ordner lib an. Beispiel:

    import os
    path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'lib')
    

Für die lokale Entwicklung empfehlen wir, Abhängigkeiten in einer virtuellen Umgebung wie virtualenv für Python 2 zu installieren.

Code zur Überprüfung von Anfragen aktualisieren

Suchen Sie in Ihrem Code nach allen Instanzen, um den Wert des Headers X-Appengine-Inbound-Appid zu erhalten. Aktualisieren Sie diese Instanzen so, dass Folgendes möglich ist:

  1. Fügen Sie die folgenden Importe hinzu:

    from google.auth.transport import requests as reqs
    from google.oauth2 import id_token
    
  2. Wenn die eingehende Anfrage nicht den Header X-Appengine-Inbound-Appid enthält, suchen Sie nach dem Header Authorization und rufen Sie dessen Wert ab.

    Der Header-Wert hat das Format "ID: Token".

  3. Mit google.oauth2.id_token.verify_oauth2_token(token, request, audience) können Sie die decodierte Token-Nutzlast prüfen und abrufen. Fügen Sie die folgenden Parameter in den Methodenaufruf ein:

    • token: Übergeben Sie das aus der eingehenden Anfrage extrahierte Token.
    • request: Übergeben Sie ein neues google.auth.transport.Request-Objekt.

    • audience: Übergeben Sie die URL der aktuellen Anwendung (die Anwendung, die die Verifizierungsanfrage sendet). Der Autorisierungsserver von Google vergleicht diese URL mit der URL, die beim ursprünglichen Generieren des Tokens angegeben wurde. Wenn die URLs nicht übereinstimmen, wird das Token nicht verifiziert und der Autorisierungsserver gibt einen Fehler zurück.

  4. Die Methode verify_oauth2_token gibt die decodierte Token-Nutzlast zurück. Diese enthält mehrere Name/Wert-Paare, einschließlich der E-Mail-Adresse des Standarddienstkontos für die Anwendung, die das Token generiert hat.

  5. Extrahieren Sie den Nutzernamen aus der E-Mail-Adresse in der Token-Nutzlast.

    Der Nutzername stimmt mit der Projekt-ID der Anwendung überein, die die Anfrage gesendet hat. Es ist der gleiche Wert, der zuvor im X-Appengine-Inbound-Appid-Header zurückgegeben wurde.

  6. Wenn sich der Nutzername bzw. die Projekt-ID in der Liste der zulässigen Projekt-IDs befindet, verarbeiten Sie die Anfrage.

Beispiel:

# Copyright 2020 Google LLC
#
# 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.

"""
Authenticate requests coming from other App Engine instances.
"""

from google.oauth2 import id_token
from google.auth.transport import requests

import logging
import webapp2

def get_app_id(request):
    # Requests from App Engine Standard for Python 2.7 will include a
    # trustworthy X-Appengine-Inbound-Appid. Other requests won't have
    # that header, as the App Engine runtime will strip it out
    incoming_app_id = request.headers.get("X-Appengine-Inbound-Appid", None)
    if incoming_app_id is not None:
        return incoming_app_id

    # Other App Engine apps can get an ID token for the App Engine default
    # service account, which will identify the application ID. They will
    # have to include at token in an Authorization header to be recognized
    # by this method.
    auth_header = request.headers.get("Authorization", None)
    if auth_header is None:
        return None

    # The auth_header must be in the form Authorization: Bearer token.
    bearer, token = auth_header.split()
    if bearer.lower() != "bearer":
        return None

    try:
        info = id_token.verify_oauth2_token(token, requests.Request())
        service_account_email = info["email"]
        incoming_app_id, domain = service_account_email.split("@")
        if domain != "appspot.gserviceaccount.com":  # Not App Engine svc acct
            return None
        else:
            return incoming_app_id
    except Exception as e:
        # report or log if desired, as here:
        logging.warning("Request has bad OAuth2 id token: {}".format(e))
        return None

class MainPage(webapp2.RequestHandler):
    allowed_app_ids = ["other-app-id", "other-app-id-2"]

    def get(self):
        incoming_app_id = get_app_id(self.request)

        if incoming_app_id is None:
            self.abort(403)

        if incoming_app_id not in self.allowed_app_ids:
            self.abort(403)

        self.response.write("This is a protected page.")

app = webapp2.WSGIApplication([("/", MainPage)], debug=True)

Updates zur Überprüfung der Identität testen

Testen Sie, ob Ihre Anwendung entweder ein ID-Token oder den X-Appengine-Inbound-Appid-Header zur Überprüfung von Anfragen verwenden kann. Führen Sie dazu die Anwendung auf dem lokalen Entwicklungsserver von Python 2 aus. Anfragen werden von Python 2-Anwendungen gesendet, die die App Identity API verwenden, und von Python 3-Anwendungen, die ID-Tokens senden.

Wenn Sie die Anwendung nicht so aktualisiert haben, dass ID-Tokens gesendet werden können, gehen Sie so vor:

  1. Laden Sie die Beispielanwendung "requesting" herunter.

  2. Fügen Sie Ihrer lokalen Umgebung Anmeldedaten für ein Dienstkonto hinzu, wie unter Aktualisierungen zum Bestätigen von Anwendungen testen beschrieben.

  3. Starten Sie mit den normalen Python 3-Befehlen die Python 3-Beispielanwendung.

  4. Senden Sie eine Anfrage von der Beispielanwendung und prüfen Sie, ob sie erfolgreich war.

Anwendungen bereitstellen

Sobald Sie Ihre Anwendung bereitstellen können, sollten Sie Folgendes tun:

  1. Testen Sie die Anwendungen in App Engine.

  2. Wenn die Anwendungen fehlerfrei ausgeführt werden, verwenden Sie die Trafficaufteilung, um den Traffic für die aktualisierten Anwendungen langsam zu erhöhen. Prüfen Sie die Anwendungen sorgfältig auf mögliche Probleme, bevor Sie mehr Traffic zu den aktualisierten Anwendungen leiten.

Anderes Dienstkonto zur Bestätigung der Identität verwenden

Wenn Sie ein ID-Token anfordern, verwendet die Anfrage standardmäßig die Identität des App Engine-Standarddienstkontos. Beim Verifizieren des Tokens enthält die Token-Nutzlast die E-Mail-Adresse des Standarddienstkontos, das der Projekt-ID der Anwendung zugeordnet ist.

Das App Engine-Standarddienstkonto hat standardmäßig eine sehr hohe Berechtigungsstufe. Damit kann Ihr gesamtes Google Cloud-Projekt aufgerufen und bearbeitet werden. Daher ist dieses Konto in den meisten Fällen nicht geeignet, wenn sich Ihre Anwendung bei Cloud-Diensten authentifizieren muss.

Das Standarddienstkonto kann jedoch verwendet werden, um die Anwendungsidentität zu bestätigen, da Sie ausschließlich das ID-Token verwenden, um die Identität der Anwendung zu verifizieren, die eine Anfrage gesendet hat. Die tatsächlichen Berechtigungen, die dem Dienstkonto gewährt wurden, werden bei diesem Vorgang nicht berücksichtigt oder benötigt.

Wenn Sie für Ihre ID-Token-Anfragen weiterhin ein anderes Dienstkonto verwenden möchten, gehen Sie so vor:

  1. Legen Sie eine Umgebungsvariable namens GOOGLE_APPLICATION_CREDENTIALS auf den Pfad einer JSON-Datei fest, die die Anmeldedaten des Dienstkontos enthält. Lesen Sie unsere Empfehlungen zum sicheren Speichern von Anmeldedaten.

  2. Rufen Sie mit google.oauth2.id_token.fetch_id_token(request, audience) ein ID-Token ab.

  3. Wenn Sie dieses Token verifizieren, enthält die Token-Nutzlast die E-Mail-Adresse des neuen Dienstkontos.