文章目錄
讓 container 中的 ASP.NET Core 也有憑證
經 2021/03/04 測試,本文內容失效,請參考 ASP.NET Core gRPC 使用自發憑證
之前筆記 使用 ASP.NET Core middleware 進行 gRPC healthy check、使用 ASP.NET Core BackgroundService 進行 gRPC healthy check 以及 ASP.NET Core gRPC 無法在 macOS 上啟動?! 都有提到過 gRPC 的原生限制:採用 HTTP2 協定並預設使用 SSL,雖然可以使用 clear text 只是如此一來不僅失去安全性在設定上還是比較繁瑣些
與其老是想著要怎麼設定跟調整來避開 gRPC 的 HTTP2 SSL 問題,突然想轉個念:乾脆給個憑證 說不定還簡單些,今天就紀錄一下做法供比較參考囉
基本環境說明
- macOS Catalina 10.15.6
- docker desktop community 2.3.0.5(48029)
- .NET Core SDK 3.1.301
docker images
yowko/healthcheck:nocert
專案程式碼如下
Program.cs
using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; namespace HealthCheck_POC { public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); } }
Startup.cs
using System; using System.Net.Http; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; namespace HealthCheck_POC { public class Startup { private IHostApplicationLifetime _hostApplicationLifetime; public void ConfigureServices(IServiceCollection services) { services.AddGrpc(); services.AddHealthChecks().AddCheck<GreeterHealthChecker>("example_health_check"); //指定 endpoint 為 localhost,並忽略憑證有效性 services.AddGrpcClient<Greeter.GreeterClient>(o => { o.Address = new Uri("https://localhost:5001"); }) .ConfigurePrimaryHttpMessageHandler(() => { return new HttpClientHandler { ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true }; }); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime hostApplicationLifetime) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapHealthChecks("/health"); endpoints.MapGrpcService<GreeterService>(); endpoints.MapGet("/", async context => { await context.Response.WriteAsync( "Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909"); }); }); } } }
appsettings.json
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*", "Kestrel": { "EndpointDefaults": { "Protocols": "Http2" } } }
GreeterHealthChecker.cs
using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Diagnostics.HealthChecks; namespace HealthCheck_POC { public class GreeterHealthChecker : IHealthCheck { private readonly Greeter.GreeterClient _client; public GreeterHealthChecker(Greeter.GreeterClient client) { _client = client; } public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default(CancellationToken)) { var reply = await _client.SayHelloAsync( new HelloRequest { Name = "GreeterClient" }); if (reply.Message == "Hello GreeterClient") { return HealthCheckResult.Healthy("A healthy result."); } return HealthCheckResult.Unhealthy("An unhealthy result."); } } }
Protos/greet.proto
syntax = "proto3"; option csharp_namespace = "HealthCheck_POC"; package greet; // The greeting service definition. service Greeter { // Sends a greeting rpc SayHello (HelloRequest) returns (HelloReply); } // The request message containing the user's name. message HelloRequest { string name = 1; } // The response message containing the greetings. message HelloReply { string message = 1; }
Services/GreeterService.cs
using System.Threading.Tasks; using Grpc.Core; using Microsoft.Extensions.Logging; namespace HealthCheck_POC { public class GreeterService : Greeter.GreeterBase { private readonly ILogger<GreeterService> _logger; public GreeterService(ILogger<GreeterService> logger) { _logger = logger; } public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context) { return Task.FromResult(new HelloReply { Message = "Hello " + request.Name }); } } }
DOCKERFILE
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build WORKDIR /app # 複製 sln csproj and restore nuget COPY *.sln . # 目標路徑資料夾要與來源路徑資料夾名稱相同(因 sln 已儲存專案路徑) COPY HealthCheck_POC/*.csproj ./HealthCheck_POC/ #RUN dotnet dev-certs https --trust # nuget restore RUN dotnet restore # 複製其他檔案 COPY HealthCheck_POC/. ./HealthCheck_POC/ WORKDIR /app/HealthCheck_POC # 使用 Release 建置專案並輸出至 out RUN dotnet publish -c Release -o out FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS runtime WORKDIR /app # 將產出物複製至 app 下 COPY --from=build /app/HealthCheck_POC/out ./ # 啟動 TestDokcer ENTRYPOINT ["dotnet", "HealthCheck_POC.dll"]
專案調整與設定
執行指令
docker run --name poc -p 5001:5001 -e ASPNETCORE_URLS=https://+:5001 yowko/healthcheck:nocert
錯誤訊息
crit: Microsoft.AspNetCore.Server.Kestrel[0] Unable to start Kestrel. System.InvalidOperationException: Unable to configure HTTPS endpoint. No server certificate was specified, and the default developer certificate could not be found or is out of date. To generate a developer certificate run 'dotnet dev-certs https'. To trust the certificate (Windows and macOS only) run 'dotnet dev-certs https --trust'. For more information on configuring HTTPS see https://go.microsoft.com/fwlink/?linkid=848054. at Microsoft.AspNetCore.Hosting.ListenOptionsHttpsExtensions.UseHttps(ListenOptions listenOptions, Action`1 configureOptions) at Microsoft.AspNetCore.Hosting.ListenOptionsHttpsExtensions.UseHttps(ListenOptions listenOptions) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.AddressesStrategy.BindAsync(AddressBindContext context) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.BindAsync(IServerAddressesFeature addresses, KestrelServerOptions serverOptions, ILogger logger, Func`2 createBinding) at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServer.StartAsync[TContext](IHttpApplication`1 application, CancellationToken cancellationToken) Unhandled exception. System.InvalidOperationException: Unable to configure HTTPS endpoint. No server certificate was specified, and the default developer certificate could not be found or is out of date. To generate a developer certificate run 'dotnet dev-certs https'. To trust the certificate (Windows and macOS only) run 'dotnet dev-certs https --trust'. For more information on configuring HTTPS see https://go.microsoft.com/fwlink/?linkid=848054. at Microsoft.AspNetCore.Hosting.ListenOptionsHttpsExtensions.UseHttps(ListenOptions listenOptions, Action`1 configureOptions) at Microsoft.AspNetCore.Hosting.ListenOptionsHttpsExtensions.UseHttps(ListenOptions listenOptions) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.AddressesStrategy.BindAsync(AddressBindContext context) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.BindAsync(IServerAddressesFeature addresses, KestrelServerOptions serverOptions, ILogger logger, Func`2 createBinding) at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServer.StartAsync[TContext](IHttpApplication`1 application, CancellationToken cancellationToken) at Microsoft.AspNetCore.Hosting.GenericWebHostService.StartAsync(CancellationToken cancellationToken) at Microsoft.Extensions.Hosting.Internal.Host.StartAsync(CancellationToken cancellationToken) at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token) at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token) at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.Run(IHost host) at HealthCheck_POC.Program.Main(String[] args) in /app/HealthCheck_POC/Program.cs:line 18
錯誤截圖
設定步驟
建立開發用憑證
語法
dotnet dev-certs https -ep ${HOME}/.aspnet/https/aspnetapp.pfx -p {password} dotnet dev-certs https --trust
範例
dotnet dev-certs https -ep ${HOME}/.aspnet/https/aspnetapp.pfx -p pass.123 dotnet dev-certs https --trust
將憑證 mount 給 container 並設定 Kestrel 監聽 port
docker run --name poc -p 5001:5001 -e ASPNETCORE_URLS=https://+:5001 -e ASPNETCORE_Kestrel__Certificates__Default__Password="pass.123" -e ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx -v ${HOME}/.aspnet/https:/https/ yowko/healthcheck:nocert
實際使用
http request 成功觸發 grpc call 並正確取得 grpc response
心得
- appsettings.json 中的
Kestrel
ptotocol 不一定需要指定為http2
使用預設的http1andhttp2
亦可,重點是 certificate AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
無效,需使用HttpClientHandler
設定
反覆測試了幾次,大致上歸納出目前的設定方式供參考
經 2021/03/04 測試,本文內容失效,請參考 ASP.NET Core gRPC 使用自發憑證
參考資訊
文章作者 Yowko Tsai
上次更新 2021-03-04
授權合約
本部落格 (Yowko's Notes) 所有的文章內容(包含圖片),任何轉載行為,必須通知並獲本部落格作者 (Yowko Tsai) 的同意始得轉載,且轉載皆須註明出處與作者。
Yowko's Notes 由 Yowko Tsai 製作,以創用CC 姓名標示-非商業性-相同方式分享 3.0 台灣 授權條款 釋出。