Skip to content

End-To-End TLS Setup With a .NET Web Application on an AKS Cluster

Technology

May 25, 2022 - 4 minute read

V01 2423 TLS In NET Blog 416X300
Krzysztof Kaczorowski DevOps engineer

A DevOps Engineer with system administration background. Passionate about serverless, public cloud and automation in Microsoft stack. After work you can see him hiking in the mountains or traversing bike trails.

See all Krzysztof's posts

2988 HC Digital Transformation 476X381

Share

Securing network traffic continues to become more and more important over the years. Increasingly frequent security breaches with progressively sophisticated attack vectors make all IT engineers look towards effective ways of protecting their systems. One of the options is traffic encryption using the Transport Layer Security (TLS), which enables communication with network services over a secure channel.

TLS (formerly known as SSL) helps to protect against numerous types of cyberattacks such as Man-in-the-Middle, domain theft, domain hijack, and more. This protocol is also utilised during the authentication process.

Whatever modern technologies you select for your project, some familiar patterns will remain relevant. Systems that utilise serverless, Kubernetes or Platform-as-a-Service solutions still face many of the same challenges that threaten the legacy monolithic infrastructures hosted on-premises.

Environment

The most common scenario of traffic encryption implementation with an application gateway is the TLS termination. During the client-server communication, the encrypted traffic is being established only between the client and application gateway. Unencrypted backend communication, although it has its upsides — most notably performance — also raises a security concern. There’s a risk of an attacker already being present in the infrastructure, hoping to exploit the unencrypted communication from within. That’s why end-to-end TLS is so important in highly secured environments.

Infrastructure

In this example, I’m going to examine the following infrastructure:

As you can see, an AKS cluster is set up behind an Azure Application Gateway. In order to demonstrate the end-to-end TLS communication, we’re going to need a web application, ingress controller and a certificate to encrypt the traffic. Once the Application Gateway Ingress Controller (AGIC) is successfully installed on the cluster, it will handle the configuration of the Azure resource. To do that, it will take care of the Gateway’s configuration synchronisation by reading annotations from ingress definition and applying them to the cloud resource.
In this scenario, the backend communication protocol should be set to ‘https’ and in the case the client sends a request via http, it will be redirected to https. These are the annotations that can make that happen:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: aspnetapp
  annotations:
    3ubernetes.io/ingress.class: azure/application-gateway
    appgw.ingress.kubernetes.io/ssl-redirect: “true”
    appgw.ingress.kubernetes.io/backend-protocol: https

The certificate in question is a full-chain certificate with a private key. It should be uploaded to Azure Key Vault, and then fetched by identity as a Kubernetes secret. The certificate file will be mounted to a pod using the secret provider class.

Integrating Azure resources and an AKS cluster is possible by mapping the managed identity resource in the Azure subscription and AzureIdentity custom resource definition (CRD) in Kubernetes. This mechanism allows the cluster to authenticate against Azure API and use the API it to perform the required actions.

Web Application

To make things as simple as possible, the web app used in this example is just the WebApp template in the sixth version of .NET from Visual Studio. The only amendments are the added Docker support and https protocol.

Official Microsoft documentation suggests that in order to enforce traffic encryption, an instruction needs to be added to the program.cs file.

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
.
.
.
app.Run();

Also, pointing to the certificate file is necessary. You can do it via application settings or environment variables:

"Certificates": {

      "Default": {

         "Path": "<path to .pem/.crt file>",

          "KeyPath": "<path to .key file>"     

}

    }

The Limitations of .NET

At this point, it would seem that we’re all set. Infrastructure, application, certificates are configured by the book. It means everything should work, right? Sadly, that’s not the case and the application will crash.

The reason is that .NET can’t support full chain certificates. It will take the certificate file from the volume mount and use only the last certificate in the chain (leaf). This only becomes a problem, because the app is located behind the Azure Application Gateway. For the application to work, both the Application Gateway and the backend server need to exchange at least the intermediate and leaf part of the certificate chain during the TLS handshake.

The Solution

The solution is to use the extensions published on GitHub. They can enable loading TLS certificates for .NET 6.0 Kestrel web applications, allowing for refreshing of certificates as well as ensuring compatibility with HTTP/3, so you can implement TLS in your project.

using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Https;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;

namespace tlsE2eAgic
{
 public static class TlsListenOptionsExtensions
  {
   public static ListenOptions UseHttpsWithFullChain(this ListenOptions listenOptions, string certPath, string keyPath, string? password = null)
{
    ArgumentNullException.ThrowIfNull(certPath);
    ArgumentNullException.ThrowIfNull(keyPath);

    var env = listenOptions.ApplicationServices.GetRequiredService<IWebHostEnvironment>();

    var fullCertPath = Path.Combine(env.ContentRootPath, certPath);
    var fullkeyPath = Path.Combine(env.ContentRootPath, keyPath);

    var leafCertWithKey = password switch
    {
     null => X509Certificate2.CreateFromPemFile(fullCertPath, fullkeyPath),
     _ => X509Certificate2.CreateFromEncryptedPemFile(fullCertPath, password.AsSpan(), fullkeyPath)
    };

    // Import the full cert chain
    var fullChain = new X509Certificate2Collection();
    fullChain.ImportFromPemFile(certPath);

    var options = new SslServerAuthenticationOptions
    {
     // Don't go online to retrieve cert chain
     ServerCertificateContext = SslStreamCertificateContext.Create(leafCertWithKey, fullChain, offline: true)
    };

    return listenOptions.UseHttps(new TlsHandshakeCallbackOptions
    {
     OnConnection = context => new(options)
    });
   }
  }
 }

With the code above, all you need to do is supply the environment variables certPath and keyPath to the application. They can both have the same value as the application handles certificates and key contents in the same file. The encrypted communication will then be established on the line User -> Application Gateway -> Web Application.

Summary

The solution described above is essentially a workaround, but it fulfils its purpose by securing internal traffic of the backend infrastructure. Even though end-to-end TLS is not a new concept in the IT world, it’s still lacking a native solution for .NET. The upcoming .NET 7 may finally introduce the long awaited functionality of supporting the full chain certificate in Kestrel middleware. According to Microsoft, the issue addressing full chain certificate support has been moved to dotnet-7.0-preview4 and it might be released soon.
For the time being, we encourage you to use the described solution, as it’s the best way to implement TLS in a .NET web application.

2988 HC Digital Transformation 476X381
Krzysztof Kaczorowski DevOps engineer

A DevOps Engineer with system administration background. Passionate about serverless, public cloud and automation in Microsoft stack. After work you can see him hiking in the mountains or traversing bike trails.

See all Krzysztof's posts

Related posts

You might be also interested in

Contact

Start your project with Objectivity

CTA Pattern - Contact - Middle

We use necessary cookies for the functionality of our website, as well as optional cookies for analytic, performance and/or marketing purposes. Collecting and reporting information via optional cookies helps us improve our website and reach out to you with information regarding our organisaton or offer. To read more or decline the use of some cookies please see our Cookie Settings.