Skip to content

Commit 873020f

Browse files
feat: support mtls env variables (#589)
Implement the two mtls env variables mentioned in https://google.aip.dev/auth/4114 New behavior summary: (1) GOOGLE_API_USE_CLIENT_CERTIFICATE env variable: Values: "true": use client cert if exists "false" (default): never use client cert, even if it exists or it is explicitly provided by user (2) GOOGLE_API_USE_MTLS_ENDPOINT env variable: Values: "never": use regular endpoint "always": use mtls endpoint "auto" (default): auto switch to mtls endpoint, if client cert exists and we are allowed to use it (controlled by GOOGLE_API_USE_CLIENT_CERTIFICATE)
1 parent 47f7688 commit 873020f

File tree

11 files changed

+471
-489
lines changed

11 files changed

+471
-489
lines changed

packages/gapic-generator/gapic/ads-templates/%namespace/%name/%version/%sub/services/%service/client.py.j2

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
{% block content %}
44
from collections import OrderedDict
5+
from distutils import util
56
import os
67
import re
78
from typing import Callable, Dict, {% if service.any_server_streaming %}Iterable, {% endif %}{% if service.any_client_streaming %}Iterator, {% endif %}Sequence, Tuple, Type, Union
@@ -13,6 +14,7 @@ from google.api_core import gapic_v1 # type: ignore
1314
from google.api_core import retry as retries # type: ignore
1415
from google.auth import credentials # type: ignore
1516
from google.auth.transport import mtls # type: ignore
17+
from google.auth.transport.grpc import SslCredentials # type: ignore
1618
from google.auth.exceptions import MutualTLSChannelError # type: ignore
1719
from google.oauth2 import service_account # type: ignore
1820

@@ -151,16 +153,19 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta):
151153
client_options (ClientOptions): Custom options for the client. It
152154
won't take effect unless ``transport`` is None.
153155
(1) The ``api_endpoint`` property can be used to override the
154-
default endpoint provided by the client. GOOGLE_API_USE_MTLS
156+
default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT
155157
environment variable can also be used to override the endpoint:
156158
"always" (always use the default mTLS endpoint), "never" (always
157-
use the default regular endpoint, this is the default value for
158-
the environment variable) and "auto" (auto switch to the default
159-
mTLS endpoint if client SSL credentials is present). However,
160-
the ``api_endpoint`` property takes precedence if provided.
161-
(2) The ``client_cert_source`` property is used to provide client
162-
SSL credentials for mutual TLS transport. If not provided, the
163-
default SSL credentials will be used if present.
159+
use the default regular endpoint) and "auto" (auto switch to the
160+
default mTLS endpoint if client certificate is present, this is
161+
the default value). However, the ``api_endpoint`` property takes
162+
precedence if provided.
163+
(2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable
164+
is "true", then the ``client_cert_source`` property can be used
165+
to provide client certificate for mutual TLS transport. If
166+
not provided, the default SSL client certificate will be used if
167+
present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not
168+
set, no client certificate will be used.
164169
client_info (google.api_core.gapic_v1.client_info.ClientInfo):
165170
The client info used to send a user-agent string along with
166171
API requests. If ``None``, then default info will be used.
@@ -175,24 +180,40 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta):
175180
client_options = ClientOptions.from_dict(client_options)
176181
if client_options is None:
177182
client_options = ClientOptions.ClientOptions()
183+
184+
# Create SSL credentials for mutual TLS if needed.
185+
use_client_cert = bool(util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")))
186+
187+
ssl_credentials = None
188+
is_mtls = False
189+
if use_client_cert:
190+
if client_options.client_cert_source:
191+
import grpc # type: ignore
192+
193+
cert, key = client_options.client_cert_source()
194+
ssl_credentials = grpc.ssl_channel_credentials(
195+
certificate_chain=cert, private_key=key
196+
)
197+
is_mtls = True
198+
else:
199+
creds = SslCredentials()
200+
is_mtls = creds.is_mtls
201+
ssl_credentials = creds.ssl_credentials if is_mtls else None
178202

179-
if transport is None and client_options.api_endpoint is None:
180-
use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS", "never")
203+
# Figure out which api endpoint to use.
204+
if client_options.api_endpoint is not None:
205+
api_endpoint = client_options.api_endpoint
206+
else:
207+
use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto")
181208
if use_mtls_env == "never":
182-
client_options.api_endpoint = self.DEFAULT_ENDPOINT
209+
api_endpoint = self.DEFAULT_ENDPOINT
183210
elif use_mtls_env == "always":
184-
client_options.api_endpoint = self.DEFAULT_MTLS_ENDPOINT
211+
api_endpoint = self.DEFAULT_MTLS_ENDPOINT
185212
elif use_mtls_env == "auto":
186-
has_client_cert_source = (
187-
client_options.client_cert_source is not None
188-
or mtls.has_default_client_cert_source()
189-
)
190-
client_options.api_endpoint = (
191-
self.DEFAULT_MTLS_ENDPOINT if has_client_cert_source else self.DEFAULT_ENDPOINT
192-
)
213+
api_endpoint = self.DEFAULT_MTLS_ENDPOINT if is_mtls else self.DEFAULT_ENDPOINT
193214
else:
194215
raise MutualTLSChannelError(
195-
"Unsupported GOOGLE_API_USE_MTLS value. Accepted values: never, auto, always"
216+
"Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted values: never, auto, always"
196217
)
197218

198219
# Save or instantiate the transport.
@@ -212,9 +233,8 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta):
212233
else:
213234
self._transport = {{ service.name }}GrpcTransport(
214235
credentials=credentials,
215-
host=client_options.api_endpoint,
216-
api_mtls_endpoint=client_options.api_endpoint,
217-
client_cert_source=client_options.client_cert_source,
236+
host=api_endpoint,
237+
ssl_channel_credentials=ssl_credentials,
218238
client_info=client_info,
219239
)
220240

