Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

NGX-Shield: Production-grade NGINX WAF Stack

License: MIT Docker Compose OWASP CRS

A production-ready Docker-Compose stack that deploys an NGINX Web Application Firewall (WAF) powered by ModSecurity and the OWASP Core Rule Set (CRS), complete with observability via Prometheus and Grafana.

Table of Contents

Overview

NGX-Shield provides a secure, configurable, and isolated environment for testing and deploying NGINX as a WAF. The stack includes:

  • NGINX as a reverse proxy and WAF gateway
  • ModSecurity with the OWASP CRS for real-time threat protection (CRS ships bundled inside the owasp/modsecurity-crs image, no separate download needed)
  • Prometheus for metrics collection and monitoring
  • Grafana for visualization and alerting
  • NGINX Exporter to expose NGINX metrics to Prometheus
  • A simple Flask application for testing WAF functionality

This stack is ideal for learning WAF concepts, testing security rules, or as a starting point for production deployments.

Features

  • 🛡️ Real-time WAF Protection: Blocks common web exploits (SQLi, XSS, RCE, etc.) using OWASP CRS
  • 📊 Full Observability: Metrics via Prometheus, dashboards via Grafana
  • 🐳 Dockerized: Easy deployment with Docker Compose
  • 🔧 Configurable: Simple to adjust rules, ports, and settings
  • 📝 Detailed Logging: ModSecurity audit log, NGINX access/error logs
  • 🧪 Testing Ready: Includes test application and verification steps
  • 📚 Well Documented: Comprehensive guides for setup, testing, and extension

Project Structure

ngx-shield/
├── docker-compose.yml          # Main compose file
├── nginx/                      # NGINX configuration
│   ├── nginx.conf              # Main NGINX config
│   ├── conf.d/
│   │   ├── waf.conf            # WAF-specific site config (app proxy + stub_status)
│   │   ├── logging.conf        # Log formats
│   │   └── modsecurity.conf    # Enables the ModSecurity module
│   └── modsec/                 # ModSecurity configuration (mounted as individual files,
│       ├── modsecurity.conf    # not as a directory, so the image's bundled OWASP CRS
│       ├── modsecurity-override.conf  # rules at /etc/modsecurity.d/owasp-crs/ stay intact)
│       └── setup.conf          # Includes the conf files above + the bundled CRS rules
├── prometheus/                 # Prometheus configuration
│   └── prometheus.yml
├── grafana/                    # Grafana provisioning
│   └── provisioning/
│       ├── datasources/ds.yml
│       └── dashboards/
│           ├── dashboards.yml      # Provider config that tells Grafana to load the JSON below
│           └── ngx-shield.json
├── logs/                       # Bind-mounted logs directory
├── app/                        # Test Flask application
│   ├── Dockerfile
│   └── app.py
└── README.md                   # This file

Prerequisites

Quick Start

1. Launch the Stack

The OWASP CRS ships bundled inside the owasp/modsecurity-crs:nginx-alpine image at /etc/modsecurity.d/owasp-crs/ — there's nothing to clone or initialize separately.

docker compose up -d

2. Verify Services

Check that all containers are healthy:

docker compose ps

3. Test the WAF

The WAF is published on host port 8080 (see docker-compose.yml):

  • Normal request (should return 200):

    curl -i http://localhost:8080/
  • SQL injection attempt (should be blocked with 403):

    curl -i "http://localhost:8080/?id=1'+OR+1=1--"

4. Access Grafana

  • URL: http://localhost:3000
  • Username: admin
  • Password: ngxshield
  • The NGX-Shield dashboard is automatically provisioned and displays key metrics (requests/sec, active connections, connection states, accepted vs. handled connections).

Testing the WAF

Module-Specific Tests

1. App and NGINX-WAF

docker compose up -d app nginx-waf
# Test normal request and SQLi as above
docker compose stop app nginx-waf

2. NGINX Exporter

docker compose up -d app nginx-waf nginx-exporter
curl -s http://localhost:9113/metrics | head -n 20
docker compose stop app nginx-waf nginx-exporter

3. Prometheus

docker compose up -d app nginx-waf nginx-exporter prometheus
# Visit http://localhost:9090, check target status, query nginx_http_requests_total
docker compose stop app nginx-waf nginx-exporter prometheus

4. Grafana (Full Stack)

docker compose up -d
# Visit http://localhost:3000, explore pre-built dashboard

