如何在 ASP.NET 核心应用程序的 Docker 上正确安装证书?

How to properly install certificates on Docker in ASP.NET Core application?

我一直在网上和 Whosebug 上搜索,但在解决我遇到的问题时遇到了一些麻烦。

我正在尝试将我的 ASP.NET 核心应用程序安装到 Docker 中。我有以下证书,我们称它为 "FooCert.pfx"。我也有一份 FooCert.pfx 作为 .PEM 文件的副本 (FooCert.pem)。我试图让我的应用程序在运行时找到证书。我有一个构建和启动容器的 docker-compose.yml 文件;我有一些环境变量 link 到证书在 Windows 主机上的位置;最后,我有一个 Docker 文件,它包装了我的应用程序预期的行为。

我的应用程序在尝试从 linux 容器上的证书存储中读取时抛出异常。它说它找不到证书并且无法识别商店。以下是我的 docker 文件中的相关行:

FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS base
WORKDIR /app
EXPOSE 80

FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build
WORKDIR /src
...

COPY ./FooCert.pem /etc/ssl/certs/FooCert.pem
COPY ./FooCert.pem /usr/local/share/ca-certificates/FooCert.pem
COPY ./FooCert.pfx /usr/local/share/ca-certificates/FooCert.pfx

RUN openssl pkcs12 -in /usr/local/share/ca-certificates/FooCert.pfx -nocerts -nodes | sed -ne '/-BEGIN PRIVATE KEY-/,/-END PRIVATE KEY-/p' > FooCert.key
RUN openssl pkcs12 -in /usr/local/share/ca-certificates/FooCert.pfx -clcerts -nokeys | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > FooCertClientcert.cer
RUN openssl pkcs12 -in /usr/local/share/ca-certificates/FooCert.pfx -cacerts -nokeys -chain | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > FooCertcacerts.cer

COPY ./FooCert.pem /etc/ssl/certs/FooCert.pem
COPY ./FooCert.pem /usr/local/share/ca-certificates/FooCert.pem
RUN ls /usr/local/share/ca-certificates
RUN ls /etc/ssl/certs
RUN update-ca-certificates
...
[code to expose ports, define entrypoint, etc here]

我知道 linux 没有与 Windows 相同的证书存储,我已经在我的代码库中说明了这一点。我试过像这样使用 CurrentUser 和 LocalMachine 打开 Root 和 CertificateAuthority 商店:

var certStore = new X509Store(StoreName.CertificateAuthority, StoreLocation.LocalMachine);

这会在 Linux 容器上引发异常,但在 Windows 上没有问题。

我还了解到,为了安全起见,在容器中公开证书并不是一个好习惯。

TL;DR:在容器中存储证书的推荐做法是什么;以及如何正确 access/find ASP.NET Core 中 linux 容器中存储的那些证书?

我找到的一个解决方案如下:

  1. 在你的 docker-compose.yml 中挂载证书目录:
version: '3.6'

services:
  dockertemplate:
    image: ${DOCKER_REGISTRY-}dockertemplate
    build:
        context: .
        dockerfile: DockerTemplate/Dockerfile
    ports:
    #bind [host port]:[container port]
        - 8000:80
        - 5051:443
        - 5050:5050
    environment:
        - "ASPNETCORE_URLS=https://+;http://+"
        - Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx
        - Kestrel__Certificates__Default__Password=changeit
    volumes:
      - ~/.aspnet/https:/https:ro #this is very important

我的 Dockerfile:

#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
WORKDIR /src
COPY ["DockerTemplate/DockerTemplate.csproj", "DockerTemplate/"]
RUN dotnet restore "DockerTemplate/DockerTemplate.csproj"
COPY . .
WORKDIR "/src/DockerTemplate"
RUN dotnet build "DockerTemplate.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "DockerTemplate.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "DockerTemplate.dll"]

在你的Program.cs中:

            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();

                    webBuilder.UseKestrel(options => options.ListenAnyIP(5050, listenOptions => listenOptions.UseHttps(
                        adapterOptions =>
                        {
                            adapterOptions.ServerCertificate = new X509Certificate2("/https/aspnetapp.pfx", "changeit");
                        })));
                });

确保您已配置共享必要的权限以共享您选择授予访问权限的目录。如果您没有在系统上看到配置的目录,请务必重新启动计算机。您可以尝试重新启动您的 docker 实例,但重新启动我的计算机对我来说已经成功了。

请注意:您可能希望使用 Kubernetes 作为您的解决方案。这是使用 docker-compose 的最小解决方案。这只是您可以采用的一种方法。

-- 附加编辑

我最近了解到您可以完全删除 .UseKestrel() 部分。 ASP.NET 将自动为您连接应用程序,您只需 运行 容器中端口 :443/:80 上的应用程序即可。理想情况下,您应该将证书和 SSL 从您的容器中分离出来。证书是一个“秘密”,即它需要安全存储,因此您可以从 80 -> 所需的 http 端口和 443 -> 所需的 https 端口转发。 launchSettings.json(项目中的属性文件夹)是您设置适当的 kestrel 选项的地方,类似于 docker-compose.yml 的设置方式。