Traefik itself does not include WAF capabilities. If you want to add this capability, you can opt to replace Traefik with Apache httpd or nginx coupled with ModSecurity, however you loose the autoconfiguration of Traefik.
Fortunately, Alexis Couvreur has developed a ModSecurity plugin for Traefik to forward requests received by Traefik to another webserver (running ModSecurity) before actually forwarding the requests to the application server. If the ModSecurity webserver returns a code > 400, then Traefik will reject the request, otherwise it will forward it to the application server.
The suggested setup uses owasp/modsecurity-crs image for ModSecurity and since this can act as a reverse proxy, it uses the well known containous/whoami image as backend, since it is lightweight and always return a 200 status code.
The setup I decided to use is the identical with the addition of SSL between the components, and multiple WAF containers depending on their intended use (paranoia level, detection only, different rules, etc.).
Let’s first create the SSL certificate. Since this is a test environment, a self-signed certificate is fine. For production use, I recommend signing the certificate with your existing CA. The common name matches the value of environment variable
v3_req extensions are included to generate a server certificate instead of a CA certificate.
openssl req -x509 -nodes -newkey ec -pkeyopt ec_paramgen_curve:secp384r1 -keyout server.key -out server.crt -days 3650 -subj "/C=US/ST=California/L=Log Angeles/O=Foobar/CN=waf" -addext "subjectAltName=DNS:*" -extensions v3_req
Traefik needs to trust this certificate, so we need to create a custom image. Create a
traefik directory with the following content:
ADD server.crt /usr/local/share/ca-certificates/server.crt
docker-compose.yml file, replace the
image: traefik:2.5.7 with
build: traefik and build the container image with
docker compose build traefik.
Since it uses a Traefik plugin, you will need a Traefik Pilot token. Once you have a token, pass it to your Traefik container using environment variable
PILOT_TOKEN. I use a
.env file with the following content:
Then, add the following items to the command of your Traefik container:
Then add the following label to your Traefik container:
Let’s add the WAF service:
BACKEND variable matches the
dummy container name.
The suggested configuration uses
containous/whoami but I have decided to use nginx. The main reason is stability: I have had some issues with
containous/whoami, sometimes it crashed with no apparent reason. We are going to replace nginx default configuration file with our own and pass it the SSL certificate:
Paste the following in a file named
error_log stderr notice;
listen 443 ssl http2;
listen [::]:443 ssl http2;
return 200 'OK\n';
add_header Strict-Transport-Security "max-age=63072000" always;
This configuration enables SSL and makes nginx reply with a status code 200 whatever the request is.
Restart the Traefik stack using
docker compose up -d.
Add WAF to an app
docker-compose.yml for an app you want to protect and add the following label:
Restart the app stack.
Validate ModSecurity rules
Call your app normally first, you should not experiment errors.
?test=../ to the URI, and you should receive a status code
Adding more WAF services
We now have seen how to have one WAF container. To add another WAF container, you need to:
docker-compose.yml, copy/paste the
waf service, rename it to your liking (eg:
wafparanoia4) and adapt the environment variables (eg:
docker-compose.yml, add a new HTTP middleware matching your new WAF service (eg:
wafparanoia4) to Traefik pointing to this new WAF container
docker-compose.yml of the app to be protected by this new WAF service, add a label to use the middleware you just created
- restart your Traefik stack
- restart your app stack
Since the request is forwarded to a dummy container, only the request is actually analyzed. If the requests passes WAF checks, it will go to your app server. Then, if the response of your app contains something that would be blocked by WAF, here it will not.
If you need to analyze the response as well, then I think you should not use this plugin but add a container owasp/modsecurity-crs in the app stack and use this as backend of Traefik.
Another solution could be to use a single owasp/modsecurity-crs container and overwrite the config file
conf/extra/httpd-vhosts.conf to specify your own backends.
That’s it folks.