Skip to content

Nginx reverse proxy with ModSecurity Web Application Firewall, SSL termination (certbot) and brotli compression

License

Notifications You must be signed in to change notification settings

vlche/docker-nginx-waf

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

71 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

vlche/nginx-waf

Docker alpine based container providing nginx with modsecurity3, brotli compression and certbot for Let's Encrypt's SSL certificates auto-renewal. You can use it as all-in-one service, or as a SSL/Load-Balancer frontend and WAF backend/backends. Additionally preconfigured options are:

SSL/TLS Optimized intermediate ssl settings. General-purpose servers with a variety of clients, recommended for almost all systems by Mozilla. Certificates are from Lets's encrypt, generated by certbot, auto renewed by separate container, hooks are preconfigured to reload nginx-waf container

brotli Default preconfigured options for Brotli are: dynamic compression, level 6

Security Headers Preconfigured optimized http headers are included and enabled, but you should certainly know what you are doing!

lua

Docker Automated build Docker Build Status GitHub issues GitHub license Docker Pulls MicroBadger Size

Launch nginx-waf using the default config:

docker run --name nginx-waf \
  --restart=always \
  --net=host \
  -e TZ=Europe/Berlin \
  -e WORKERS=5 \
  -v /data/nginx/conf.d:/etc/nginx/conf.d:rw \
  -v /data/letsencrypt:/etc/letsencrypt:rw \
  -v /data/www:/www:rw \
  -p 80:80 -p 443:443 -d \
  vlche/nginx-waf
  • Env variable CRON starts cron embedded.
  • Env variable TZ sets your timezone.
  • Env variable WAF_INSTANCE points cron notify target to WAF instance
  • Env variable WORKERS sets nginx's worker_processes to your value.

Certbot's autoupdater can be run as an embedded cron daemon or as a separate cron container. To run it embedded just set CRON variable:

docker run --name nginx-waf \
...
  -e CRON=1 \
...
  vlche/nginx-waf

or To run certbot's cron updater as a separate container set WAF_INSTANCE variable to match your nginx-waf instance:

docker run --name nginx-waf-cron \
  --restart=always \
  -e TZ=Europe/Berlin \
  -e WAF_INSTANCE=nginx-waf \
  -v /data/nginx/conf.d:/etc/nginx/conf.d:rw \
  -v /data/letsencrypt:/etc/letsencrypt:rw \
  -v /data/www:/www:rw \
  -v /run/docker.sock:/run/docker.sock \
  vlche/nginx-waf
  /cron.sh

ModSecurity

Pre-configured with rules from OWASP CRS on my default. If you want to disable it for a particular location simply set it to off

upstream backend {
    server 127.0.0.1:9000;
}

server {
    listen       80;
    #listen       [::]80;
    server_name  insecure.example.com;
    #listen 443 ssl http2;
    #listen [::]:443 ssl http2;

    #ssl_certificate /etc/letsencrypt/live/insecure.example.com/fullchain.pem; # managed by Certbot
    #ssl_certificate_key /etc/letsencrypt/live/insecure.example.com/privkey.pem; # managed by Certbot
    #ssl_trusted_certificate /etc/letsencrypt/live/insecure.example.com/chain.pem; # managed by Certbot
    #ssl_stapling on; # managed by Certbot
    #ssl_stapling_verify on; # managed by Certbot

    #access_log  /var/log/nginx/host.access.log  main;

    # modsec has already been enabled globally in 01-local.conf
    #
    #modsecurity on;
    #modsecurity_rules_file /etc/nginx/modsec/main.conf;

    # include letsencrypt endpoints to bypass proxy and be able to autoupdate:
    include snippets/letsencrypt.conf;
    # add some CSRF headers:
    include snippets/policy_headers.conf;

    location / {
        root   /usr/share/nginx/html;
    }

    # serve static files with modsecurity disabled
    #
    #location /static/ {
    #    modsecurity off;
    #    root   /usr/share/nginx/html;
    #}

    # disable SecRule # 949110 for /api/ route:
    #
    #location /api/ {
    # set proxy headers: X-Forwarded-Proto, Host, X-Forwarded-Host, X-Forwarded-For, X-Real-IP for upstreams:
    #    include snippets/proxy_headers.conf;
    #    proxy_pass $backend;
    #    modsecurity_rules "SecRuleRemoveById 949110";
    #}

    # proxy requests to remote WebSockets backends for /ws/ route:
    #
    #location /ws/ {
    # set proxy headers: X-Forwarded-Proto, Host, X-Forwarded-Host, X-Forwarded-For, X-Real-IP for upstreams,
    # enable connection upgrade:
    #    include snippets/proxy_headers_ws.conf;
    #    proxy_pass $backend;
    #}

    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    #location ~ \.php$ {
    # set proxy headers: X-Forwarded-Proto, Host, X-Forwarded-Host, X-Forwarded-For, X-Real-IP for upstreams:
    #    include snippets/proxy_headers.conf;
    #    proxy_pass   http://127.0.0.1;
    #}

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ \.php$ {
    #    root           html;
    #    fastcgi_pass   127.0.0.1:9000;
    #    fastcgi_index  index.php;
    #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
    #    include        fastcgi_params;
    #}

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #    deny  all;
    #}

}

If you have multiple server blocks, it is recommended to load ModSecurity rules file in http context, and then customize SecRules per endpoints, to ruduce memory footprint. Example of /etc/nginx/conf.d/00-local.conf:

# modsec is enabled for every server context.
# If you want to enable it on a per server basis, please disable it here!
modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/main.conf;

# you should use reachable resolver for ssl stapling
# this one is generated from /etc/resolv.conf's first nameserver
include snippets/resolver.conf;
# this one is whatever you say
#resolver 8.8.8.8;

include snippets/log.conf;
include snippets/ssl.conf;
include snippets/brotli.conf;
# increased for nextcloud like apps
client_max_body_size        512m;

# define indexes here to reduce per vhost complexity
index  index.html index.htm;

# map to proxify WebSockets
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

Certbot

Easily add SSL security to your nginx hosts with certbot. docker exec -it nginx-waf /bin/sh will bring up a prompt at which time you can certbot to your hearts content.

or

docker exec -it nginx-waf certbot --no-redirect --must-staple -d example.com

It even auto-renew's for you every day!