gRPC 服務預設會使用 HTTPS,不過這個只有針對 localhost 這個網域名稱,這天我想要將自己的區域網路 IP 指定為 gRPC 服務的端點,好做一些驗證跟測試,於是我就動手修改了 Kestrel 監聽的 IP,使用的是 HTTP 協定,然後就在客戶端收到了這個錯誤訊息。
IOException: The response ended prematurely.
Http2UnencryptedSupport
這個錯誤的原因是客戶端發送 Request 給 HTTP/2 的端點時,預設必須要加密,所以 HTTP 的服務端點就打不通了,官方的解法是客戶端在建立 Channel 之前加入下面這一行程式碼:
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
// 建立連接到 gRPC 服務的通道
var channel = GrpcChannel.ForAddress("http://abc.xxx.yyy.zzz:5001");
不過如果想針對 HTTPS 的情境進行一些測試的話,就得為我的區域網路 IP 建立 SSL 憑證,提供給 gRPC 服務來使用。
mkcert
mkcert 是國外一個在加密領域的神人所做的一個小工具,它可以讓我們簡單一行指令就簽發出任何網域名稱的 SSL 憑證,並且以我們的本機做為 CA,來讓瀏覽器認定我們做出來的 SSL 憑證是安全的。
先到這裡下載執行檔,以我的區域網路 IP 192.168.0.188
為例,底下即是產生 SSL 憑證的指令:
mkcert -pkcs12 -p12-file kestrel.pfx 192.168.0.188
產生好的 SSL 憑證預設會放在與 mkcert 執行檔同目錄下,我們把它複製到 gRPC 伺服器端使用,憑證密碼預設是 changeit
,接著在伺服端多加兩行程式碼。
不過我們這樣做,客戶端還是會收到與 SSL 相關的錯誤訊息。
AuthenticationException: The remote certificate is invalid according to the validation procedure.
DangerousAcceptAnyServerCertificateValidator
這個錯誤的原因是因為 SSL 憑證被認定為是不受信任的,官方也有個解法,在客戶端建立 Channel 的時候,另外產生 HttpClient
給 Channel 使用。
var httpClient = new HttpClient(
new HttpClientHandler
{
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
});
// 建立連接到 gRPC 服務的通道
var channel = GrpcChannel.ForAddress("https://abc.xxx.yyy.zzz:5001", new GrpcChannelOptions { HttpClient = httpClient });
為了讓指定的 IP 可以成為服務端點,我們可以看到客戶端需要配合跟著修改一些的程式碼,其實我們只要讓 SSL 憑證成為是受信任的,客戶端的程式就幾乎不用動,我們執行下面指令,利用 mkcert 把本機做為 CA 來讓 SSL 憑證變成是受信任的。
mkcert -install
這裡會跳出一個警告,按「是(Y)」就好了。
弄好之後,我的 SSL 憑證就會顯示為是安全的,客戶端除了端點的設定之外,程式碼的部分都不需要去動到。