WAF-Specific Panels (Requires Loki)

The bundled dashboard only covers connection/request metrics from nginx-prometheus-exporter's stub_status scrape — per-status-code rates, blocked IPs, and triggered CRS rule IDs aren't available from stub_status. To get those, add Loki and Promtail to ingest modsec_audit.log:

  1. Add to docker-compose.yml:

    loki:
      image: grafana/loki:2.9.4
      ports: ["3100:3100"]
      networks: [shield]
    
    promtail:
      image: grafana/promtail:2.9.4
      volumes:
        - ./logs:/var/log/nginx:ro
        - ./promtail-config.yml:/etc/promtail/config.yml:ro
      networks: [shield]
  2. Create promtail-config.yml:

    server:
      http_listen_port: 9080
      grpc_listen_port: 0
    
    positions:
      filename: /tmp/positions.yaml
    
    clients:
      - url: http://loki:3100/loki/api/v1/push
    
    scrape_configs:
      - job_name: nginx
        static_configs:
          - targets: [localhost]
            labels:
              job: nginx
              __path__: /var/log/nginx/modsec_audit.log
  3. Add a Loki data source in Grafana and extend ngx-shield.json with Loki-backed panels.

Observability Stack

Component Host Port Purpose
NGINX (WAF) 8080 WAF entry point (proxies to the app)
NGINX stub_status 8081 Internal metrics endpoint scraped by NGINX Exporter
Prometheus 9090 Metrics collection and querying
Grafana 3000 Visualization and alerting
NGINX Exporter 9113 Exposes NGINX metrics to Prometheus
Test app 3001 Direct access to the Flask test app (bypassing the WAF)

Configuration

ModSecurity Rules

  • The OWASP CRS itself lives inside the image at /etc/modsecurity.d/owasp-crs/ — upgrade it by pulling a newer owasp/modsecurity-crs image tag.
  • Core directives: nginx/modsec/modsecurity.conf
  • Local overrides/custom rules: add directives to nginx/modsec/modsecurity-override.conf
  • Include order: nginx/modsec/setup.conf
  • To toggle between DetectionOnly and Prevention modes, edit SecRuleEngine in nginx/modsec/modsecurity.conf

NGINX Settings

  • Main config: nginx/nginx.conf
  • WAF site: nginx/conf.d/waf.conf (adjust proxy_pass for your application)
  • Log formats: nginx/conf.d/logging.conf

Prometheus & Grafana

  • Prometheus scrape config: prometheus/prometheus.yml
  • Grafana dashboards: grafana/provisioning/dashboards/
  • Data sources: grafana/provisioning/datasources/ds.yml

Maintenance

Log Monitoring

# ModSecurity audit log (attack details)
tail -f logs/modsec_audit.log

# NGINX access log
tail -f logs/access.log

# NGINX error log
tail -f logs/error.log

Stack Management

# View logs for all services
docker compose logs -f

# Restart specific service
docker compose restart nginx-waf

# Update the WAF image (also picks up a newer bundled OWASP CRS release)
docker compose pull nginx-waf
docker compose up -d nginx-waf

Security Best Practices

  1. Always test rules in DetectionOnly mode first
  2. Regularly update the owasp/modsecurity-crs image to pick up newer CRS releases
  3. Review logs for false positives and tune rules accordingly
  4. Consider rate limiting and IP reputation for production
  5. Use HTTPS in production (terminate at upstream or add certs to NGINX — TLS isn't configured in this stack by default)

Contributing

We welcome contributions to improve NGX-Shield! Please follow these guidelines:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/AmazingFeature)
  3. Commit your changes (git commit -m 'Add some AmazingFeature')
  4. Push to the branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

Contribution Types

  • Documentation improvements
  • New feature requests (WAF rules, exporters, dashboards)
  • Bug fixes
  • Configuration examples
  • Testing scenarios

Please ensure your contributions align with the project's goal of providing a production-ready, educational WAF stack.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Note: The OWASP CRS bundled inside the owasp/modsecurity-crs image is licensed separately under the Apache License 2.0.

Contact

Project Link: https://github.com/sudoNaji/ngx-shield

For questions, suggestions, or security concerns, please open an issue on the GitHub repository.


Built with ❤️ for secure web applications

About

ngx-shield — A lightweight and extensible NGINX security layer that protects web applications from malicious traffic through efficient request filtering, threat detection, and customizable defense rules.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages