Windows主机开发证书在Windows Docker容器.NET Core应用中使用失败排查
这是一个运行在Windows容器中的.NET Core应用,已暴露80和443(HTTPS)端口,使用Visual Studio生成的默认Dockerfile:
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base WORKDIR /app EXPOSE 80 EXPOSE 443 FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build WORKDIR /src COPY ["IdentityServer/IdentityServer.csproj", "IdentityServer/"] RUN dotnet restore "IdentityServer/IdentityServer.csproj" COPY . . WORKDIR "/src/IdentityServer" RUN dotnet build "IdentityServer.csproj" -c Development -o /app/build FROM build AS publish RUN dotnet publish "IdentityServer.csproj" -c Development -o /app/publish /p:UseAppHost=false FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "IdentityServer.dll","--environment=Development"]
我遵循微软官方文档尝试使用Windows主机的开发证书构建Docker镜像,已执行以下命令(确保证书名称IdentityServer.pfx与项目程序集名称匹配):
dotnet dev-certs https -ep %USERPROFILE%\.aspnet\https\IdentityServer.pfx -p crypticpassword dotnet dev-certs https --trust
随后执行镜像构建命令:
docker build -t identity -f IdentityServer/Dockerfile .
通过docker images ls确认镜像构建成功后,执行容器启动命令:
docker run --rm -it -p 8080:80 -p 8081:443 -e ASPNETCORE_URLS="https://+;http://+" -e ASPNETCORE_HTTPS_PORT=8081 -e ASPNETCORE_Kestrel__Certificates__Default__Password="crypticpassword" -e ASPNETCORE_Kestrel__Certificates__Default__Path=$env:USERPROFILE\.aspnet\https\IdentityServer.pfx -v $env:USERPROFILE\.aspnet\https:C:\https\ --user ContainerAdministrator identity
但启动失败,报错:
Unhandled exception. System.IO.DirectoryNotFoundException: Could not find a part of the path 'C:\Users\Administrator.aspnet\https\IdentityServer.pfx'.
完整错误堆栈信息如下:
info: Duende.IdentityServer.Startup[0]
Using the default authentication scheme Identity.Application for IdentityServer Unhandled exception.
System.IO.DirectoryNotFoundException: Could not find a part of the
path 'C:\Users\Administrator.aspnet\https\IdentityServer.pfx'. at
Microsoft.Win32.SafeHandles.SafeFileHandle.CreateFile(String fullPath,
FileMode mode, FileAccess access, FileShare share, FileOptions
options) at Microsoft.Win32.SafeHandles.SafeFileHandle.Open(String
fullPath, FileMode mode, FileAccess access, FileShare share,
FileOptions options, Int64 preallocationSize, Nullable1 unixCreateMode) at System.IO.Strategies.OSFileStreamStrategy..ctor(String path, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable1 unixCreateMode) at
System.IO.Strategies.FileStreamHelpers.ChooseStrategyCore(String path,
FileMode mode, FileAccess access, FileShare share, FileOptions
options, Int64 preallocationSize, Nullable1 unixCreateMode) at System.IO.StreamReader.ValidateArgsAndOpenPath(String path, Encoding encoding, Int32 bufferSize) at System.IO.File.ReadAllText(String path, Encoding encoding) at System.Security.Cryptography.X509Certificates.X509Certificate2Collection.ImportFromPemFile(String certPemFilePath) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Certificates.CertificateConfigLoader.LoadCertificate(CertificateConfig certInfo, String endpointName) at Microsoft.AspNetCore.Server.Kestrel.KestrelConfigurationLoader.LoadDefaultCert() at Microsoft.AspNetCore.Server.Kestrel.KestrelConfigurationLoader.Reload() at Microsoft.AspNetCore.Server.Kestrel.KestrelConfigurationLoader.Load() at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerImpl.BindAsync(CancellationToken cancellationToken) at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerImpl.StartAsync[TContext](IHttpApplication1
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 Program.$(String[] args) in
C:\src\IdentityServer\Program.cs:line 14
我未使用用户密钥存储密码(与文档不同),参考相关问题未解决。
更新:
已添加用户密钥配置:
{ "Kestrel":{ "Certificates":{ "Default":{ "Path": "/root/.aspnet/https/<AppName>>.pfx", "Password": "<<Your-Password>>" } } } }
请问我哪里操作有误?
解决方案
你当前的问题主要出在路径配置不匹配和混淆了Windows容器与Linux容器的路径规则,具体错误点及修复步骤如下:
启动命令中证书路径错误
你在docker run命令中设置的ASPNETCORE_Kestrel__Certificates__Default__Path使用了主机的路径$env:USERPROFILE\.aspnet\https\IdentityServer.pfx,但容器内部无法直接访问主机的环境变量路径。你已经将主机的%USERPROFILE%\.aspnet\https挂载到了容器的C:\https\,所以容器内部的证书路径应该是C:\https\IdentityServer.pfx,而不是主机的路径。修正后的启动命令中该环境变量应改为:
-e ASPNETCORE_Kestrel__Certificates__Default__Path=C:\https\IdentityServer.pfx用户密钥配置中的路径错误
你更新的用户密钥配置里用了Linux容器的路径/root/.aspnet/https/...,但你使用的是Windows容器,路径应该用Windows格式。同时注意配置里的<AppName>>多了一个右尖括号,需要修正为实际的程序集名称IdentityServer。修正后的用户密钥配置应为:
{ "Kestrel":{ "Certificates":{ "Default":{ "Path": "C:\\https\\IdentityServer.pfx", "Password": "crypticpassword" } } } }注意:JSON中的反斜杠需要转义,所以用
\\。验证挂载的路径正确性
可以先启动容器进入交互式模式,检查挂载的C:\https\目录下是否存在证书文件:docker run --rm -it -v $env:USERPROFILE\.aspnet\https:C:\https\ identity cmd在容器内执行
dir C:\https\确认证书文件存在。简化环境变量配置
如果你已经在用户密钥中配置了证书信息,可以去掉启动命令中对应的ASPNETCORE_Kestrel__Certificates__Default__Path和ASPNETCORE_Kestrel__Certificates__Default__Password环境变量,避免冲突。
修正后的完整启动命令示例:
docker run --rm -it -p 8080:80 -p 8081:443 -e ASPNETCORE_URLS="https://+;http://+" -e ASPNETCORE_HTTPS_PORT=8081 -v $env:USERPROFILE\.aspnet\https:C:\https\ --user ContainerAdministrator identity
内容的提问来源于stack exchange,提问作者Nouman Bhatti

