Docker容器中IdentityServer与微服务HTTPS证书信任问题排查
问题描述
我正在开发一个使用IdentityServer向Ocelot网关及微服务传递令牌的项目,本地运行正常,但在Linux Docker容器(通过Docker Compose组合部署)中配置相同环境时,遇到自签名证书不被信任的问题。所有容器处于同一域中,目标是通过网关使用IdentityServer颁发的令牌以HTTPS方式访问微服务。
Docker Override配置文件
version: '3.4' services: abiidentity.web: environment: ASPNETCORE_ENVIRONMENT: docker ASPNETCORE_URLS: https://+;http://+ Kestrel__Certificates__Default__Password: development Kestrel__Certificates__Default__Path: "/root/.aspnet/https/aspnetapp-identity-server.pfx" ports: - "45570:443" - "45571:80" volumes: - ${APPDATA}/ASP.NET/Https:/root/.aspnet/https - type: bind source: "${APPDATA}\\ASP.NET\\Https\\aspnetapp-root-cert.cer" target: "/root/.aspnet/https/aspnetapp-root-cert.cer" depends_on: - idendity-db - abiweb.apigateway abiweb.HRM: environment: ASPNETCORE_ENVIRONMENT: docker ASPNETCORE_URLS: https://+;http://+ Kestrel__Certificates__Default__Password: development Kestrel__Certificates__Default__Path: "/root/.aspnet/https/aspnetapp-web-api.pfx" ports: - "45591:443" - "45590:80" volumes: - ${APPDATA}/ASP.NET/Https:/root/.aspnet/https - type: bind source: "${APPDATA}/ASP.NET/Https/aspnetapp-root-cert.cer" target: /root/.aspnet/https/aspnetapp-root-cert.cer abiweb.apigateway: environment: ASPNETCORE_ENVIRONMENT: docker ASPNETCORE_URLS: https://+;http://+ Kestrel__Certificates__Default__Password: development Kestrel__Certificates__Default__Path: "/root/.aspnet/https/aspnetapp-gateway.pfx" ports: - "44351:443" - "44350:80" volumes: - ${APPDATA}/ASP.NET/Https:/root/.aspnet/https - type: bind source: "${APPDATA}/ASP.NET/Https/aspnetapp-root-cert.cer" target: /root/.aspnet/https/aspnetapp-root-cert.cer
证书生成脚本
# Generate self-signed certificate to be used by IdentityServer. # When using localhost - API cannot see the IdentityServer from within the docker-compose'd network. # You have to run this script as Administrator (open Powershell by right click -> Run as Administrator). $ErrorActionPreference = "Stop" $rootCN = "IdentityServerDockerDemoRootCert" $identityServerCNs = "abiidentity.web", "localhost" $webApiCNs = "abiweb.hrm", "localhost" $gatewayCns = "abiweb.apigateway", "localhost" $alreadyExistingCertsRoot = Get-ChildItem -Path Cert:\LocalMachine\My -Recurse | Where-Object {$_.Subject -eq "CN=$rootCN"} $alreadyExistingCertsIdentityServer = Get-ChildItem -Path Cert:\LocalMachine\My -Recurse | Where-Object {$_.Subject -eq ("CN={0}" -f $identityServerCNs[0])} $alreadyExistingCertsApi = Get-ChildItem -Path Cert:\LocalMachine\My -Recurse | Where-Object {$_.Subject -eq ("CN={0}" -f $webApiCNs[0])} $alreadyExistingCertsGateway = Get-ChildItem -Path Cert:\LocalMachine\My -Recurse | Where-Object {$_.Subject -eq ("CN={0}" -f $gatewayCns[0])} if ($alreadyExistingCertsRoot.Count -eq 1) { Write-Output "Skipping creating Root CA certificate as it already exists." $testRootCA = [Microsoft.CertificateServices.Commands.Certificate] $alreadyExistingCertsRoot[0] } else { $testRootCA = New-SelfSignedCertificate -Subject $rootCN -KeyUsageProperty Sign -KeyUsage CertSign -CertStoreLocation Cert:\LocalMachine\My } if ($alreadyExistingCertsIdentityServer.Count -eq 1) { Write-Output "Skipping creating Identity Server certificate as it already exists." $identityServerCert = [Microsoft.CertificateServices.Commands.Certificate] $alreadyExistingCertsIdentityServer[0] } else { # Create a SAN cert for both identity-server and localhost. $identityServerCert = New-SelfSignedCertificate -DnsName $identityServerCNs -Signer $testRootCA -CertStoreLocation Cert:\LocalMachine\My } if ($alreadyExistingCertsApi.Count -eq 1) { Write-Output "Skipping creating API certificate as it already exists." $webApiCert = [Microsoft.CertificateServices.Commands.Certificate] $alreadyExistingCertsApi[0] } else { # Create a SAN cert for both web-api and localhost. $webApiCert = New-SelfSignedCertificate -DnsName $webApiCNs -Signer $testRootCA -CertStoreLocation Cert:\LocalMachine\My } if ($alreadyExistingCertsGateway.Count -eq 1) { Write-Output "Skipping creating API certificate as it already exists." $webApiCert = [Microsoft.CertificateServices.Commands.Certificate] $alreadyExistingCertsGateway[0] } else { # Create a SAN cert for both web-api and localhost. $webApiCert = New-SelfSignedCertificate -DnsName $gatewayCns -Signer $testRootCA -CertStoreLocation Cert:\LocalMachine\My } # Export it for docker container to pick up later. $password = ConvertTo-SecureString -String "xxx" -Force -AsPlainText $rootCertPathPfx = "C:/Users/jvdw/AppData/Roaming/ASP.NET/Https" $identityServerCertPath = "C:/Users/jvdw/AppData/Roaming/ASP.NET/Https" $webApiCertPath = "C:/Users/jvdw/AppData/Roaming/ASP.NET/Https" [System.IO.Directory]::CreateDirectory($rootCertPathPfx) | Out-Null [System.IO.Directory]::CreateDirectory($identityServerCertPath) | Out-Null [System.IO.Directory]::CreateDirectory($webApiCertPath) | Out-Null Export-PfxCertificate -Cert $testRootCA -FilePath "$rootCertPathPfx/aspnetapp-root-cert.pfx" -Password $password | Out-Null Export-PfxCertificate -Cert $identityServerCert -FilePath "$identityServerCertPath/aspnetapp-identity-server.pfx" -Password $password | Out-Null Export-PfxCertificate -Cert $webApiCert -FilePath "$webApiCertPath/aspnetapp-web-api.pfx" -Password $password | Out-Null Export-PfxCertificate -Cert $webApiCert -FilePath "$rootCertPathPfx/aspnetapp-gateway.pfx" -Password $password | Out-Null # Export .cer to be converted to .crt to be trusted within the Docker container. $rootCertPathCer = "C:/Users/jvdw/AppData/Roaming/ASP.NET/Https/aspnetapp-root-cert.cer" Export-Certificate -Cert $testRootCA -FilePath $rootCertPathCer -Type CERT | Out-Null # Trust it on your host machine. $store = New-Object System.Security.Cryptography.X509Certificates.X509Store "Root","LocalMachine" $store.Open("ReadWrite") $rootCertAlreadyTrusted = ($store.Certificates | Where-Object {$_.Subject -eq "CN=$rootCN"} | Measure-Object).Count -eq 1 if ($rootCertAlreadyTrusted -eq $false) { Write-Output "Adding the root CA certificate to the trust store." $store.Add($testRootCA) } $store.Close()
错误信息
执行wget https://abiidentity.web时返回:
root@1e3233925091:/app# wget https://abiidentity.web --2023-03-06 19:30:39-- https://abiidentity.web/ Resolving abiidentity.web (abiidentity.web)... 172.21.0.11 Connecting to abiidentity.web (abiidentity.web)|172.21.0.11|:443... connected. ERROR: The certificate of 'abiidentity.web' is not trusted. ERROR: The certificate of 'abiidentity.web' doesn't have a known issuer.
根证书验证结果
执行echo | openssl verify /usr/local/share/ca-certificates/aspnetapp-root-cert.crt显示:
echo | openssl verify /usr/local/share/ca-certificates/aspnetapp-root-cert.crt /usr/local/share/ca-certificates/aspnetapp-root-cert.crt: OK
问题排查与解决
核心问题点
- 根证书未被容器系统信任:虽然将
.cer文件挂载到了容器的/root/.aspnet/https/目录,但Linux系统默认信任证书存储在/usr/local/share/ca-certificates/,且需要将DER格式的.cer转换为PEM格式的.crt,并执行update-ca-certificates更新信任列表。 - 证书生成脚本变量错误:生成网关证书时,错误复用了
$webApiCert变量,导致网关证书与API证书重复,而非独立的、包含网关域名的证书。
修复步骤
1. 修正证书生成脚本
修改网关证书生成逻辑,使用独立变量:
# 原网关证书生成部分替换为: if ($alreadyExistingCertsGateway.Count -eq 1) { Write-Output "Skipping creating Gateway certificate as it already exists." $gatewayCert = [Microsoft.CertificateServices.Commands.Certificate] $alreadyExistingCertsGateway[0] } else { # Create a SAN cert for gateway and localhost. $gatewayCert = New-SelfSignedCertificate -DnsName $gatewayCns -Signer $testRootCA -CertStoreLocation Cert:\LocalMachine\My } # 导出网关证书部分替换为: Export-PfxCertificate -Cert $gatewayCert -FilePath "$rootCertPathPfx/aspnetapp-gateway.pfx" -Password $password | Out-Null
重新执行脚本生成正确的网关证书。
2. 配置容器信任根证书
在Docker Compose的每个服务中添加命令,完成证书格式转换和信任更新:
services: abiidentity.web: # ... 原有配置 ... command: > sh -c " openssl x509 -inform der -in /root/.aspnet/https/aspnetapp-root-cert.cer -out /usr/local/share/ca-certificates/aspnetapp-root-cert.crt && update-ca-certificates && dotnet AbiIdentity.Web.dll " abiweb.HRM: # ... 原有配置 ... command: > sh -c " openssl x509 -inform der -in /root/.aspnet/https/aspnetapp-root-cert.cer -out /usr/local/share/ca-certificates/aspnetapp-root-cert.crt && update-ca-certificates && dotnet AbiWeb.HRM.dll " abiweb.apigateway: # ... 原有配置 ... command: > sh -c " openssl x509 -inform der -in /root/.aspnet/https/aspnetapp-root-cert.cer -out /usr/local/share/ca-certificates/aspnetapp-root-cert.crt && update-ca-certificates && dotnet AbiWeb.ApiGateway.dll "
注意替换dotnet后的DLL名称为实际项目的输出文件。
3. 验证修复效果
重新启动容器后,在容器内执行wget https://abiidentity.web,此时应能正常访问,无证书信任错误。
内容的提问来源于stack exchange,提问作者Jurgen Vandw
相关产品推荐
相关产品推荐

