Setup Nginx as a Reverse Proxy and WAF with ModSecurity in Docker
Nginx is a highly versatile web server which can effectively function as a reverse proxy and web application firewall (WAF). To function as a WAF, the ModSecurity WAF module is usually used with the OWASP ModSecurity Core Rule Set.
The ModSecurity WAF module will check incoming requests and outgoing responses against the rules it has loaded. If the request matches one of the rules, it will be ended before it is passed to the web application. The OWASP ModSecurity Core Rule Set contains generic rules intended to protect web applications from a variety of common attacks such as SQL injection and XSS.
I run a few different applications using Docker, mostly because it makes them easier for me to manage and update. Part of this includes an Nginx container which acts as a reverse proxy for the other web application containers. To help increase security I used a Nginx image with a WAF built in.
Prerequisites
The following software will need to be setup and installed to be able to follow the steps below.
- Docker
- Docker Compose
The aim
For this example, I am going to setup an Apache server for some static pages and a WordPress website. Both will run in their own docker containers, and the WordPress container will have its own docker network for accessing its database container. The final container will be an Nginx container which will act as the reverse proxy with WAF.
Creating the reverse proxy docker network
The network used by the reverse proxy to connect to the containers we’d like to expose will be used by a few different services. Because of this, it is best to create it separately. We can do this with a quick docker command.
docker network create reverse-proxy
This will create a docker network with the name “reverse-proxy” which the containers we’d like to expose can connect to.
Setup WordPress and Apache
Just to have some example apps running, WordPress and Apache can be setup each of the following docker compose files. This can be skipped if you already have a running container you want to reverse proxy.
These files includes the setup required for making sure the containers are connected to the required docker networks, so you may need to amend your files appropriately to do the same.
Be sure to amend any password placeholders to something secure!
WordPress Docker Compose
version: "3.3"
services:
db:
image: mariadb:latest
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: yhesNBmKLeXGGwpY3BzsCG3Zfbxi6Zz2
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: dbpassword
networks:
wordpress:
aliases:
- db
wordpress:
depends_on:
- db
image: wordpress:latest
volumes:
- wordpress_data:/var/www/html
restart: always
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: dbpassword
WORDPRESS_DB_NAME: wordpress
networks:
wordpress:
aliases:
- site
reverse-proxy:
aliases:
- wordpress
volumes:
db_data:
wordpress_data:
networks:
wordpress:
driver: bridge
reverse-proxy:
external: true
Apache Docker Compose
test
Setup Nginx with ModSecurity Web Application Firewall
The free Nginx docker image does not include the ModSecurity module required load and process the OWASP ModSecurity Core Rule Set. This leaves the option of building a new image which includes the necessary module or finding an image from a different provider which already includes the module.
Thankfully, OWASP provides this docker image includes ModSecurity and the core rule set. This makes everything simpler and easier to manage, as to update the rules of Nginx version only the new docker image needs updating and the container recreating.
The following docker compose file will create a container which connects to the reverse-proxy network which will allow it to communicate with the containers it needs to proxy requests for. It will also setup a volume binds to allow the proxy configuration to be managed. This is a file named proxy.conf
.
Nginx WAF Docker Compose
version: '3.3'
services:
reverseproxy:
container_name: reverse-proxy-nginx
ports:
- '80:80'
volumes:
- ~/docker/reverse-proxy-nginx/volumes/proxy.conf:/etc/nginx/conf.d/proxy.conf:ro
environment:
- PROXY=1
restart: always
image: owasp/modsecurity-crs:nginx-alpine
networks:
reverse-proxy:
aliases:
- reverse-proxy-nginx
networks:
reverse-proxy:
external: true
Proxy Configuration
server {
listen 80;
server_name test.example.com;
location / {
proxy_pass http://webserver-apache:80;
}
}
server {
listen 80;
server_name test1.example.com;
location / {
proxy_pass http://wordpress:80;
proxy_set_header Host $host;
}
}
Starting to Reverse Proxy
You will need to make sure that all volume binds and container names match the above, or have been amended to match your environment as needed. When this is all in place you can start the reverse proxy using docker compose with the following command.
docker-compose up -d
Amending ModSecurity Rules Configuration
Sometimes it may be necessary for you to amend the rules used for a specific host. This can be done by amending the proxy configuration file as needed for your scenario. For example, to disable rule 949110
for the WordPress website, I could modify that server block to the below.
server {
listen 80;
server_name test1.example.com;
location / {
proxy_pass http://wordpress:80;
proxy_set_header Host $host;
modsecurity_rules '
SecRuleRemoveById 949110
';
}
}
You can find information about more complex modifications in the ModSecurity CRS Tuning section on the docker hub page.
Future Steps
After this, there should be the Apache web server, and WordPress website available at their respective server addresses. The reverse proxy should be filtering the web requests to those addresses.
It would be a good idea to modify the reverse proxy further to enable SSL and provide an appropriate certificate to allow HTTPS connections to be used instead of HTTP.