Tag Archives: Container

Configuring Docker to use a Dev Cert When Calling out to the Host Machine

I’ve recently being wrestling with trying to get an ASP.Net service within a docker container to call a service running outside of that container (but on the same machine). As you’ll see as we get further into the post, this is a lot more difficult that it first appears. Let’s start with a diagram to illustrate the problem:

The diagram above illustrates, at a very basic level, what I was trying to achieve. Essentially, have the service running inside docker call to a service outside of docker. In real life, the service (2) would be remote: very likely on a different physical server, and definitely have an allocated domain address; however, for this experiment, it lives on the same physical (or virtual) machine as the docker host.

Before I continue, I must point out that the solution to this comes by way of some amazing help by Rob Richardson: he gave a talk at NDC Porto that got be about 70% of the way there, and helped me out further to actually get this working!

Referencing a Service outside of Docker from within Docker

Firstly, let’s consider a traditional docker problem: if I load Asp.Net Service (2) then I would do so in a browser referencing localhost. However, if I reference localhost from within docker, that refers to the localhost of the container, not the host machine. The way around this is with host.docker.internal: this gives you a path to the host machine of the docker container.

Certificates – The Problem

Okay, so onto the next (and main) issue: when I try to call Asp.Net Service (2) from the docker container, I get an SSL error:

The remote certificate is invalid according to the validation procedure: RemoteCertificateNameMismatch, RemoteCertificateChainErrors


The reason has to do with the way that certificates work; and, in some cases, don’t work. Firstly, if you watch the linked video, you’ll see that the dev-cert functionality in Linux has a slight flaw – in that it doesn’t do anything*. Secondly, because you’re jumping (effectively) across machines here, you can’t just issue a dev cert to each anyway, as it will be a different dev cert; and thirdly, dev-certs are issues to localhost by default: but as we saw, we’re actually trying to contact host.docker.internal.

Just to elaborate on the trust chain; let’s consider the following diagram:

In this diagram, Certificate A is based on the Root Certificate – if the machine trusts the root certificate, then it will trust Certificate A – however, the same machine will only trust Certificate B if it is explicitly told to do so.

This means that the dev cert for the container will not be trusted on the host, and vice-versa – as neither have any trust chain and relationship – this is the problem, but it’s also the solution.

Okay, so that’s the why – onto the how…


Let’s start with introducing mkcert – this is an incredibly useful tool that hugely simplifies the whole process; it can be installed via chocolatey:

choco install mkcert

If you don’t want to use Chocolatey, then the repo is here.

Essentially, what this allows us to do is to create a trusted root certificate, from which, we can base our other certificates. So, once this is installed, we can create a new trusted root certificate like this:

mkcert -install

This installs our trusted root certificate; which we can see here:

This will also generate the following files (on Windows, these will be in %localappdata%\mkcert):


These are the root certificates, so the next thing we need is a certificate that covers the specific domain. You can do that by simply calling mkcert with the appropriate domain(s):

mkcert localhost host.docker.internal

This creates a valid cert for both localhost and host.docker.internal:


You may wish to rename these to be something slightly more descriptive, but for the purpose of this post, this is sufficient.


Almost there now – we have our certificates, but we need to copy them to the correct location. Because we’ve run mkcert -install the root certificate is already on the local machine; however, we now need that on the docker client (Asp.Net Service (1) from the diagram above). Firstly, let’s download a mkcert.exe from here for the relevant version of Linux that you’re running.

Let’s copy both the rootCA.pem and rootCA-key.pem into our Asp.Net Service (1) project and then change the dockerfile:

. . .
FROM base AS final
COPY mkcert /usr/local/bin
COPY rootCA*.pem /root/.local/share/mkcert/
RUN chmod +x /usr/local/bin/mkcert \
  && mkcert -install \
  && rm -rf /usr/local/bin/mkcert 
COPY --from=publish /app/publish .
. . .

A few things to mention here:

