ASP.NET Core 2.1 application in Docker with HTTPS enabled
In one of my previous blog posts, I talked about using HSTS in ASP.Net Core application, today we look into how to deploy ASP.NET Core 2.1 application in Docker with HTTPS enabled.
If you want to use Docker for development, you will have a problem using https and that is that you must install the certificate in the image. Using a dotnet RUN dev-certs https -trust in the Dockerfile is not an option, because that command belongs to the netcore SDK and therefore is not available in an image with just the runtime (besides the small detail that, for the moment, it is not available for Linux).
To see what we need, let’s start with the typical Dockerfile of multistage generated by Visual Studio itself:
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base WORKDIR /app EXPOSE 80 EXPOSE 443 FROM microsoft/dotnet:2.1-sdk AS build WORKDIR /src COPY Webapi8/mywebapi.csproj mywebapi/ RUN dotnet restore Webapi8/mywebapi.csproj COPY . . WORKDIR /src/mywebapi RUN dotnet build mywebapi.csproj -c Release -o /app FROM build AS publish RUN dotnet publish mywebapi.csproj -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . ENTRYPOINT ["dotnet", "mywebapi.dll"]
If we start it, the first thing we will see is that it gives us an error when redirecting to https.
This error ( Failed to determine the https port for redirect ) is because the redirection middleware does not know which is the port of HTTPS, since this port is set in an environment variable called ASPNETCORE_HTTPS_PORT, therefore we must establish a value to that variable, using p. ex :
docker run -p5000:80 -p:5001:443 -e ASPNETCORE_HTTPS_PORT=5001 mywebapi
If you now browse to http://localhost:500 /api/values the browser will be redirected to https://localhost: 5001/api/values, but nobody is listening there. Kestrel has not lifted the SSL port. To raise it, we must indicate it using the ASPNETCORE_URLS environment variable:
docker run -p5000:80 -p:5001:443 -e ASPNETCORE_HTTPS_PORT=5001 -e ASPNETCORE_URLS=https://+;http://+ mywebapi
And now, when launching the image, we will receive an error that Kestrel cannot open the https port because it does not have the certificate:
crit: Microsoft.AspNetCore.Server.Kestrel Unable to start Kestrel. System.InvalidOperationException: Unable to configure HTTPS endpoint. No server certificate was specified, and the default developer certificate could not be found.
We need to share the certificate of development that we have in our machine, with the image of Docker. For this we export it to pfx, using the following command from our machine (use the route you want):
dotnet dev-certs https -v -ep d:\temp\cert-aspnetcore.pfx -p ufo
Important: You MUST use -p to set a password for the pfx file, otherwise it will not work, since the private key will not be included in the file (and Kestrel will give you an error ( System.NotSupportedException: The server mode SSL must use a certificate with the associated private key ) when accessing via https).
With that, we export the development certificate of our machine (in my case ad: \ temp \ cert-aspnetcore.pfx). The next step is to tell Kestrel that it runs in Docker where that certificate is , using the environment variables Kestrel__Certificates__Default__Path and Kestrel__Certificates__Default__Password and of course sharing the pfx file using a bind mount :
docker run -p5000:80 -p:5001:443 -e ASPNETCORE_HTTPS_PORT=5001 -e ASPNETCORE_URLS=https://+;http://+ -e Kestrel__Certificates__Default__Path=/root/.dotnet/https/cert-aspnetcore.pfx -e Kestrel__Certificates__Default__Password=ufo -v D:\temp\:/root/.dotnet/https mywebapi
With that, Kestrel looks for the certificate in the indicated directory (/root/.dotnet/https) where there is the pfx that we have in our development machine.
And if you now access http://localhost: 5000/api/values, you will be redirected to https://localhost: 5001/api/values and you should not receive any errors.
Of course, it’s much better to use a compose file before that “docker run” call with so many parameters -ey -v, but well, that’s already minor details 😉 The important thing is that you follow these:
- We generate a certificate on our machine using dotnet dev-certs.
- We export it to pfx
- We share this certificate with to Docker’s image
- We use the Kestrel configuration block: Certificates: Default to indicate where the certificate is and its password