How to force CloudFlare WAF: mTLS
We'll see how it's possible to do mutual TLS (mTLS) with Nginx and force your users to pass through CloudFlare WAF and reject all direct connections.more
What's mTLS?
mTLS or mutual TLS is a double check that web server and browser do at the same time.
Classic way
Your browser checks the server certificate when accessing your web server. It's normal way for you.
mTLS way
Your browser or service server (like API) sends a client certificate to your web server in the same that the web server sends his certificate.
The server verifies and if it's ok, no problem otherwise it rejects with an HTTP 496 SSL Certificate Required (expansion of the 400 Bad Request response code, used when a client certificate is required but not provided).
Why do you need to use it with CloudFlare?
If a person finds the IP address of your server, it's possible to bypass CloudFlare WAF so we need to harden.
mTLS vs IP filtering
You have 2 ways to restrict your web server from having exclusive traffic through CloudFlare.
For a dedicated web server, you can use IP filtering with nftables/iptables but in addition, you need to keep your configuration up to date with CloudFlare ranges (IPv4/IPv6).
When you're mutualizing your web server like having some virtual hosts with CloudFlare WAF and others without CloudFlare WAF, your only way is mTLS, and in addition, it's more easy and secure because forged packets with spoofed IP origin still fail due to mTLS.
Enabling mTLS
Webserver
You need to do very little configuration:
- download CloudFlare's CA certificate used for mTLS
cd /etc/nginx
wget -N -O cloudflare-mtls.pem https://support.cloudflare.com/hc/en-us/article_attachments/360044928032/origin-pull-ca.pem
- add 2 lines in your virtual host
ssl_client_certificate /etc/nginx/cloudflare-mtls.pem;
ssl_verify_client on;
Never change ssl_verify_client to another possibility otherwise you'll accept a connection without certificate and/or with an invalid certificate.
- restart nginx
systemctl restart nginx
On CloudFlare
On your domain dashboard, in SSL/TLS, you need to activate Authenticated Origin Pulls
Conclusion and bonus
All done, you're using mTLS for your virtual host with strict mode so any connection without a valid certificate is rejected.
Bonus: change return code when no client certificate
In your virtual host config, inside your location, you can add:
if ($ssl_client_verify != SUCCESS) { return 403; }
This replaces the return code of 496 to 403 or if you want to be quicker, you can prefer 444 Connection Closed Without Response.
444 is a non-standard status code used to instruct Nginx to close the connection without sending a response to the client, most commonly used to deny malicious or malformed requests.
This status code is not seen by the client, it only appears in Nginx log files.
Subscribe to my newsletter
Read articles from sycured directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
sycured
sycured
Designing, building, and running secure infrastructure on public, hybrid, and private clouds.