Merge pull request #22638 from tgalkovskyi/master

Expose ALTS client/server credentials in Python API
pull/22857/head
yihuaz 5 years ago committed by GitHub
commit 4efeb8f1fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 49
      examples/python/data_transmission/BUILD
  2. 39
      examples/python/data_transmission/alts_client.py
  3. 39
      examples/python/data_transmission/alts_server.py
  4. 5
      examples/python/data_transmission/client.py
  5. 1
      examples/python/data_transmission/server.py
  6. 37
      src/python/grpcio/grpc/__init__.py
  7. 6
      src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd.pxi
  8. 29
      src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi
  9. 15
      src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
  10. 11
      src/python/grpcio_tests/tests/interop/client.py
  11. 13
      src/python/grpcio_tests/tests/interop/server.py
  12. 2
      src/python/grpcio_tests/tests/unit/_api_test.py
  13. 2
      src/python/grpcio_tests/tests_aio/interop/client.py
  14. 5
      src/python/grpcio_tests/tests_aio/interop/server.py
  15. 4
      tools/run_tests/run_interop_tests.py

@ -0,0 +1,49 @@
# Copyright 2020 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
licenses(["notice"]) # 3-clause BSD
load("@grpc_python_dependencies//:requirements.bzl", "requirement")
py_binary(
name = "alts_server",
srcs = [
"alts_server.py",
"demo_pb2.py",
"demo_pb2_grpc.py",
"server.py",
],
main = "alts_server.py",
python_version = "PY3",
srcs_version = "PY2AND3",
deps = [
"//src/python/grpcio/grpc:grpcio",
],
)
py_binary(
name = "alts_client",
srcs = [
"alts_client.py",
"client.py",
"demo_pb2.py",
"demo_pb2_grpc.py",
],
main = "alts_client.py",
python_version = "PY3",
srcs_version = "PY2AND3",
deps = [
"//src/python/grpcio/grpc:grpcio",
],
)

@ -0,0 +1,39 @@
# Copyright 2020 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""The example of using ALTS credentials to setup gRPC client.
The example would only successfully run in GCP environment."""
import grpc
import demo_pb2_grpc
from client import (bidirectional_streaming_method, client_streaming_method,
server_streaming_method, simple_method)
SERVER_ADDRESS = "localhost:23333"
def main():
with grpc.secure_channel(
SERVER_ADDRESS,
credentials=grpc.alts_channel_credentials()) as channel:
stub = demo_pb2_grpc.GRPCDemoStub(channel)
simple_method(stub)
client_streaming_method(stub)
server_streaming_method(stub)
bidirectional_streaming_method(stub)
if __name__ == '__main__':
main()

@ -0,0 +1,39 @@
# Copyright 2020 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""The example of using ALTS credentials to setup gRPC server in python.
The example would only successfully run in GCP environment."""
from concurrent import futures
import grpc
import demo_pb2_grpc
from server import DemoServer
SERVER_ADDRESS = 'localhost:23333'
def main():
svr = grpc.server(futures.ThreadPoolExecutor())
demo_pb2_grpc.add_GRPCDemoServicer_to_server(DemoServer(), svr)
svr.add_secure_port(SERVER_ADDRESS,
server_credentials=grpc.alts_server_credentials())
print("------------------start Python GRPC server with ALTS encryption")
svr.start()
svr.wait_for_termination()
if __name__ == '__main__':
main()

@ -19,6 +19,11 @@ import grpc
import demo_pb2_grpc import demo_pb2_grpc
import demo_pb2 import demo_pb2
__all__ = [
'simple_method', 'client_streaming_method', 'server_streaming_method',
'bidirectional_streaming_method'
]
SERVER_ADDRESS = "localhost:23333" SERVER_ADDRESS = "localhost:23333"
CLIENT_ID = 1 CLIENT_ID = 1

