这是this question的跟进。
我已使用以下脚本生成并信任自签名证书:
#create a SAN cert for both host.docker.internal and localhost
#$cert = New-SelfSignedCertificate -DnsName "host.docker.internal", "localhost" -CertStoreLocation "cert:\LocalMachine\Root"
# does not work: New-SelfSignedCertificate : A new certificate can only be installed into MY store.
$cert = New-SelfSignedCertificate -DnsName "host.docker.internal", "localhost" -CertStoreLocation cert:\localmachine\my
#export it for docker container to pick up later
$password = ConvertTo-SecureString -String "password_here" -Force -AsPlainText
Export-PfxCertificate -Cert $cert -FilePath "$env:USERPROFILE\.aspnet\https\aspnetapp.pfx" -Password $password
# trust it on your host machine
$store = New-Object System.Security.Cryptography.X509Certificates.X509Store [System.Security.Cryptography.X509Certificates.StoreName]::Root,"LocalMachine"
$store.Open("ReadWrite")
$store.Add($cert)
$store.Close()
当访问主机上的https://host.docker.internal:5500/.well-known/openid-configuration
和https://localhost:5500/.well-known/openid-configuration
时,它按预期方式工作(证书正常)。>>
但是,在容器中运行的Web API应用程序对此不满意:
web_api | System.InvalidOperationException: IDX20803: Unable to obtain configuration from: 'https://host.docker.internal:5500/.well-known/openid-configuration'. web_api | ---> System.IO.IOException: IDX20804: Unable to retrieve document from: 'https://host.docker.internal:5500/.well-known/openid-configuration'. web_api | ---> System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception. web_api | ---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure. web_api | at System.Net.Security.SslStream.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, ExceptionDispatchInfo exception)
API的docker-compose文件如下(仅相关部分):
web.api: image: web_api_image build: context: . dockerfile: ProjectApi/Dockerfile environment: - ASPNETCORE_ENVIRONMENT=ContainerDev container_name: web_api ports: - "5600:80" networks: - backend - data_layer depends_on: - identity.server - mssqlserver - web.cache identity.server: image: identity_server_image build: context: . dockerfile: MyProject.IdentityServer/Dockerfile environment: - ASPNETCORE_ENVIRONMENT=ContainerDev - ASPNETCORE_URLS=https://+:443;http://+:80 - ASPNETCORE_Kestrel__Certificates__Default__Password=password_here - ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx volumes: - ~/.aspnet/https:/https:ro container_name: identity_server ports: - "5500:443" - "5501:80" networks: - backend - data_layer depends_on: - mssqlserver
我该如何进行这项工作?
对身份服务器的调用是通过在API客户端中设置安全性来使用它来完成的(无显式HTTPS调用):
/// <summary> /// configures authentication and authorization /// </summary> /// <param name="services"></param> /// <param name="configuration"></param> public static void ConfigureSecurity(this IServiceCollection services, IConfiguration configuration) { string baseUrl = configuration.GetSection("Idam").GetValue<string>("BaseUrl"); Console.WriteLine($"Authentication server base URL = {baseUrl}"); services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }).AddJwtBearer(o => { o.MetadataAddress = $"{baseUrl}/.well-known/openid-configuration"; o.Authority = "dev_identity_server"; o.Audience = configuration.GetSection("Idam").GetValue<string>("Audience"); o.RequireHttpsMetadata = false; }); services.AddAuthorization(); }
身份服务器配置
public void ConfigureServices(IServiceCollection services)
{
string connectionStr = Configuration.GetConnectionString("Default");
Console.WriteLine($"[Identity server] Connection string = {connectionStr}");
services.AddDbContext<AppIdentityDbContext>(options => options.UseSqlServer(connectionStr));
services.AddTransient<AppIdentityDbContextSeedData>();
services.AddIdentity<AppUser, IdentityRole>()
.AddEntityFrameworkStores<AppIdentityDbContext>()
.AddDefaultTokenProviders();
services.AddIdentityServer(act =>
{
act.IssuerUri = "dev_identity_server";
})
.AddDeveloperSigningCredential()
// this adds the operational data from DB (codes, tokens, consents)
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder => builder.UseSqlServer(Configuration.GetConnectionString("Default"));
// this enables automatic token cleanup. this is optional.
options.EnableTokenCleanup = true;
options.TokenCleanupInterval = 30; // interval in seconds
})
//.AddInMemoryPersistedGrants()
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients(Configuration))
.AddAspNetIdentity<AppUser>();
services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(@"\\UNC-PATH"));
services.AddTransient<IProfileService, IdentityClaimsProfileService>();
services.AddCors(options => options.AddPolicy("AllowAll", p => p.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()));
services.AddMvc(options =>
{
options.EnableEndpointRouting = false;
}).SetCompatibilityVersion(CompatibilityVersion.Latest);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public static void Configure(IApplicationBuilder app, IWebHostEnvironment env,
ILoggerFactory loggerFactory, AppIdentityDbContextSeedData seeder)
{
seeder.SeedTestUsers();
IdentityModelEventSource.ShowPII = true;
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseExceptionHandler(builder =>
{
builder.Run(async context =>
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
var error = context.Features.Get<IExceptionHandlerFeature>();
if (error != null)
{
context.Response.AddApplicationError(error.Error.Message);
await context.Response.WriteAsync(error.Error.Message).ConfigureAwait(false);
}
});
});
// app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCors("AllowAll");
app.UseIdentityServer();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
这是该问题的后续内容。我已经使用以下脚本生成并信任了自签名证书:#为host.docker.internal和localhost创建一个SAN证书#$ cert = New -...
[经过几次尝试,我放弃了让Docker容器信任由New-SelfSignedCertificate
生成的证书的尝试(您可以尝试使其生效-概念完全相同,只是证书有所不同)。但是,我确实通过OpenSSL获得了成功: