3

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

J King
  • 4,108
  • 10
  • 53
  • 103

1 Answers1

2

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.

jccampanero
  • 50,989
  • 3
  • 20
  • 49
  • thanks for the help, I still have not quite got it working. I updated my question to include more details. Can you confirm based on my settings that I have it right? The file mount is set to the mount path of security, which is a folder in my file share. As a result I assume the 'root' of the mounted file to be the security folder. – J King Nov 02 '20 at 22:32
  • You are welcome @JKing. Sorry to hear that it is still not working. I updated my answer with further information: I am sorry, I previously I followed the steps for an ACI setup, just as indicated in the docker docs, but not for the web app. Please, can you try and see if it works? One thing you can also try is to connect to the container using any of the mechanisms provided in the ```Development Tools``` section of your App Service, mainly SSH or Kudu, an see if the /secure path exists and your certificate is where it is supposed to be. – jccampanero Nov 03 '20 at 11:05
  • something crazy is going on here. I have made the changes you suggested and easily a dozen other minor variations of file names and paths. Same error persists. I can't ssh into my container because its a custom container and I can't get ssh working on it. The kudu bash terminal is throwing an error for some reason I cant resolve even though it was working a couple days ago. the 10 minute round trip to build in azure ci/cd and check logs makes this very tedious. haha, we love our jobs right? – J King Nov 03 '20 at 17:50
  • I see @JKing. Have you got the ability to test in a fresh App Service environment? Did you follow all the [steps](https://learn.microsoft.com/en-us/azure/app-service/tutorial-multi-container-app) to correctly configure the multi-container app? Please, can you verify if the [WEBSITES_ENABLE_APP_SERVICE_STORAGE](https://learn.microsoft.com/en-us/azure/app-service/tutorial-multi-container-app#configure-environment-variables) is enabled? I assume that yes because you already configured it but please, can you verify it in your app service configuration? – jccampanero Nov 03 '20 at 18:09
  • I will review those docks and try in a new app service. However, this is a single container/service app, the docker compose file only declares one service. WEBSITES_ENABLE_APP_SERVICE_STORAGE was not enabled – J King Nov 03 '20 at 19:11
  • I understand that @JKing, but you are using a docker-compose file so I think it could be helpful. Do not worry about the setting then, if you found the certs and the folder in your container it is probably not relevant: I will investigate it further. I think the best option is try a fresh environment: maybe something is wrong or cached, and it could help to resolve your problem. – jccampanero Nov 03 '20 at 19:29
  • Despite the problems in the development tools, were you able to check if the mounted directory and certificate were where they should be? – jccampanero Nov 03 '20 at 19:36
  • @JKing Can you please update your answer with some snippet of your Dockerfile? Are you actually passing all the required environment to the container? Please, see this stack overflow question and answer: https://stackoverflow.com/questions/52429984/docker-compose-build-environment-variable . I think it could be also related with your problem. Perhaps the environment used in your build is not visible to your container – jccampanero Nov 03 '20 at 20:43
  • thanks for sticking with this. I updated (Edit 2) my question to include more details and I am currently deploying a new "fresh" app service to test a clean environment – J King Nov 03 '20 at 21:04
  • 1
    Nice @JKing. Please, update your Dockerfile, I think you need to define ARGS for every environment variable used in the build, and expose it in your Dockerfile as ENV in order yo make it visible to your app and the .net framework. Do you get the idea? – jccampanero Nov 03 '20 at 21:20
  • 1
    @JKing I updated the answer with the changes I proposed you to do. I hope it helps. – jccampanero Nov 03 '20 at 22:11
  • so manually setting the aspnet_idenity key path and password worked. The error I initially reported is now gone and the docker logs are error free, but the website does not work. I get a failed request with no error details. On to the next one haha. thanks for your help! – J King Nov 04 '20 at 04:10
  • So the problem really was that the environment variables were not visible in the container, right? You're welcome @JKing. I'm glad at least the app was able to start. I hope the new problem will be solved very soon. If I can be of help with something, do not hesitate to tell me. – jccampanero Nov 04 '20 at 09:06
  • one last unrelated comment, should the azure web app expose the same port as the container? should i add the WEBSITES_PORT=5000,5001 to the configuration settings of the app? When I ping my service it shows 443 open but 5000 and 5001 closed. thanks again – J King Nov 05 '20 at 14:27
  • Sorry for the late reply. Do not worry @JKing, as I told you, if you think that I can be of any help, please tell me. I think the problem could be related with an incompatible setting of the ```ASPNETCORE_URLS``` property and the exposed ports in your ```docker-compose``` file: on one hand your are exposing your services on ports ```80``` and ```443```, but on the other you are exposing the ports ```5000``` and ```5001```. One thing you can try is to modify this configuration so that both settings match, for instance, by defining port mappings ```80:80``` and ```443:443```. See next comment – jccampanero Nov 05 '20 at 22:17
  • Here a question comes to my mind, and I am sorry because I think it should have been the first answer that I should have given you: the setup is great for a containerized application on their own, but in the case of app service, AFAIK, with the aforementioned setup it will give you only the ability to expose one port, ```80``` by default, or the value indicated in the ```WEBSITES_PORT``` setting if indicated. Maybe you can set this setting to ```443``` but you need to test if it works properly. Again, AFAIK, in order to support load balancing, service scaling, etcetera, see next comment – jccampanero Nov 05 '20 at 22:30
  • App service will sit a kind of load balancer in front of your application. Among the above mentioned things, this load balancer will also perform HTTPS (SSL/TLS) termination. This means that your application will never receive HTTPS traffic: all the communication between the load balancer and your service will be unencrypted. You can customize this SSL/TLS settings, the HTTPS facade exposed for your service, through Azure Portal (I do not know, although I think so, also with the CLI and PS), typically by registering a custom domain and a certificate emitted by a recognized CA, see next comment – jccampanero Nov 05 '20 at 22:42
  • say DigiCert, Let's Encrypt, or, I assume, an Azure certificate like the one you purchased. Does this make sense to you? Please, in any case, test your configuration. If you need further help, please, tell me, I will be glad to help you if I can. – jccampanero Nov 05 '20 at 22:49
  • @JKing I just saw your new question. Please, have you got a moment for chat? – jccampanero Nov 09 '20 at 22:28
  • Have you the ability to do that? Do you know how to do that here? I mean init a new chat – jccampanero Nov 09 '20 at 22:35
  • i have not done it in a while, ill start a room and let you know – J King Nov 09 '20 at 22:39
  • try this link https://chat.stackoverflow.com/rooms/224339/azure-app-service-and-docker – J King Nov 09 '20 at 22:39
  • @JKing Are you there? – jccampanero Nov 10 '20 at 21:54