Setting up mTLS and Kestrel
Pretty sure everyone at this point knows what TLS is, but what about mTLS? How is it different from TLS, what’s it used for?
What is TLS? TLS, or Transport Layer Security, is the successor to SSL; both of which are means of secure communication. There have been several versions of TLS, each subsequent version being more secure, easier to use, or a combination of the two. We’re up to TLS v1.3.
You can read a lot more about TLS here.
The basic idea of TLS is to secure communications between multiple parties, you’re probably very used to “seeing” it when you visit websites like this one.
There’s a lot of “magic” going on when connecting to a website, in the form of back and forth between client and server. The link I posted about goes into greater detail regarding this, but we can also pretty easily see some of it using a cURL command.
We’re going to use a testing web api project for this post, I’ll start it with:
Now run the project with
dotnet run, and submit a cURL command to the default WeatherForecast controller:
You’ll notice in the above that we’re using the
--insecure flag in our cURL command as we’re using a “development” certificate through the web api to establish secure connections.
So now that we’ve established a very high level of what TLS is and what it looks like, what is mTLS?
Mutual authentication or two-way authentication refers to two parties authenticating each other at the same time, being a default mode of authentication in some protocols (IKE, SSH) and optional in others (TLS).
By default the TLS protocol only proves the identity of the server to the client using X.509 certificate and the authentication of the client to the server is left to the application layer. TLS also offers client-to-server authentication using client-side X.509 authentication. As it requires provisioning of the certificates to the clients and involves less user-friendly experience, it’s rarely used in end-user applications.
Mutual TLS authentication (mTLS) is much more widespread in business-to-business (B2B) applications, where a limited number of programmatic and homogeneous clients are connecting to specific web services, the operational burden is limited, and security requirements are usually much higher as compared to consumer environments.
There’s a fair amount of information in the above, but the tdlr in my opinion is:
- both parties provide their identity by some means
- often used in business to business applications
What this means is that application access can be controlled to our system through our system generating “passwords” for our users to use, in the form of certificates signed by our CA, that we provide back to them.
Note (I’m going to make it several times throughout the post) that the code is not set up in a way to verify that the client provided cert was signed by our CA, just that it is signed. This is not desired behavior, but I will try to handle the additional auth in another post. Additionally, you will often want to set up another layer of security than just the cert, dual auth of some sort provided by a one time password or something similar. This will help protect your system in an instance where a client’s cert/private key has made it out into the wild; without that “second factor” users won’t be able to get in (also not covered in this post).
mTLS, at least in the way we’re going to set it up in this post, has a few steps, many of which are outside the bounds of “coding”. A high level list of steps includes:
- Create a local CA
- Import the CA as a trusted root CA for our “server” (our local machine in this case)
- Create a certificate for use by the “client” which is signed by the CA
- Enable/enforce client certificates in our .net core application
- run a cURL command against our code again, without providing a cert, see our request is denied
- run a cURL command against our code, this time providing our client cert, see our request gets through
I followed this tutorial: https://deliciousbrains.com/ssl-certificate-authority-for-local-https-development/
Now install the crt as a trusted root authority by double clicking it and “install cert”:
create a file client.ext with the following information:
Now generate the client key/cert:
You should now have a client.crt available, and when viewing, you should be able to see the “full certificate chain” in that the certificate was signed by the myCa (kritnerCa in my case):
It’s pretty straight forward getting mTLS working with Kestrel, a bit more involved with IIS (which I may cover in another post…?)
Add to the project file a NuGet package that allows for client certificate authentication:
We’ll be adding “Require Client Certificate” to our application bootstrapping in the
Then in the
Startup.cs, we’ll need to update
Configure to set up the authentication and register the authentication middleware.
Please be aware of the comments in the above code block. If you do not implement your own validation to go on top of the normal cert validation, then any valid certificate passed in from the client will be allowed, regardless of whether or not it was signed by the CA we created earlier in the post. I’m not going to cover writing such a validator in this post, but I’ll try to remember to do so in another; this post is taking me more time than I had intended already!
Note the above
app.UseAuthentication should be done after
app.UseRouting(); and before
app.UseAuthorization();. The whole
Configure method now looks like this:
Now we have mTLS set up in regards to our system, and our code. Let’s give it a run!
First, start the web application.
Next, let’s try our same curl command we used in the beginning of the post:
which looks like:
The above makes sense, we haven’t provided a certificate to the web application, so we are being rejected.
Now let’s make sure we can actually get in with our signed cert, using the following command:
which looks like:
- Cover setting up mTLS on IIS - there are registry settings that need to be updated in some cases (yuck!)
Setting up a custom certificate validator, right now we’re just letting in any cert that is not self signed, rather than checking that the signed cert was signed by our CA.
- Multi factor auth
Setting up mTLS and Kestrel