Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I've been digging around the net and Stackoverflow and I've had some trouble solving a problem I have.

I am trying to standup my ASP.NET Core application into Docker. I have the following cert, let's call it, "FooCert.pfx". I have a copy of FooCert.pfx as a .PEM file as well (FooCert.pem). I'm trying to get my application to find the certificate at runtime. I have a docker-compose.yml file that builds and starts the container; I have some environment variables that link to where the certs are located on the Windows Host; and lastly, I have a DockerFile that wraps the behavior expected by my app.

My application throws an exception when it tries to read from the cert store on the linux container. It says that it can't find the certificate and the store is not recognized. Here are the relevant lines in my dockerfile:

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]

I understand that linux doesn't have the same cert stores as Windows, and I have accounted for that in my codebase. I've tried opening the Root and CertificateAuthority stores with CurrentUser and LocalMachine like so:

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

This throws an exception on the Linux container, but has no issue on Windows.

I've also read that it isn't a good practice to expose your certs in your container for security purposes.

TL;DR: What is the recommended practice for storing certificates in containers; and how do I properly access/find those certificates from the store on the linux container in ASP.NET Core?

like image 589
rdelgado-incinc Avatar asked Dec 06 '19 00:12

rdelgado-incinc


People also ask

Where are certificates stored in Docker container?

A custom certificate is configured by creating a directory under /etc/docker/certs.

Can ASP NET application be run in Docker container?

By default, Docker runs on port 80 with ASP.NET Core, but you can override that. In the example below, the Kestrel server that will run in the container is being configured to listen on port 5000. The other environment variable is simply specifying our environment, which is development in this case.


Video Answer


1 Answers

One solution I found was as follows:

  1. Mount the certificate directory in your 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

My 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"]

And in your 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");
                        })));
                });

Make sure that you have configured sharing the necessary permissions for sharing the directory you've chosen to grant access. If you aren't seeing the configured directory on your system, be sure to restart your computer. You can try restarting your docker instance, but restarting my computer did it for me.

Please note: you'd probably want to use Kubernetes for your solution. This is a minimal solution using docker-compose. This is merely one approach you can take.

-- Additional Edit

I learned recently that you can completely remove the .UseKestrel() section. ASP.NET will hook up the application for you automatically and you can just run the app on port :443/:80 in the container. Ideally you should decouple certificates and SSL from your container. A certificate is a "secret", i.e. it is something that requires it to be securely stored, so you can just port forward from 80 -> your desired http port and 443 -> your desired https port. The launchSettings.json (properties folder in project) is where you would setup the appropriate kestrel options, similar to how the docker-compose.yml is setup.

like image 103
cr1pto Avatar answered Oct 16 '22 19:10

cr1pto