Setup Nginx as a Reverse Proxy and WAF with ModSecurity in Docker

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.