Skip to content

Commit f8bf6f9

Browse files
feat: Auto enable mTLS when supported certificates are detected (#869)
The Python SDK will use a hybrid approach for mTLS enablement: If the GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable is set (either true or false or any value), the SDK will respect that setting. This is necessary for test scenarios and users who need to explicitly control mTLS behavior. If the GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable is not set, the SDK will automatically enable mTLS only if it detects Managed Workload Identity (MWID) or X.509 Workforce Identity Federation (WIF) certificate sources. In other cases where the variable is not set, mTLS will remain disabled. --------- Signed-off-by: Radhika Agrawal <[email protected]>
1 parent f0188c6 commit f8bf6f9

File tree

2 files changed

+37
-11
lines changed

2 files changed

+37
-11
lines changed

google/api_core/operations_v1/abstract_operations_base_client.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -300,16 +300,22 @@ def __init__(
300300
client_options = client_options_lib.ClientOptions()
301301

302302
# Create SSL credentials for mutual TLS if needed.
303-
use_client_cert = os.getenv(
304-
"GOOGLE_API_USE_CLIENT_CERTIFICATE", "false"
305-
).lower()
306-
if use_client_cert not in ("true", "false"):
307-
raise ValueError(
308-
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
309-
)
303+
if hasattr(mtls, "should_use_client_cert"):
304+
use_client_cert = mtls.should_use_client_cert()
305+
else:
306+
# if unsupported, fallback to reading from env var
307+
use_client_cert_str = os.getenv(
308+
"GOOGLE_API_USE_CLIENT_CERTIFICATE", "false"
309+
).lower()
310+
if use_client_cert_str not in ("true", "false"):
311+
raise ValueError(
312+
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be"
313+
" either `true` or `false`"
314+
)
315+
use_client_cert = use_client_cert_str == "true"
310316
client_cert_source_func = None
311317
is_mtls = False
312-
if use_client_cert == "true":
318+
if use_client_cert:
313319
if client_options.client_cert_source:
314320
is_mtls = True
315321
client_cert_source_func = client_options.client_cert_source

tests/unit/operations_v1/test_operations_rest_client.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,15 @@
3535
from google.api_core import client_options
3636
from google.api_core import exceptions as core_exceptions
3737
from google.api_core import gapic_v1
38+
from google.api_core import parse_version_to_tuple
3839
from google.api_core.operations_v1 import AbstractOperationsClient
3940

4041
import google.auth
4142
from google.api_core.operations_v1 import pagers
4243
from google.api_core.operations_v1 import pagers_async
4344
from google.api_core.operations_v1 import transports
4445
from google.auth import credentials as ga_credentials
46+
from google.auth import __version__ as auth_version
4547
from google.auth.exceptions import MutualTLSChannelError
4648
from google.longrunning import operations_pb2
4749
from google.oauth2 import service_account
@@ -345,12 +347,30 @@ def test_operations_client_client_options(
345347
with pytest.raises(MutualTLSChannelError):
346348
client = client_class()
347349

348-
# Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value.
350+
# Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value
349351
with mock.patch.dict(
350352
os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"}
351353
):
352-
with pytest.raises(ValueError):
353-
client = client_class()
354+
# Test behavior for google.auth versions < 2.43.0.
355+
# These versions do not have the updated mtls.should_use_client_cert logic.
356+
# Verify that a ValueError is raised when GOOGLE_API_USE_CLIENT_CERTIFICATE
357+
# is set to an unsupported value, as expected in these older versions.
358+
if parse_version_to_tuple(auth_version) < (2, 43, 0):
359+
with pytest.raises(ValueError):
360+
client = client_class()
361+
# Test behavior for google.auth versions >= 2.43.0.
362+
# In these versions, if GOOGLE_API_USE_CLIENT_CERTIFICATE is set to an
363+
# unsupported value (e.g., not 'true' or 'false'), the expected behavior
364+
# of the internal google.auth.mtls.should_use_client_cert() function
365+
# is to return False. Expect should_use_client_cert to return False, so
366+
# client creation should proceed without requiring a client certificate.
367+
else:
368+
with mock.patch.object(transport_class, "__init__") as patched:
369+
patched.return_value = None
370+
client = client_class(
371+
credentials=ga_credentials.AnonymousCredentials(),
372+
transport=transport_name,
373+
)
354374

355375
# Check the case quota_project_id is provided
356376
options = client_options.ClientOptions(quota_project_id="octopus")

0 commit comments

Comments
 (0)