Traefik proxy with Web Application Firewall (WAF)
Traefik proxy is a versatile and very lightweight cloud-native application gateway / load balancer that integrates really well with Docker and Kubernetes, for example.
While Traefik is great and brilliant, it lacks Web Application Firewall features and integrations. Traditionally, one need to put some kind of third-party WAF in front of the Traefik and route requests from there to Traefik. This increases the complexity of the system and complicates troubleshooting.
ModSecurity plugin for Traefik
Alexis Couvreur has developed wonderful and simple plugin for Traefik. It proxies all the requests to the Nginx container which mangles the requests through ModSecurity rules and if the request is fine then the Traefik will pass that to the real application.
How it works — technically
Quote from Alexis
This is a very simple plugin that proxies the query to the owasp/modsecurity apache container.
The plugin checks that the response from the waf container hasn’t an http code > 400 before forwarding the request to the real service.
If it is > 400, then the error page is returned instead.
The dummy service is created so the waf container forward the request to a service and respond with 200 OK all the time.
owasp/modsecurity-crs:nginx container needs some kind of service that responds all the time with the status code HTTP 200, so traefik/whoami container is used for that “dummy” service.
The OWASP website has further information about ModSecurity and the Core Rule Set.
Testing the solution
In this scenario, Nginx containers have taken the place of the Apache containers that Alexis originally used when he developed the plugin.
Below is a docker-compose.yaml -file which defines Traefik, ModSec/Nginx, Dummy and Whoami containers.
Spin up the docker containers and point your browser to http://localhost:8000 and you should get traditional Whoami container response:
Next test it with the URL http://localhost:8000/?waf=<script>alert(0)</script> and you should get Nginx default error page, because of XSS attack:
and from the logs we can see that ModSecurity has blocked that request.
{
"message": "Inbound Anomaly Score Exceeded (Total Score: 15)",
"details": {
"match": "Matched \"Operator `Ge' with parameter `10' against variable `TX:ANOMALY_SCORE' (Value: `15' )",
"reference": "",
"ruleId": "949110",
"file": "/etc/modsecurity.d/owasp-crs/rules/REQUEST-949-BLOCKING-EVALUATION.conf",
"lineNumber": "81",
"data": "",
"severity": "2",
"ver": "OWASP_CRS/3.3.4",
"rev": "",
"tags": [
"modsecurity",
"application-multi",
"language-multi",
"platform-multi",
"attack-generic"
],
"maturity": "0",
"accuracy": "0"
}
Performance
I did quick performance test with bombardier
Whoami container with WAF enabled:
Whoami container without WAF:
So the latency more than doubles when you put WAF in front of the service. Not the ideal situation. But this is simply because the plugin needs to make an extra HTTP call and check its return value.
Modifying the configuration of the plugin
The plugin itself is not very customizable, there’s not really anything to customize, but as the plugin uses OWASP ModSecurity docker image as a backend it is really well documented and it can be easily customised to suit your needs. The easiest way to do this is by modifying Docker’s environment variables. The options allow you to modify how aggressively requests are screened and how easily they are blocked.
Afterwords
This is by no means the best possible solution and there is significant room for improvement in this project, but this is the best plugin (and probably the only) currently available to Traefik for WAF functionality.
In the first chapters I wrote that an external Web Application Firewall adds complexity to the system, and it’s the same with this one. Additional containers that can break. So this does not ease the pain.
Hopefully someday in the future someone will make a proper plugin that utilises the ModSecurity rules and engine more “directly”. Or alternatively something completely different.