@ -20,6 +20,7 @@ import grpc
import demo_pb2_grpc import demo_pb2_grpc
import demo_pb2 import demo_pb2
__all__ = 'DemoServer'
SERVER_ADDRESS = 'localhost:23333' SERVER_ADDRESS = 'localhost:23333'
SERVER_ID = 1 SERVER_ID = 1

@ -1833,6 +1833,41 @@ def local_server_credentials(local_connect_type=LocalConnectionType.LOCAL_TCP):
_cygrpc.server_credentials_local(local_connect_type.value)) _cygrpc.server_credentials_local(local_connect_type.value))
def alts_channel_credentials(service_accounts=None):
"""Creates a ChannelCredentials for use with an ALTS-enabled Channel.
This is an EXPERIMENTAL API.
ALTS credentials API can only be used in GCP environment as it relies on
handshaker service being available. For more info about ALTS see
https://cloud.google.com/security/encryption-in-transit/application-layer-transport-security
Args:
service_accounts: A list of server identities accepted by the client.
If target service accounts are provided and none of them matches the
peer identity of the server, handshake will fail. The arg can be empty
if the client does not have any information about trusted server
identity.
Returns:
A ChannelCredentials for use with an ALTS-enabled Channel
"""
return ChannelCredentials(
_cygrpc.channel_credentials_alts(service_accounts or []))
def alts_server_credentials():
"""Creates a ServerCredentials for use with an ALTS-enabled connection.
This is an EXPERIMENTAL API.
ALTS credentials API can only be used in GCP environment as it relies on
handshaker service being available. For more info about ALTS see
https://cloud.google.com/security/encryption-in-transit/application-layer-transport-security
Returns:
A ServerCredentials for use with an ALTS-enabled Server
"""
return ServerCredentials(_cygrpc.server_credentials_alts())
def channel_ready_future(channel): def channel_ready_future(channel):
"""Creates a Future that tracks when a Channel is ready. """Creates a Future that tracks when a Channel is ready.
@ -2036,6 +2071,8 @@ __all__ = (
'composite_channel_credentials', 'composite_channel_credentials',
'local_channel_credentials', 'local_channel_credentials',
'local_server_credentials', 'local_server_credentials',
'alts_channel_credentials',
'alts_server_credentials',
'ssl_server_credentials', 'ssl_server_credentials',
'ssl_server_certificate_configuration', 'ssl_server_certificate_configuration',
'dynamic_ssl_server_credentials', 'dynamic_ssl_server_credentials',

@ -102,3 +102,9 @@ cdef class ServerCredentials:
cdef class LocalChannelCredentials(ChannelCredentials): cdef class LocalChannelCredentials(ChannelCredentials):
cdef grpc_local_connect_type _local_connect_type cdef grpc_local_connect_type _local_connect_type
cdef class ALTSChannelCredentials(ChannelCredentials):
cdef grpc_alts_credentials_options *c_options
cdef grpc_channel_credentials *c(self) except *

@ -351,3 +351,32 @@ def server_credentials_local(grpc_local_connect_type local_connect_type):
cdef ServerCredentials credentials = ServerCredentials() cdef ServerCredentials credentials = ServerCredentials()
credentials.c_credentials = grpc_local_server_credentials_create(local_connect_type) credentials.c_credentials = grpc_local_server_credentials_create(local_connect_type)
return credentials return credentials
cdef class ALTSChannelCredentials(ChannelCredentials):
def __cinit__(self, list service_accounts):
self.c_options = grpc_alts_credentials_client_options_create()
cdef str account
for account in service_accounts:
grpc_alts_credentials_client_options_add_target_service_account(self.c_options, account)
def __dealloc__(self):
if self.c_options != NULL:
grpc_alts_credentials_options_destroy(self.c_options)
cdef grpc_channel_credentials *c(self) except *:
return grpc_alts_credentials_create(self.c_options)
def channel_credentials_alts(list service_accounts):
return ALTSChannelCredentials(service_accounts)
def server_credentials_alts():
cdef ServerCredentials credentials = ServerCredentials()
cdef grpc_alts_credentials_options* c_options = grpc_alts_credentials_server_options_create()
credentials.c_credentials = grpc_alts_server_credentials_create(c_options)
# Options can be destroyed as deep copy was performed.
grpc_alts_credentials_options_destroy(c_options)
return credentials

@ -606,6 +606,21 @@ cdef extern from "grpc/grpc_security.h":
grpc_server_credentials *grpc_local_server_credentials_create( grpc_server_credentials *grpc_local_server_credentials_create(
grpc_local_connect_type type) grpc_local_connect_type type)
ctypedef struct grpc_alts_credentials_options:
# We don't care about the internals (and in fact don't know them)
pass
grpc_channel_credentials *grpc_alts_credentials_create(
const grpc_alts_credentials_options *options)
grpc_server_credentials *grpc_alts_server_credentials_create(
const grpc_alts_credentials_options *options)
grpc_alts_credentials_options* grpc_alts_credentials_client_options_create()
grpc_alts_credentials_options* grpc_alts_credentials_server_options_create()
void grpc_alts_credentials_options_destroy(grpc_alts_credentials_options *options)
void grpc_alts_credentials_client_options_add_target_service_account(grpc_alts_credentials_options *options, const char *service_account)
cdef extern from "grpc/compression.h": cdef extern from "grpc/compression.h":

@ -43,6 +43,10 @@ def parse_interop_client_args():
default=False, default=False,
type=resources.parse_bool, type=resources.parse_bool,
help='require a secure connection') help='require a secure connection')
parser.add_argument('--use_alts',
default=False,
type=resources.parse_bool,
help='require an ALTS secure connection')
parser.add_argument('--use_test_ca', parser.add_argument('--use_test_ca',
default=False, default=False,
type=resources.parse_bool, type=resources.parse_bool,
@ -85,6 +89,8 @@ def _create_call_credentials(args):
def get_secure_channel_parameters(args): def get_secure_channel_parameters(args):
call_credentials = _create_call_credentials(args) call_credentials = _create_call_credentials(args)
channel_opts = None
if args.use_tls:
if args.use_test_ca: if args.use_test_ca:
root_certificates = resources.test_root_certificates() root_certificates = resources.test_root_certificates()
else: else:
@ -95,12 +101,13 @@ def get_secure_channel_parameters(args):
channel_credentials = grpc.composite_channel_credentials( channel_credentials = grpc.composite_channel_credentials(
channel_credentials, call_credentials) channel_credentials, call_credentials)
channel_opts = None
if args.server_host_override: if args.server_host_override:
channel_opts = (( channel_opts = ((
'grpc.ssl_target_name_override', 'grpc.ssl_target_name_override',
args.server_host_override, args.server_host_override,
),) ),)
elif args.use_alts:
channel_credentials = grpc.alts_channel_credentials()
return channel_credentials, channel_opts return channel_credentials, channel_opts
@ -108,7 +115,7 @@ def get_secure_channel_parameters(args):
def _create_channel(args): def _create_channel(args):
target = '{}:{}'.format(args.server_host, args.server_port) target = '{}:{}'.format(args.server_host, args.server_port)
if args.use_tls: if args.use_tls or args.use_alts:
channel_credentials, options = get_secure_channel_parameters(args) channel_credentials, options = get_secure_channel_parameters(args)
return grpc.secure_channel(target, channel_credentials, options) return grpc.secure_channel(target, channel_credentials, options)
else: else:

@ -38,13 +38,20 @@ def parse_interop_server_arguments():
default=False, default=False,
type=resources.parse_bool, type=resources.parse_bool,
help='require a secure connection') help='require a secure connection')
parser.add_argument('--use_alts',
default=False,
type=resources.parse_bool,
help='require an ALTS connection')
return parser.parse_args() return parser.parse_args()
def get_server_credentials(): def get_server_credentials(use_tls):
if use_tls:
private_key = resources.private_key() private_key = resources.private_key()
certificate_chain = resources.certificate_chain() certificate_chain = resources.certificate_chain()
return grpc.ssl_server_credentials(((private_key, certificate_chain),)) return grpc.ssl_server_credentials(((private_key, certificate_chain),))
else:
return grpc.alts_server_credentials()
def serve(): def serve():
@ -53,8 +60,8 @@ def serve():
server = test_common.test_server() server = test_common.test_server()
test_pb2_grpc.add_TestServiceServicer_to_server(service.TestService(), test_pb2_grpc.add_TestServiceServicer_to_server(service.TestService(),
server) server)
if args.use_tls: if args.use_tls or args.use_alts:
credentials = get_server_credentials() credentials = get_server_credentials(args.use_tls)
server.add_secure_port('[::]:{}'.format(args.port), credentials) server.add_secure_port('[::]:{}'.format(args.port), credentials)
else: else:
server.add_insecure_port('[::]:{}'.format(args.port)) server.add_insecure_port('[::]:{}'.format(args.port))

