Implementing WAF and mutual TLS on Kubernetes (AKS) with Nginx ModSecurity

Hey there,

In a recent project that included deploying microservices into AKS, our client had a number of specific requirements:

1. Use Azure Kubernetes Service (AKS) as the platform for the application microservices.
2. Integrate mTLS capabilities to authenticate clients approaching our client’s APIs.
3. Use the client certificate to validate the client’s origin (in our case, a hospital).
4. Protect the public-facing microservices with a web application firewall (WAF).


Azure does provide WAF services, like Application Gateway and Front Door, but neither of them has mTLS capabilities.




To meet our client’s requirements, we had to search for a third-party solution that provides it all. We came up with the idea of using the tried-and-tested Kubernetes Ingress-NGINX Controller for the following reasons: as well as being an Ingress controller with all the advantages of the NGINX engine, it also supports mTLS (client requirement 3); with its opensource WAF ModSecurity add-on (client requirement 2), it provides OWASP CRS support; and we can add more rules if we want to.

For the validation requirement, we configured the NGINX to transfer the client certificate fingerprint to the backend app.


Solution Demo:



If you want to follow this demo, you will need the following:

1. Azure account (
2. Your own domain.
3. DNS to manage your DNS records (I manage mine with Azure’s DNS service).


Let’s start by installing AKS (client requirement 1) with Pulumi, a rising star in the IaC world which uses familiar language code, like Python, Typescript, GO, and more.
• Install Pulumi (in my case MacOS)

• Install Python 3.6 or above and verify PIP is installed
• Next, we will create a new Pulumi project

  • The command will launch a short configuration section for a new Pulumi project.

• Now we have three main files
1. Pulumi.yaml defines the project
2. contains configuration values for the stack we initialized.
3. is the Pulumi program that defines our stack resources




  • Next, we will deploy AKS

  • The next step is to connect to our AKS

  • Now, we create two namespaces – one for the application, and one for the Ingress-NGINX

  • Next, we have to create self-signed certificates for the client verification -mTLS (client requirement 3). (See this blog for more information about how to do this)

  • For the server authentication, I created a Let’s Encrypt certificate with Certbot. (See this blog for more information about how to do this)



The result of this command is two pem files privkey.pem and fullchain.pem.



  • Now, after completing all the certificate creation steps, we can deploy secrets into the AKS app namespace, for the Ingress deployment.

  • Now for the Ingress-NGINX deployment. I use Helm 3 to deploy Ingress-NGINX into the cluster, with the following value file:

(See here for an explanation of the different ModSecurity configuration options)

  • Now that we have the Ingress-NGINX Controller up and running, let’s deploy our app into AKS. The app will just echo back to us with HTTP request properties. We also deploy the service and Ingress in the same yaml stack. Each of the ModSecurity and configuration annotation snippets will demonstrate one of the capabilities that our use case needs.






Now we can check if our Ingress configuration answers the client’s requirements.

Client requirement 1: We are using AKS as our application platform.
Client requirements 2 and 3: Enforcing mTLS and forwarding the client fingerprint to the client microservice for another validation phase.
  • Let’s try to curl the website without client certificate and key certificate – we get an error message: “400 Bad Request”.




And if we try with client and key certificate we get “200”.




We can also see that we have forwarded the client certificate fingerprint.



Now, let’s turn to client requirement 4: ModSecurity as WAF.



We have created two rules and SecRuleEngine is ‘On’. This means that ModSecurity will be in prevention mode.

  • Let’s check the IP rule :




I get “ 403 Forbidden” curling from IP “”

  • Let’s check the logs to see the error:

  • Now, let’s check the request header rule: as user: admin I get blocked; changing to user: user, I pass.


Conclusion :


Using ModSecurity and Ingress-NGINX, we have managed to resolve all our client’s demands that couldn’t be met by the Azure service alone. Ingress-NGINX and ModSecurity are two powerful tools that give us the agility and freedom to control Ingress traffic and protect our environment, and we have the extra benefit of minimizing our vendor-lock.