packages/gapic-generator/gapic/ads-templates/%namespace/%name/%version/%sub/services/%service/transports/grpc.py.j2

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ from google.api_core import operations_v1 # type: ignore
1010
from google.api_core import gapic_v1 # type: ignore
1111
from google import auth # type: ignore
1212
from google.auth import credentials # type: ignore
13-
from google.auth.transport.grpc import SslCredentials # type: ignore
1413

1514

1615
import grpc # type: ignore
@@ -40,8 +39,7 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):
4039
host: str{% if service.host %} = '{{ service.host }}'{% endif %},
4140
credentials: credentials.Credentials = None,
4241
channel: grpc.Channel = None,
43-
api_mtls_endpoint: str = None,
44-
client_cert_source: Callable[[], Tuple[bytes, bytes]] = None,
42+
ssl_channel_credentials: grpc.ChannelCredentials = None,
4543
client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
4644
) -> None:
4745
"""Instantiate the transport.
@@ -57,14 +55,8 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):
5755
This argument is ignored if ``channel`` is provided.
5856
channel (Optional[grpc.Channel]): A ``Channel`` instance through
5957
which to make calls.
60-
api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If
61-
provided, it overrides the ``host`` argument and tries to create
62-
a mutual TLS channel with client SSL credentials from
63-
``client_cert_source`` or applicatin default SSL credentials.
64-
client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): A
65-
callback to provide client SSL certificate bytes and private key
66-
bytes, both in PEM format. It is ignored if ``api_mtls_endpoint``
67-
is None.
58+
ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials
59+
for grpc channel. It is ignored if ``channel`` is provided.
6860
client_info (google.api_core.gapic_v1.client_info.ClientInfo):
6961
The client info used to send a user-agent string along with
7062
API requests. If ``None``, then default info will be used.
@@ -82,27 +74,17 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):
8274

8375
# If a channel was explicitly provided, set it.
8476
self._grpc_channel = channel
85-
elif api_mtls_endpoint:
86-
host = api_mtls_endpoint if ":" in api_mtls_endpoint else api_mtls_endpoint + ":443"
77+
else:
78+
host = host if ":" in host else host + ":443"
8779

8880
if credentials is None:
8981
credentials, _ = auth.default(scopes=self.AUTH_SCOPES)
9082

91-
# Create SSL credentials with client_cert_source or application
92-
# default SSL credentials.
93-
if client_cert_source:
94-
cert, key = client_cert_source()
95-
ssl_credentials = grpc.ssl_channel_credentials(
96-
certificate_chain=cert, private_key=key
97-
)
98-
else:
99-
ssl_credentials = SslCredentials().ssl_credentials
100-
10183
# create a new channel. The provided one is ignored.
10284
self._grpc_channel = grpc_helpers.create_channel(
10385
host,
10486
credentials=credentials,
105-
ssl_credentials=ssl_credentials,
87+
ssl_credentials=ssl_channel_credentials,
10688
scopes=self.AUTH_SCOPES,
10789
)
10890

packages/gapic-generator/gapic/ads-templates/setup.py.j2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ setuptools.setup(
1616
platforms='Posix; MacOS X; Windows',
1717
include_package_data=True,
1818
install_requires=(
19-
'google-api-core >= 1.17.0, < 2.0.0dev',
19+
'google-api-core >= 1.22.2, < 2.0.0dev',
2020
'googleapis-common-protos >= 1.5.8',
2121
'grpcio >= 1.10.0',
2222
'proto-plus >= 1.4.0',

0 commit comments

Comments
 (0)