1. The rest of this file is from the standard Ast.Net docker file. See this post for possible modifications to that file.
2. Each time you execute a RUN command docker makes a temporary image, hence why combining three lines (on line 7) with the && makes sense.
3. When you run the mkcert -install it will pick up the root certificate that you copy into the /root/.local/share/mkcert.
4. Make sure that these lines apply to the runtime version of the image, and not the SDK version (there’s absolutely no point in adding a certificate to the SDK version).
5. The last line (rm -rf /usr/local/bin/mkcert) just cleans up the mkcert files.

The Service

The final part is to copy the generated certificates (localhost.pem and localhost-key.pem) over to the service application (Asp.Net Service (2)). Finally, in the appsettings.json, we need to tell Kestrel to use that key:

  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
  "AllowedHosts": "*",
  "Kestrel": {
    "Certificates": {
      "Default": {
        "Path": "localhost-host.pem",
        "KeyPath": "localhost-host-key.pem"

That’s it! If you open up the Asp.Net Service (2), you can check the certificate, and see that it’s based on the mkcert root:

References and Acknowledgements

As I said at the start, this video and Rob himself helped me a lot with this – so thanks to him!

It’s also worth mentioning that without mkcert this process would be considerably more difficult!


* actually, that’s not strictly true – Rob points out in his video the nuance here; but the takeaway is that it’s unlikely to be helpful

CosmosDb – Errors on Inserting New Data

It’s been a while since I looked at CosmosDB (there is a chapter in my book – to the right of your screen – that covers some details of using CosmosDD, but using the MongoDB Api. To get going with Cosmos, you obviously need to create yourself a Cosmos resource in the Portal (or somehow). Once that’s done, you can interact with it using the package:


You need to create a client, database, and container:

var cosmosClient = new CosmosClient(endpointUri, primaryKey, options);

var database = await cosmosClient.CreateDatabaseIfNotExistsAsync(databaseId);            
var container = await database.CreateContainerIfNotExistsAsync(containerId, $"/{partition}");

You can then add to the container like this:

var response = await container.CreateItemAsync<ItemType>(newItem, new PartitionKey(partitionKey));

In this post, I wanted to cover a couple of errors that I got when trying to use it, and some possible solutions. For this post, I was using the Core (SQL) API.

You Need an ID

The first error was:

Microsoft.Azure.Cosmos.CosmosException: ‘Response status code does not indicate success: BadRequest (400); Substatus: 0; ActivityId: cdf12360b-0cbc-4575-9a1c-966f26c2351a; Reason: (Message: {“Errors”:[“The input content is invalid because the required properties – ‘id; ‘ – are missing”]}

The reason here is that, when you’re writing to the DB, you need an “ID” field; or, more specifically, an “id” field. This means that either your model should have a field names “id”, or you should reference that name in a Json Property, or you can call it “Id”, and use something like this:

var options = new CosmosClientOptions()
    SerializerOptions = new CosmosSerializationOptions()
        PropertyNamingPolicy = CosmosPropertyNamingPolicy.CamelCase
var cosmosClient = new CosmosClient(endpointUri, primaryKey, options);

Partition Key

The next error relates to the partition key. You typically create your container like this:

var container = await database.CreateContainerIfNotExistsAsync(containerId, $"/{partitionkey}");

And then add data to it like this:

var response = await container.CreateItemAsync<ItemType>(newItem, new PartitionKey(partitionkey));

Essentially, the rule here is this:

Whatever you specify as the partition key must exist in the payload with the exact same name.

Microsoft.Azure.Cosmos.CosmosException: ‘Response status code does not indicate success: BadRequest (400); Substatus: 1001; ActivityId: 88c1895e-a23c-5812-c205-d4503555d2cd; Reason: (Message: {“Errors”:[“PartitionKey extracted from document doesn’t match the one specified in the header”]}

You can find out what it thinks you’ve set the partition key to by using the following code:

var properties = await container.ReadContainerAsync();

When I say exactly, I mean it has to be the same case in the payload; so if you’re key is Item, and you supply the field item, you’ll get this error. Such a mismatch can be caused by the camel casing that we mentioned earlier!