J King
J King

Reputation: 4434

Unable to configure HTTPS endpoint for .net Core Kestral Server in Linux Container on Azure

I have a .net core 3 app running locally/development just fine, both on its own and when run in a linux container. I then take the app and have it built into a docker image inside azure pipelines. The image is loaded to azure container registry.

Finally, I have an Azure Web APP for Containers (Linux) that uses the image to run.

Locally I have the docker-compose file set up like this:

    environment:
      - "ASPNETCORE_ENVIRONMENT=Development"
      ...
      - "ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx"
      - "ASPNETCORE_Kestrel__Certificates__Default__Password=Your_password123"
    volumes:
      - ~/.aspnet/https:/https:ro

For production I have the following:

    environment:
      - UseInMemoryDatabase=false
      - ASPNETCORE_ENVIRONMENT=Production
      - ASPNETCORE_Kestrel__Certificates__Default__Path=/security/mycert.pfx
      - "ASPNETCORE_Kestrel__Certificates__Default__Password=Your_password123"
    ports:
      - "5000:5000"
      - "5001:5001"
    volumes:
       - fsmount001: /security:/security
       - /var/ssl/private:/https

I loaded "mycert" into the azure portal and added its thumbprint to the App's configuration settings under WEBSITE_LOAD_CERTIFICATES

I used Open SSL to create the mycert file and I can use it locally and kestral will use it, but with a warning.

THE PROBLEM

When I run the app with this image I get the following error in the docker logs:

System.InvalidOperationException: Unable to configure HTTPS endpoint. No server certificate was specified, and the default developer certificate could not be found or is out of date ... at Microsoft.AspNetCore.Hosting.ListenOptionsHttpsExtensions.UseHttps(ListenOptions listenOptions, Action`1 configureOptions)

I have tried a lot of variations of loading certs and can not get any of them to work. This is an issue that only happens in production.

I also have tried:

  1. Purchased an Azure App Certificate and used the thumbprint.p12 file like:
      - ASPNETCORE_Kestrel__Certificates__Default__Path=/var/ssl/private/<thumbprint>.p12
      - ASPNETCORE_Kestrel__Certificates__Default__Password=""

I used no password because when you buy a cert there is no password set

  1. Downloaded the purchased App Cert and used open ssl to create a password linked .pfk file and uploaded that as another private key

  2. Use azure file mount and upload my dev cert files and reference them from the file mount like:

      - ASPNETCORE_Kestrel__Certificates__Default__Path=/security/mycert.com.pfx
      - ASPNETCORE_Kestrel__Certificates__Default__Password="Your_password123"
    volumes:
       - fsmount001: /security:/security

EDIT 1: Full docker-compose and azure file setup

  1. Here is how I have defined my file share: enter image description here

There is a security folder with the mycert.pfx file inside it

  1. Here is how I set up the file mount in my azure app service configuration:

enter image description here

I set the mount path to be the security folder in my file share

  1. Here is the full docker compose file:
services:
  webui:
    image: ${DOCKER_REGISTRY-}webui
    build:
      context: .
      dockerfile: src/WebUI/Dockerfile
    environment:
      - UseInMemoryDatabase=false
      - ASPNETCORE_ENVIRONMENT=Production
      - ASPNETCORE_URLS=https://+:443;http://+:80
      - "ConnectionStrings__DefaultConnection=****"
      - ASPNETCORE_Kestrel__Certificates__Default__Path=/secure/mycert.pfx
      - ASPNETCORE_Kestrel__Certificates__Default__Password="Your_password123"
    ports:
      - "5000:5000"
      - "5001:5001"
    volumes:
       - fsmount001: /secure
       - ~/var/ssl/private:/https
    restart: always

volumes:
  fsmount001:
    driver: azure_file
    driver_opts:
      share_name: st-*****tus
      storage_account_name: st********001

EDIT 2: DOCKERFILE

for further context you can find my dockerfile below

please note that I am using the open source application template/framework cleanarchiecture. You can see that I am trying to use the docker pull request for the repo as the base code. My goal is to "dockerize" this base framework in azure ci/cd pipeline and deploy it to azure web app for containers (linux)


FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
ENV ASPNETCORE_URLS=https://+:5001;http://+:5000

WORKDIR /app
EXPOSE 5000 5001 2222

FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
RUN curl -sL https://deb.nodesource.com/setup_12.x | bash -
RUN apt install -y nodejs
WORKDIR /src
COPY ["src/WebUI/WebUI.csproj", "src/WebUI/"]
COPY ["src/Application/Application.csproj", "src/Application/"]
COPY ["src/Domain/Domain.csproj", "src/Domain/"]
COPY ["src/Infrastructure/Infrastructure.csproj", "src/Infrastructure/"]
RUN dotnet restore "src/WebUI/WebUI.csproj"
COPY . .
WORKDIR "/src/src/WebUI"
RUN dotnet build "WebUI.csproj" -c Release -o /app/build

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

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .

ENTRYPOINT ["dotnet", "CleanArchitecture.WebUI.dll"]

Can someone help me figure out how to set a certificate for kestral inside a Linux container?

thanks in advance

Upvotes: 3

Views: 7889

Answers (1)

jccampanero
jccampanero

Reputation: 53411

Is it possible that your docker-compose file has an error in the definition of the service volumes?

You have the following docker-compose fragment for your service:

    environment:
      - UseInMemoryDatabase=false
      - ASPNETCORE_ENVIRONMENT=Production
      - ASPNETCORE_Kestrel__Certificates__Default__Path=/security/mycert.pfx
      - "ASPNETCORE_Kestrel__Certificates__Default__Password=Your_password123"
    ports:
      - "5000:5000"
      - "5001:5001"
    volumes:
       - fsmount001: /security:/security
       - /var/ssl/private:/https

With this setup you are trying to create two volumes.

On one hand, you are mapping the /var/ssl/private path in the host system to the /https container destination. It should work fine.

But, on the other hand, I think you are mixing syntax for named volumes and volumes based on path mapping.

For your update you are trying to use an Azure File storage mount. Then you need to modify your service volumes definition as follows:

    environment:
      ...
    ports:
      ...
    volumes:
       - fsmount001:/security
       - /var/ssl/private:/https

As indicated in the documentation, it is important to understand that the mount path corresponds to the folder inside the container that you want to mount to Azure Storage:

The mount path setting corresponds to the folder inside the container that you want to mount to Azure Storage. Setting it to '/' mounts the entire container to Azure Storage.

Please, also note that the path provided for fsmount001 in your docker-compose file is the same as the mount path indicated when you created the mount, /security in this case.

With this setup, you need to configure the certificate location like this:

- ASPNETCORE_Kestrel__Certificates__Default__Path=/security/security/mycert.pfx

The first /security for the path in the container, and the second for the directory in which your pfx is included in the file share.

UPDATE

After reviewing your Dockerfile and docker-compose files together, I think your problem could be motivated not for the actual file share, but because the environment variables required to setup your HTTPS are not visible inside the docker container because they are used only in its build phase. Please, see this I think related stack overflow question.

You need to provide this environment information directly in your Dockerfile or indirectly using ARGs in your docker-compose file.

For example, modify your docker-compose file as follows - basically, change your environmententry for args:

services:
  webui:
    image: ${DOCKER_REGISTRY-}webui
    build:
      context: .
      dockerfile: src/WebUI/Dockerfile
    args:
      - UseInMemoryDatabase=false
      - ASPNETCORE_ENVIRONMENT=Production
      - ASPNETCORE_URLS=https://+:443;http://+:80
      - ConnectionStrings__DefaultConnection=****
      - ASPNETCORE_Kestrel__Certificates__Default__Path=/secure/mycert.pfx
      - ASPNETCORE_Kestrel__Certificates__Default__Password="Your_password123"
    ports:
      - "5000:5000"
      - "5001:5001"
    volumes:
       - fsmount001: /secure
       - ~/var/ssl/private:/https
    restart: always

volumes:
  fsmount001:
    driver: azure_file
    driver_opts:
      share_name: st-*****tus
      storage_account_name: st********001

And them modify your Dockerfile to read the provided build arguments:

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base

ARG UseInMemoryDatabase
ENV UseInMemoryDatabase=$UseInMemoryDatabase

ARG ASPNETCORE_ENVIRONMENT
ENV ASPNETCORE_ENVIRONMENT=$ASPNETCORE_ENVIRONMENT
      
ARG ASPNETCORE_URLS=https://+:5001;http://+:5000
ENV ASPNETCORE_URLS=$ASPNETCORE_URLS

ARG ConnectionStrings__DefaultConnection
ENV ConnectionStrings__DefaultConnection=$ConnectionStrings__DefaultConnection
      
ARG ASPNETCORE_Kestrel__Certificates__Default__Path
ENV ASPNETCORE_Kestrel__Certificates__Default__Path=$ASPNETCORE_Kestrel__Certificates__Default__Path

ARG ASPNETCORE_Kestrel__Certificates__Default__Password
ENV ASPNETCORE_Kestrel__Certificates__Default__Password=$ASPNETCORE_Kestrel__Certificates__Default__Password

WORKDIR /app
EXPOSE 5000 5001 2222

FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
RUN curl -sL https://deb.nodesource.com/setup_12.x | bash -
RUN apt install -y nodejs
WORKDIR /src
COPY ["src/WebUI/WebUI.csproj", "src/WebUI/"]
COPY ["src/Application/Application.csproj", "src/Application/"]
COPY ["src/Domain/Domain.csproj", "src/Domain/"]
COPY ["src/Infrastructure/Infrastructure.csproj", "src/Infrastructure/"]
RUN dotnet restore "src/WebUI/WebUI.csproj"
COPY . .
WORKDIR "/src/src/WebUI"
RUN dotnet build "WebUI.csproj" -c Release -o /app/build

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

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .

ENTRYPOINT ["dotnet", "CleanArchitecture.WebUI.dll"]

Please, modify it as you consider appropriate. Pay attention especially at the moment in which the different ARGs and ENVs should be defined because they are scoped per build-stage, and will not be preserved in the next stage. You can try as is, or you can define the ARGs in base and the ENVs in final, as one extend from the other the ARGs variables should be visible in both.

Upvotes: 3

Related Questions