為什麼我需要證書才能作為客戶端建立安全的 gRPC 連接?
當在普通 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 是簽署 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