From d0490ce1658a45c1c2e8463cb23e7b2eadd90c10 Mon Sep 17 00:00:00 2001 From: Zev Goldstein Date: Tue, 20 Jul 2021 11:55:33 -0400 Subject: [PATCH] fix: do not use the GAE APIs on gen2+ runtimes Currently, this library uses the App Engine API in all environments if it can be imported successfully. This assumption made sense when the API was only available on gen1, but this is no longer the case. See https://github.com/GoogleCloudPlatform/appengine-python-standard In order to comply with AIP-4115, we must treat GAE gen2+ as a "compute engine equivalent environment" even if the GAE APIs are importable. In other words, google.auth.default() must never return an app_engine.Credental on GAE gen2+.Currently, this library uses the App Engine API in all environments if it can be imported successfully. This assumption made sense when the API was only available on gen1, but this is no longer the case. See https://github.com/GoogleCloudPlatform/appengine-python-standard In order to comply with AIP-4115, we must treat GAE gen2+ as a "compute engine equivalent environment" even if the GAE APIs are importable. In other words, google.auth.default() must never return an app_engine.Credental on GAE gen2+. --- google/auth/_default.py | 5 +++++ google/auth/environment_vars.py | 6 ++++++ tests/test__default.py | 17 ++++++++++++++++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/google/auth/_default.py b/google/auth/_default.py index 4dc0725e7..66bd1d4ea 100644 --- a/google/auth/_default.py +++ b/google/auth/_default.py @@ -230,6 +230,11 @@ def _get_explicit_environ_credentials(): def _get_gae_credentials(): """Gets Google App Engine App Identity credentials and project ID.""" + # If not GAE gen1, prefer the metadata service even if the GAE APIs are + # available as per AIP-4115. + if os.environ.get(environment_vars.LEGACY_APPENGINE_RUNTIME) != "python27": + return None, None + # While this library is normally bundled with app_engine, there are # some cases where it's not available, so we tolerate ImportError. try: diff --git a/google/auth/environment_vars.py b/google/auth/environment_vars.py index f02774181..d36d6c4af 100644 --- a/google/auth/environment_vars.py +++ b/google/auth/environment_vars.py @@ -60,6 +60,12 @@ The default value is false. Users have to explicitly set this value to true in order to use client certificate to establish a mutual TLS channel.""" +LEGACY_APPENGINE_RUNTIME = "APPENGINE_RUNTIME" +"""Gen1 environment variable defining the App Engine Runtime. + +Used to distinguish between GAE gen1 and GAE gen2+. +""" + # AWS environment variables used with AWS workload identity pools to retrieve # AWS security credentials and the AWS region needed to create a serialized # signed requests to the AWS STS GetCalledIdentity API that can be exchanged diff --git a/tests/test__default.py b/tests/test__default.py index e13689625..94254b8bc 100644 --- a/tests/test__default.py +++ b/tests/test__default.py @@ -447,7 +447,9 @@ def app_identity(monkeypatch): yield app_identity_module -def test__get_gae_credentials(app_identity): +@mock.patch.dict(os.environ) +def test__get_gae_credentials_gen1(app_identity): + os.environ[environment_vars.LEGACY_APPENGINE_RUNTIME] = "python27" app_identity.get_application_id.return_value = mock.sentinel.project credentials, project_id = _default._get_gae_credentials() @@ -455,6 +457,19 @@ def test__get_gae_credentials(app_identity): assert isinstance(credentials, app_engine.Credentials) assert project_id == mock.sentinel.project +@mock.patch.dict(os.environ) +def test__get_gae_credentials_gen2(): + os.environ[environment_vars.LEGACY_APPENGINE_RUNTIME] = "python37" + os.environ["GAE_RUNTIME"] = "python37" + credentials, project_id = _default._get_gae_credentials() + assert credentials is None + assert project_id is None + +def test__get_gae_credentials_env_unset(): + assert environment_vars.LEGACY_APPENGINE_RUNTIME not in os.environ + credentials, project_id = _default._get_gae_credentials() + assert credentials is None + assert project_id is None def test__get_gae_credentials_no_app_engine(): import sys