Skip to content

Commit 4e91c54

Browse files
feat: Auto enable mTLS when supported certificates are detected (#1637)
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]> Co-authored-by: Chandra Shekhar Sirimala <[email protected]>
1 parent 4c6d549 commit 4e91c54

File tree

4 files changed

+268
-50
lines changed

4 files changed

+268
-50
lines changed

google/cloud/_storage_v2/services/storage/client.py

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,34 @@ def _get_default_mtls_endpoint(api_endpoint):
184184
_DEFAULT_ENDPOINT_TEMPLATE = "storage.{UNIVERSE_DOMAIN}"
185185
_DEFAULT_UNIVERSE = "googleapis.com"
186186

187+
@staticmethod
188+
def _use_client_cert_effective():
189+
"""Returns whether client certificate should be used for mTLS if the
190+
google-auth version supports should_use_client_cert automatic mTLS enablement.
191+
192+
Alternatively, read from the GOOGLE_API_USE_CLIENT_CERTIFICATE env var.
193+
194+
Returns:
195+
bool: whether client certificate should be used for mTLS
196+
Raises:
197+
ValueError: (If using a version of google-auth without should_use_client_cert and
198+
GOOGLE_API_USE_CLIENT_CERTIFICATE is set to an unexpected value.)
199+
"""
200+
# check if google-auth version supports should_use_client_cert for automatic mTLS enablement
201+
if hasattr(mtls, "should_use_client_cert"):
202+
return mtls.should_use_client_cert()
203+
else:
204+
# if unsupported, fallback to reading from env var
205+
use_client_cert_str = os.getenv(
206+
"GOOGLE_API_USE_CLIENT_CERTIFICATE", "false"
207+
).lower()
208+
if use_client_cert_str not in ("true", "false"):
209+
raise ValueError(
210+
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be"
211+
" either `true` or `false`"
212+
)
213+
return use_client_cert_str == "true"
214+
187215
@classmethod
188216
def from_service_account_info(cls, info: dict, *args, **kwargs):
189217
"""Creates an instance of this client using the provided credentials
@@ -390,20 +418,16 @@ def get_mtls_endpoint_and_cert_source(
390418
)
391419
if client_options is None:
392420
client_options = client_options_lib.ClientOptions()
393-
use_client_cert = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")
421+
use_client_cert = StorageClient._use_client_cert_effective()
394422
use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto")
395-
if use_client_cert not in ("true", "false"):
396-
raise ValueError(
397-
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
398-
)
399423
if use_mtls_endpoint not in ("auto", "never", "always"):
400424
raise MutualTLSChannelError(
401425
"Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
402426
)
403427

404428
# Figure out the client cert source to use.
405429
client_cert_source = None
406-
if use_client_cert == "true":
430+
if use_client_cert:
407431
if client_options.client_cert_source:
408432
client_cert_source = client_options.client_cert_source
409433
elif mtls.has_default_client_cert_source():
@@ -435,20 +459,14 @@ def _read_environment_variables():
435459
google.auth.exceptions.MutualTLSChannelError: If GOOGLE_API_USE_MTLS_ENDPOINT
436460
is not any of ["auto", "never", "always"].
437461
"""
438-
use_client_cert = os.getenv(
439-
"GOOGLE_API_USE_CLIENT_CERTIFICATE", "false"
440-
).lower()
462+
use_client_cert = StorageClient._use_client_cert_effective()
441463
use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto").lower()
442464
universe_domain_env = os.getenv("GOOGLE_CLOUD_UNIVERSE_DOMAIN")
443-
if use_client_cert not in ("true", "false"):
444-
raise ValueError(
445-
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
446-
)
447465
if use_mtls_endpoint not in ("auto", "never", "always"):
448466
raise MutualTLSChannelError(
449467
"Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
450468
)
451-
return use_client_cert == "true", use_mtls_endpoint, universe_domain_env
469+
return use_client_cert, use_mtls_endpoint, universe_domain_env
452470

453471
@staticmethod
454472
def _get_client_cert_source(provided_cert_source, use_cert_flag):

google/cloud/storage/_helpers.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,6 @@ def _virtual_hosted_style_base_url(url, bucket, trailing_slash=False):
111111
return base_url
112112

113113

114-
def _use_client_cert():
115-
return os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE") == "true"
116-
117-
118114
def _get_environ_project():
119115
return os.getenv(
120116
environment_vars.PROJECT,

google/cloud/storage/client.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@
2020
import datetime
2121
import functools
2222
import json
23+
import os
2324
import warnings
2425
import google.api_core.client_options
2526

2627
from google.auth.credentials import AnonymousCredentials
27-
28+
from google.auth.transport import mtls
2829
from google.api_core import page_iterator
2930
from google.cloud._helpers import _LocalStack
3031
from google.cloud.client import ClientWithProject
@@ -35,7 +36,6 @@
3536
from google.cloud.storage._helpers import _get_api_endpoint_override
3637
from google.cloud.storage._helpers import _get_environ_project
3738
from google.cloud.storage._helpers import _get_storage_emulator_override
38-
from google.cloud.storage._helpers import _use_client_cert
3939
from google.cloud.storage._helpers import _virtual_hosted_style_base_url
4040
from google.cloud.storage._helpers import _DEFAULT_UNIVERSE_DOMAIN
4141
from google.cloud.storage._helpers import _DEFAULT_SCHEME
@@ -218,7 +218,15 @@ def __init__(
218218
# The final decision of whether to use mTLS takes place in
219219
# google-auth-library-python. We peek at the environment variable
220220
# here only to issue an exception in case of a conflict.
221-
if _use_client_cert():
221+
use_client_cert = False
222+
if hasattr(mtls, "should_use_client_cert"):
223+
use_client_cert = mtls.should_use_client_cert()
224+
else:
225+
use_client_cert = (
226+
os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE") == "true"
227+
)
228+
229+
if use_client_cert:
222230
raise ValueError(
223231
'The "GOOGLE_API_USE_CLIENT_CERTIFICATE" env variable is '
224232
'set to "true" and a non-default universe domain is '

0 commit comments

Comments
 (0)