【发布时间】:2020-07-13 02:55:04
【问题描述】:
上下文
我用 Java 开发了一个 gRPC 服务器,并用 C# 开发了一个相应的 gRPC 客户端。目标是从部署在 Windows 机器上的多个 gRPC 客户端调用 gRPC 服务器。
了解了 Azure、AWS 和 Google 云平台 (GCP) 如何支持 gRPC,我可能会在 GCP 上托管 gRPC 服务器。因此,我目前正在测试 Google 在gRPC on Compute Engine 教程中描述的 gRPC 服务器的部署方案。简而言之,这意味着 gRPC 服务器在 Google Compute Engine (GCE) 虚拟机 (VM) 上的定制 Docker 容器中运行,紧邻可扩展服务代理 (ESP),后者在自己的预配置 Docker 中运行容器在同一个虚拟机上。
在生产中使用的一个重要方面是能够使用 SSL/TSL 在 gRPC 客户端和 gRPC 服务器之间建立安全通信通道。这就是我在 云托管场景 中遇到问题的地方(但在自托管场景中却没有,这很好用)。
到目前为止有什么效果?
在我的本地 Windows 10 机器上运行的 gRPC 客户端与 gRPC 服务器成功通信:
通过安全 SSL/TLS 通道,以防我在本地 Windows 10 计算机上自托管服务器;和
通过不安全的通道,以防我如上所述在GCE 上托管服务器。
我已在 GCE VM 上发出以下命令来创建 docker 容器,以便通过不安全通道成功进行客户端-服务器通信。
# Create the container network.
sudo docker network create --driver bridge esp_net
# Create dss-signer container from docker image.
# The Java gRPC service listens on port 50051 (see esp container below).
sudo docker run \
--detach \
--name=dss-signer \
--net=esp_net \
gcr.io/[my-project-name]/dss-signer:1.1
# Create Extensible Service Proxy (ESP) container from predefined docker image.
# The ESP container's port 9000 is published as port 80 on the host machine,
# meaning a client will have to connect to port 80 on the host machine.
sudo docker run \
--detach \
--name=esp \
--publish 80:9000 \
--net esp_net \
gcr.io/endpoints-release/endpoints-runtime:1 \
--service=signer.endpoints.[my-project-name].cloud.goog \
--rollout_strategy=managed \
--http2_port=9000 \
--backend=grpc://dss-signer:50051
因此,我认为它通常可以工作,并且 Java 或 C# 代码本身没有问题(除了与使其通过 SSL/TLS 工作相关的配置相关问题之外)。
什么不起作用?
按照Enabling SSL 上的描述,我未能在 gRPC 客户端和服务器之间建立安全通道。我的 C# 客户端总是抛出以下异常:
Grpc.Core.RpcException
HResult=0x80131500
Message=Status(StatusCode=Unavailable, Detail="failed to connect to all addresses")
Source=mscorlib
StackTrace:
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.ConfiguredTaskAwaitable.ConfiguredTaskAwaiter.GetResult()
at SignerClient.AbstractSignatureHandler.<InitiateCall>d__4.MoveNext()
[Rest of stack trace removed as it did not contain any helpful hints.]
我尝试了什么?
这是用于创建服务器密钥和证书的openssl 命令。对于公用名,我使用的是 Google Cloud Console 中显示的 GCE VM 的 IP 地址。我已将密钥和证书复制到/etc/nginx/ssl 目录。
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ./nginx.key -out ./nginx.crt -subj "/O=[My Org]/OU=Servers/CN=[GCE VM IP Address]"
这是用于创建和启动 ESP docker 容器的 docker 命令,根据我的理解,这是启用 SSL/TLS 需要更改的内容。这也意味着与我上面描述的相比,我没有更改“后端”gRPC 服务器。不知道这是否正确。
sudo docker run \
--detach \
--name=esp \
--publish 443:443 \
--net esp_net \
--volume=/etc/nginx/ssl:/etc/nginx/ssl \
gcr.io/endpoints-release/endpoints-runtime:1 \
--service=signer.endpoints.[my-project-name].cloud.goog \
--rollout_strategy=managed \
--ssl_port=443 \
--backend=grpc://dss-signer:50051
这是用于在客户端设置安全通道的 C# 代码:
const string host = "[GCE VM IP Address]";
const int port = 443;
// Create the SSL credentials.
string caCertPem = File.ReadAllText("Certs\\ca.cer");
string clientCertPem = File.ReadAllText("Certs\\client.cer");
string clientKeyPem = File.ReadAllText("Certs\\client.key");
var keyCertificatePair = new KeyCertificatePair(clientCertPem, clientKeyPem);
var sslCredentials = new SslCredentials(caCertPem, keyCertificatePair);
// Create a client that communicates over a secure channel.
var channel = new Channel(host, port, sslCredentials);
我还尝试了上述 docker 命令的变体,例如,使用不同的 SSL 端口 (8080) 或保留 http2_port 设置。不幸的是,没有任何效果。
因此,如何设置它以使 gRPC 客户端和 GCE 托管的服务器通过 SSL/TSL 安全地通信?我是否需要以不同方式配置 C# 客户端和 Java 服务器?我需要如何配置 ESP docker 容器?
【问题讨论】:
-
作为定位问题的初始诊断步骤:端口 GCE_VM_IP_Address:443 是否可以从客户端 VM 获得?例如,客户端 VM 上的命令
telnet GCE_VM_IP_Address 443的输出是什么? -
@mebius99,虽然我没有使用 telnet 测试连接性,但我成功地运行了 OpenAPI echo-api 示例,并在同一个 GCE VM 上启用了 SSL。我用
curl对其进行了测试,使用像https://[GCE_VM_IP_Address]:443/echo?key=[API_KEY]这样的URL
标签: ssl google-cloud-platform google-compute-engine grpc grpc-java