当通过纯 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:错误:1000007d:SSL 例程:OPENSSL_internal:CERTIFICATE_VERIFY_FAILED。
答案1
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
必须为每个客户端传递。
File.read
对于所有 *_pem 变量,通过相应的 PEM 文件加载内容,由您的信任中心提供或可能是之前自行生成的:
- server_ca_pem 是签署 server_cert_pem 的证书颁发机构
- server_cert_pem 是服务器证书链,由客户端通过 server_ca_pem 证明
- server_key_pem 是服务器的私钥
- client_ca_pem 是已签署 client_cert_pem 的证书颁发机构
- client_cert_pem 是客户端证书链,由服务器通过 client_ca_pem 证明
- client_key_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