当在普通 TCP 上使用 gRPC 时,客户端会像这样(在 ruby 中)与服务器建立一个通道:
stub = Helloworld::Greeter::Stub.new(service_url, :this_channel_is_insecure)
但是,当我在服务器上实现 TLS 并在服务器上放入我的 LetsEncrypt 证书时,客户端必须建立一个像这样的安全连接(在 ruby 中):
creds = GRPC::Core::Credentials.new(load_certs) # load_certs typically loads a CA roots file
stub = Helloworld::Greeter::Stub.new(service_url, creds)
带有注释的代码取自官方 gRPC 文档。
我的问题是,为什么客户在这里需要证书?我认为是服务器需要证书,客户端确保它是有效的。当我的浏览器连接到安全网站时,它是否会将自己的证书带到桌面上?如果没有,为什么 gRPC 客户端需要一个?
从我的阅读中,客户端知道一些受信任的机构,服务器的证书链链接到其中之一。上面代码中的注释引用了一个“CA 根文件”,它可能是链顶部的权威证书。所以也许 gRPC 客户端会将服务器链根的证书与它自己的证书进行比较——但如果是这样的话,如果它得到错误的 CA 会发生什么?
所有解释如何从 gRPC 客户端建立安全连接的文档和帖子都说要从本地文件中读取证书,但没有一个说明该证书是什么,或者它来自哪里。
我错过了什么?
编辑:
我在我的计算机上发现了一个目录,其中包含一堆似乎是 CA 根证书的目录。/etc/ssl/certs/
其中之一似乎是验证 LetsEncrypt 的授权,我在服务器上使用它,所以我尝试在客户端读取该证书,如下所示:
GRPC::Core::ChannelCredentials.new(File.read('/etc/ssl/certs/ISRG_Root_X1.pem'))
但它只导致了这个错误
握手失败,出现致命错误 SSL_ERROR_SSL: error:1000007d:SSL routines:OPENSSL_internal:CERTIFICATE_VERIFY_FAILED。
gRPC 主要用于通过调用远程过程来连接服务,例如微服务。与 Web 服务器和多个浏览器客户端之间的单方信任关系相比,所涉及的双方必须明确地相互信任以避免中间人攻击。gRPC 通过设计为 TLS 安全连接强制执行此操作。
在不安全(无 TLS 安全)连接(仅用于测试目的)的情况下,对于 gRPC 服务器,参数
:this_port_is_insecure
将传递给(GRPC::RpcServer.new).add_http2_port
方法,对于每个涉及的 gRPC 客户端,参数channel_args: :this_channel_is_insecure
将传递给Core::Stub.new
方法。相反,在通过 TLS 进行安全连接的情况下,
GRPC::Core::ServerCredentials.new client_ca_pem, [{private_key: server_key_pem, cert_chain: server_cert_pem}], true
必须为 gRPC 服务器传递,并且GRPC::Core::ChannelCredentials.new server_ca_pem, client_key_pem, client_cert_pem
必须为每个客户端传递。对于所有 *_pem 变量,通过
File.read
从您的信任中心提供的相应 PEM 文件加载内容,或者可能之前自行生成:server_ca_pem
并且client_ca_pem
可能相同也可能不同。GRPC::Core::CallCredentials
如果您需要在呼叫级别保护服务-客户关系,请使用附加。gRPC 认证指南:
https://grpc.io/docs/guides/auth/
Ruby 代码示例:
https://github.com/grpc/grpc/blob/master/src/ruby/spec/channel_credentials_spec.rb