ssl - 如何通过 SSL/TLS 与托管在 Google Compute Engine 上的 gRPC 服务器通信?

标签 ssl google-cloud-platform google-compute-engine grpc grpc-java

语境

我用 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 服务器之间建立安全通信 channel 。这是我在 中遇到问题的地方云主机场景 (但不是在自托管场景中,这很好用)。

到目前为止什么有效?

在我的本地 Windows 10 机器上运行的 gRPC 客户端与 gRPC 服务器成功通信:

  • 超过 安全 SSL/TLS channel 如果我是 自托管服务器 在我的本地 Windows 10 机器上;和
  • 超过 不安全的 channel 如果我主持 GCE 上的服务器 如上所述。

  • 我在 GCE VM 上发出了以下命令来创建 docker 容器,以便通过 成功进行客户端-服务器通信。不安全的 channel .
    # 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用于创建服务器 key 和证书的命令。对于通用名称,我使用 GCE 虚拟机的 IP 地址,如 Google Cloud Console 中所示。我已将 key 和证书复制到 /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]"
    

    这是docker用于创建和启动 ESP 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 容器?

    最佳答案

    基于 Google 小组 Google Cloud Endpoints 上的 Wayne Zhang 提供的有用提示关于为 gRPC 客户端启用 gRPC 日志记录以及与日志中报告的错误相关的更多研究,我找到了自己问题的答案。

    如何在客户端启用 gRPC 日志?

    为了在我的 Windows 10 机器上运行的客户端上启用 gRPC 日志记录,我设置了 GRPC_TRACEGRPC_VERBOSITY环境变量如下:

    set GRPC_TRACE=all
    set GRPC_VERBOSITY=DEBUG
    

    在命令提示符下运行 gRPC 客户端会产生非常详细的输出,其中包含足够的信息来发现问题。

    是什么导致了错误?

    我很惊讶在日志中发现 SSL 握手错误。这是因为按照 Google 操作指南中所述创建的服务器证书不包含 subjectAltName X.509 扩展名。 .NET gRPC 客户端(用 C# 编写)似乎期望这一点,尽管这在我的自托管方案中从来都不是问题。

    谷歌的操作指南也对此保持沉默,原因有两个。首先,可扩展服务代理 (ESP) 文档主要关注 OpenAPI 而不是 gRPC。其次,它与语言无关,不解决像这样的语言特定问题。

    我是如何解决这个问题的?

    在第一次尝试中,我创建了一个自签名的 X.509 证书,它的通用名称 (CN) 和 subjectAltName 扩展名都设置为我的 GCE VM 的 IP 地址。但是,这仍然不起作用,因为 .NET gRPC 客户端不接受自签名服务器证书(即,主题和颁发者相同的证书)。因此,我必须创建一个服务器证书请求,然后在第二步中使用我的自签名根 CA 证书和 key 创建服务器证书。

    因此,这是我创建 gRPC 服务器证书的方式:
    REM Create GCE server certificate in PEM encoding.
    REM Set ipAddress to whatever IP address the GCE VM is using.
    
    set ipAddress=[GCE VM IP address]
    set subject=/O=[MyOrganization]/OU=[MyOrganizationalUnit]/CN=%ipAddress%
    set subjectAltNameConfig=subjectAltName = IP:%ipAddress%
    echo %subjectAltNameConfig% > extfile.cfg
    
    openssl req -newkey rsa:2048 -keyout Certs\nginx.key -nodes -out Certs\Requests\nginx.csr -subj "%subject%" -addext "%subjectAltNameConfig%"
    
    REM Sign GCE server certificate.
    
    openssl x509 -req -extfile extfile.cfg -in Certs\Requests\nginx.csr -CA Certs\ca.cer -CAkey Certs\ca.key -passin pass:[password] -days 365 -set_serial 01 -out Certs\nginx.crt
    

    我的代码(Java gRPC 服务器、C# gRPC 客户端)和配置(用于 gRPC 服务器和启用 SSL 的 ESP 的 Docker 命令)中的所有其他内容都是正确的。两个文件nginx.crtnginx.key必须转移到 GCE VM 并存储在文件夹 /etc/nginx/ssl 中,它安装在 Docker 命令中。

    关于ssl - 如何通过 SSL/TLS 与托管在 Google Compute Engine 上的 gRPC 服务器通信?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60969509/

    相关文章:

    node.js - 使用 Google Cloud Platform 部署时出错

    python - OpenAI Gym - AttributeError : module 'contextlib' has no attribute 'nullcontext'

    html - 如何将谷歌云服务用于 HTML5 游戏?

    ssl - 去 ListenAndServeTLS 握手

    php - 使用 PHP 安全绑定(bind)到 Active Directory 的问题

    python-3.x - 我似乎无法让 google.cloud.texttospeech 工作

    machine-learning - 使用 'gsutil' 从 Cloud Storage 存储桶上传到 Google Colab 时出错

    git 和 "Server aborted the SSL handshake"错误

    ssl - 错误 : unable to verify the first certificate/How to trust all certificates?

    python - SSL 错误 : Bad handshake (Python requests)