[料理佳餚] gRPC 服務使用指定 IP 做為端點的疑難排解

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 憑證就會顯示為是安全的,客戶端除了端點的設定之外,程式碼的部分都不需要去動到。

參考資料

C# 指南 ASP.NET 教學 ASP.NET MVC 指引
Azure SQL Database 教學 SQL Server 教學 Xamarin.Forms 教學