From fec2c4faedc060ea571eed46e0d4fc426f7ed0ac Mon Sep 17 00:00:00 2001 From: Andy Zhao Date: Wed, 26 Nov 2025 11:18:49 -0800 Subject: [PATCH 1/4] fix(auth): Delegate workload cert and key default loading to helper --- google/auth/transport/_mtls_helper.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/google/auth/transport/_mtls_helper.py b/google/auth/transport/_mtls_helper.py index 7740f2fe8..7b2b0407f 100644 --- a/google/auth/transport/_mtls_helper.py +++ b/google/auth/transport/_mtls_helper.py @@ -279,7 +279,7 @@ def _run_cert_provider_command(command, expect_encrypted_key=False): def get_client_ssl_credentials( generate_encrypted_key=False, context_aware_metadata_path=CONTEXT_AWARE_METADATA_PATH, - certificate_config_path=CERTIFICATE_CONFIGURATION_DEFAULT_PATH, + certificate_config_path=None, ): """Returns the client side certificate, private key and passphrase. @@ -306,13 +306,10 @@ def get_client_ssl_credentials( the cert, key and passphrase. """ - # 1. Check for certificate config json. - cert_config_path = _check_config_path(certificate_config_path) - if cert_config_path: - # Attempt to retrieve X.509 Workload cert and key. - cert, key = _get_workload_cert_and_key(cert_config_path) - if cert and key: - return True, cert, key, None + # 1. Attempt to retrieve X.509 Workload cert and key. + cert, key = _get_workload_cert_and_key(certificate_config_path) + if cert and key: + return True, cert, key, None # 2. Check for context aware metadata json metadata_path = _check_config_path(context_aware_metadata_path) From 0b5a5b5fb40249624fa0505f971a2c5ef406911e Mon Sep 17 00:00:00 2001 From: Andy Zhao Date: Wed, 26 Nov 2025 15:17:59 -0800 Subject: [PATCH 2/4] fix(auth): Update unit tests --- tests/transport/test__mtls_helper.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/transport/test__mtls_helper.py b/tests/transport/test__mtls_helper.py index 01d5e3a40..63c742c1f 100644 --- a/tests/transport/test__mtls_helper.py +++ b/tests/transport/test__mtls_helper.py @@ -334,9 +334,15 @@ def test_success_with_certificate_config( assert key == pytest.private_key_bytes assert passphrase is None + @mock.patch( + "google.auth.transport._mtls_helper._get_workload_cert_and_key", autospec=True + ) @mock.patch("google.auth.transport._mtls_helper._check_config_path", autospec=True) - def test_success_without_metadata(self, mock_check_config_path): + def test_success_without_metadata( + self, mock_check_config_path, mock_get_workload_cert_and_key + ): mock_check_config_path.return_value = False + mock_get_workload_cert_and_key.return_value = (None, None) has_cert, cert, key, passphrase = _mtls_helper.get_client_ssl_credentials() assert not has_cert assert cert is None @@ -395,12 +401,17 @@ def test_missing_cert_command( ) @mock.patch("google.auth.transport._mtls_helper._load_json_file", autospec=True) @mock.patch("google.auth.transport._mtls_helper._check_config_path", autospec=True) + @mock.patch( + "google.auth.transport._mtls_helper._get_workload_cert_and_key", autospec=True + ) def test_customize_context_aware_metadata_path( self, + mock_get_workload_cert_and_key, mock_check_config_path, mock_load_json_file, mock_run_cert_provider_command, ): + mock_get_workload_cert_and_key.return_value = (None, None) context_aware_metadata_path = "/path/to/metata/data" mock_check_config_path.return_value = context_aware_metadata_path mock_load_json_file.return_value = {"cert_provider_command": ["command"]} From 06d90458a9b1bd5b78cf0646b7d3cfdd63324f95 Mon Sep 17 00:00:00 2001 From: Andy Zhao Date: Mon, 1 Dec 2025 21:49:32 -0800 Subject: [PATCH 3/4] fix(auth): Add temporary patch to workload cert logic to accomodate Cloud Run mis-configuration --- google/auth/transport/_mtls_helper.py | 32 ++++++++++++++++++++ tests/transport/test__mtls_helper.py | 42 +++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/google/auth/transport/_mtls_helper.py b/google/auth/transport/_mtls_helper.py index 7b2b0407f..2a40701e4 100644 --- a/google/auth/transport/_mtls_helper.py +++ b/google/auth/transport/_mtls_helper.py @@ -47,6 +47,20 @@ b"-----BEGIN PASSPHRASE-----(.+)-----END PASSPHRASE-----", re.DOTALL ) +# Temporary patch to accomodate incorrect cert config in Cloud Run prod environment. +_WELL_KNOWN_CLOUD_RUN_CERT_PATH = ( + "/var/run/secrets/workload-spiffe-credentials/certificates.pem" +) +_WELL_KNOWN_CLOUD_RUN_KEY_PATH = ( + "/var/run/secrets/workload-spiffe-credentials/private_key.pem" +) +_INCORRECT_CLOUD_RUN_CERT_PATH = ( + "/var/lib/volumes/certificate/workload-certificates/certificates.pem" +) +_INCORRECT_CLOUD_RUN_KEY_PATH = ( + "/var/lib/volumes/certificate/workload-certificates/private_key.pem" +) + def _check_config_path(config_path): """Checks for config file path. If it exists, returns the absolute path with user expansion; @@ -183,6 +197,24 @@ def _get_workload_cert_and_key_paths(config_path): ) key_path = workload["key_path"] + # == BEGIN Temporary Cloud Run PATCH == + if (cert_path == _INCORRECT_CLOUD_RUN_CERT_PATH) and ( + key_path == _INCORRECT_CLOUD_RUN_KEY_PATH + ): + if not path.exists(cert_path) and not path.exists(key_path): + _LOGGER.debug( + "Applying Cloud Run certificate path patch. " + "Configured paths not found: %s, %s. " + "Using well-known paths: %s, %s", + cert_path, + key_path, + _WELL_KNOWN_CLOUD_RUN_CERT_PATH, + _WELL_KNOWN_CLOUD_RUN_KEY_PATH, + ) + cert_path = _WELL_KNOWN_CLOUD_RUN_CERT_PATH + key_path = _WELL_KNOWN_CLOUD_RUN_KEY_PATH + # == END Temporary Cloud Run PATCH == + return cert_path, key_path diff --git a/tests/transport/test__mtls_helper.py b/tests/transport/test__mtls_helper.py index 63c742c1f..402f454f7 100644 --- a/tests/transport/test__mtls_helper.py +++ b/tests/transport/test__mtls_helper.py @@ -334,6 +334,48 @@ def test_success_with_certificate_config( assert key == pytest.private_key_bytes assert passphrase is None + @mock.patch( + "google.auth.transport._mtls_helper._read_cert_and_key_files", autospec=True + ) + @mock.patch( + "google.auth.transport._mtls_helper._get_cert_config_path", autospec=True + ) + @mock.patch("google.auth.transport._mtls_helper._load_json_file", autospec=True) + @mock.patch("google.auth.transport._mtls_helper._check_config_path", autospec=True) + def test_success_with_certificate_config_cloud_run_patch( + self, + mock_check_config_path, + mock_load_json_file, + mock_get_cert_config_path, + mock_read_cert_and_key_files, + ): + cert_config_path = "/path/to/config" + mock_check_config_path.return_value = cert_config_path + mock_load_json_file.return_value = { + "cert_configs": { + "workload": { + "cert_path": _mtls_helper._INCORRECT_CLOUD_RUN_CERT_PATH, + "key_path": _mtls_helper._INCORRECT_CLOUD_RUN_KEY_PATH, + } + } + } + mock_get_cert_config_path.return_value = cert_config_path + mock_read_cert_and_key_files.return_value = ( + pytest.public_cert_bytes, + pytest.private_key_bytes, + ) + + has_cert, cert, key, passphrase = _mtls_helper.get_client_ssl_credentials() + assert has_cert + assert cert == pytest.public_cert_bytes + assert key == pytest.private_key_bytes + assert passphrase is None + + mock_read_cert_and_key_files.assert_called_once_with( + _mtls_helper._WELL_KNOWN_CLOUD_RUN_CERT_PATH, + _mtls_helper._WELL_KNOWN_CLOUD_RUN_KEY_PATH, + ) + @mock.patch( "google.auth.transport._mtls_helper._get_workload_cert_and_key", autospec=True ) From 8990f869b758c6917939f4bd2dacbc5351b0127f Mon Sep 17 00:00:00 2001 From: Andy Zhao Date: Wed, 3 Dec 2025 21:54:20 -0800 Subject: [PATCH 4/4] fix(auth): Add additional test coverage and comments --- google/auth/transport/_mtls_helper.py | 1 + tests/transport/test__mtls_helper.py | 45 +++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/google/auth/transport/_mtls_helper.py b/google/auth/transport/_mtls_helper.py index 2a40701e4..f5d6b6724 100644 --- a/google/auth/transport/_mtls_helper.py +++ b/google/auth/transport/_mtls_helper.py @@ -198,6 +198,7 @@ def _get_workload_cert_and_key_paths(config_path): key_path = workload["key_path"] # == BEGIN Temporary Cloud Run PATCH == + # See https://github.com/googleapis/google-auth-library-python/issues/1881 if (cert_path == _INCORRECT_CLOUD_RUN_CERT_PATH) and ( key_path == _INCORRECT_CLOUD_RUN_KEY_PATH ): diff --git a/tests/transport/test__mtls_helper.py b/tests/transport/test__mtls_helper.py index 402f454f7..2a7a524b1 100644 --- a/tests/transport/test__mtls_helper.py +++ b/tests/transport/test__mtls_helper.py @@ -376,6 +376,51 @@ def test_success_with_certificate_config_cloud_run_patch( _mtls_helper._WELL_KNOWN_CLOUD_RUN_KEY_PATH, ) + @mock.patch("os.path.exists", autospec=True) + @mock.patch( + "google.auth.transport._mtls_helper._read_cert_and_key_files", autospec=True + ) + @mock.patch( + "google.auth.transport._mtls_helper._get_cert_config_path", autospec=True + ) + @mock.patch("google.auth.transport._mtls_helper._load_json_file", autospec=True) + @mock.patch("google.auth.transport._mtls_helper._check_config_path", autospec=True) + def test_success_with_certificate_config_cloud_run_patch_skipped_if_cert_exists( + self, + mock_check_config_path, + mock_load_json_file, + mock_get_cert_config_path, + mock_read_cert_and_key_files, + mock_os_path_exists, + ): + cert_config_path = "/path/to/config" + mock_check_config_path.return_value = cert_config_path + mock_os_path_exists.return_value = True + mock_load_json_file.return_value = { + "cert_configs": { + "workload": { + "cert_path": _mtls_helper._INCORRECT_CLOUD_RUN_CERT_PATH, + "key_path": _mtls_helper._INCORRECT_CLOUD_RUN_KEY_PATH, + } + } + } + mock_get_cert_config_path.return_value = cert_config_path + mock_read_cert_and_key_files.return_value = ( + pytest.public_cert_bytes, + pytest.private_key_bytes, + ) + + has_cert, cert, key, passphrase = _mtls_helper.get_client_ssl_credentials() + assert has_cert + assert cert == pytest.public_cert_bytes + assert key == pytest.private_key_bytes + assert passphrase is None + + mock_read_cert_and_key_files.assert_called_once_with( + _mtls_helper._INCORRECT_CLOUD_RUN_CERT_PATH, + _mtls_helper._INCORRECT_CLOUD_RUN_KEY_PATH, + ) + @mock.patch( "google.auth.transport._mtls_helper._get_workload_cert_and_key", autospec=True )