@ -63,6 +63,8 @@ class AllTest(unittest.TestCase):
'LocalConnectionType', 'LocalConnectionType',
'local_channel_credentials', 'local_channel_credentials',
'local_server_credentials', 'local_server_credentials',
'alts_channel_credentials',
'alts_server_credentials',
'unary_unary_rpc_method_handler', 'unary_unary_rpc_method_handler',
'unary_stream_rpc_method_handler', 'unary_stream_rpc_method_handler',
'stream_unary_rpc_method_handler', 'stream_unary_rpc_method_handler',

@ -30,7 +30,7 @@ _LOGGER.setLevel(logging.DEBUG)
def _create_channel(args): def _create_channel(args):
target = f'{args.server_host}:{args.server_port}' target = f'{args.server_host}:{args.server_port}'
if args.use_tls: if args.use_tls or args.use_alts:
channel_credentials, options = interop_client_lib.get_secure_channel_parameters( channel_credentials, options = interop_client_lib.get_secure_channel_parameters(
args) args)
return aio.secure_channel(target, channel_credentials, options) return aio.secure_channel(target, channel_credentials, options)

@ -30,9 +30,8 @@ _LOGGER.setLevel(logging.DEBUG)
async def serve(): async def serve():
args = interop_server_lib.parse_interop_server_arguments() args = interop_server_lib.parse_interop_server_arguments()
if args.use_tls: if args.use_tls or args.use_alts:
credentials = interop_server_lib.get_server_credentials() credentials = interop_server_lib.get_server_credentials(args.use_tls)
address, server = await _test_server.start_test_server( address, server = await _test_server.start_test_server(
port=args.port, secure=True, server_credentials=credentials) port=args.port, secure=True, server_credentials=credentials)
else: else:

@ -786,9 +786,9 @@ _LANGUAGES_WITH_HTTP2_CLIENTS_FOR_HTTP2_SERVER_TEST_CASES = [
'java', 'go', 'python', 'c++' 'java', 'go', 'python', 'c++'
] ]
_LANGUAGES_FOR_ALTS_TEST_CASES = ['java', 'go', 'c++'] _LANGUAGES_FOR_ALTS_TEST_CASES = ['java', 'go', 'c++', 'python']
_SERVERS_FOR_ALTS_TEST_CASES = ['java', 'go', 'c++'] _SERVERS_FOR_ALTS_TEST_CASES = ['java', 'go', 'c++', 'python']
_TRANSPORT_SECURITY_OPTIONS = ['tls', 'alts', 'insecure'] _TRANSPORT_SECURITY_OPTIONS = ['tls', 'alts', 'insecure']

Loading…
Cancel
Save