Skip to content

Caddy WAF (OWASP rule-based filtering, IP and DNS filtering, rate limiting, GeoIP)

License

Notifications You must be signed in to change notification settings

fabriziosalmi/caddy-waf

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

πŸ›‘οΈ Caddy WAF Middleware

A simple Web Application Firewall (WAF) middleware for the Caddy server, designed to provide comprehensive protection against web attacks. This middleware integrates seamlessly with Caddy and offers a wide range of security features to safeguard your applications.

Go Build and test Caddy with WAF CodeQL

🌟 Key Features

  • Rule-based request filtering with regex patterns.
  • IP and DNS blacklisting to block malicious traffic.
  • Country-based blocking using MaxMind GeoIP2.
  • Rate limiting per IP address to prevent abuse.
  • Anomaly scoring system for detecting suspicious behavior.
  • Request inspection (URL, args, body, headers, cookies, user-agent).
  • Protection against common attacks (SQL injection, XSS, RCE, Log4j, etc.).
  • Detailed logging and monitoring for security analysis.
  • Dynamic reloading on changes for rules, IP and DNS blacklists

πŸš€ Quick start

curl -fsSL -H "Pragma: no-cache" https://raw.githubusercontent.com/fabriziosalmi/caddy-waf/refs/heads/main/install.sh | bash

Example output

INFO	Starting caddy-waf	{"version": "v0.0.0-20250109090908-5a8c1c74fab0"}
INFO	Rate limit configuration	{"requests": 1000, "window": 60, "cleanup_interval": 300}
INFO	[INFO] Starting rate limiter cleanup goroutine
INFO	GeoIP database loaded successfully	{"path": "GeoLite2-Country.mmdb"}
INFO	Rules loaded	{"file": "rules.json", "total_rules": 14, "invalid_rules": 0}
INFO	IP blacklist loaded successfully	{"file": "ip_blacklist.txt", "valid_entries": 3, "total_lines": 3}
INFO	DNS blacklist loaded successfully	{"file": "dns_blacklist.txt", "valid_entries": 2, "total_lines": 2}
INFO	Rules and Blacklists loaded successfully	{"total_rules": 14}
INFO	WAF middleware provisioned successfully

πŸ“‘ Table of Contents

  1. πŸš€ Installation
  2. πŸ› οΈ Configuration
  3. βš™οΈ Configuration Options
  4. πŸ“œ Rules Format (rules.json)
  5. πŸ›‘οΈ Protected Attack Types
  6. 🚫 Blacklist Formats
  7. ⏱️ Rate Limiting
  8. 🌍 Country Blocking
  9. πŸ”„ Dynamic Updates
  10. πŸ§ͺ Testing
  11. 🐳 Docker Support
  12. 🐍 Rule/Blacklist Population Scripts
  13. πŸ“œ License
  14. πŸ™ Contributing

πŸš€ Installation

# Step 1: Clone the caddy-waf repository from GitHub
git clone https://github.com/fabriziosalmi/caddy-waf.git

# Step 2: Navigate into the caddy-waf directory
cd caddy-waf

# Step 3: Clean up and update the go.mod file
go mod tidy

# Step 4: Fetch and install the required Go modules
go get github.com/caddyserver/caddy/v2
go get github.com/caddyserver/caddy/v2/caddyconfig/caddyfile
go get github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile
go get github.com/caddyserver/caddy/v2/modules/caddyhttp
go get github.com/oschwald/maxminddb-golang
go get github.com/fsnotify/fsnotify
go get -v github.com/fabriziosalmi/caddy-waf
go mod tidy

# Step 5: Download the GeoLite2 Country database
wget https://git.io/GeoLite2-Country.mmdb

# Step 6: Build Caddy with the caddy-waf module
xcaddy build --with github.com/fabriziosalmi/caddy-waf=./

# Step 7: Fix Caddyfile format
caddy fmt --overwrite

# Step 8: Run the compiled Caddy server
./caddy run

Final Notes

  • If you encounter any issues, ensure that your Go environment is set up correctly and that you're using a compatible version of Go (as specified in the caddy-waf repository's go.mod file).
  • After building Caddy with xcaddy, the resulting binary will include the WAF middleware. You can verify this by running:
    ./caddy list-modules
    Look for the http.handlers.waf module in the output.

πŸ› οΈ Configuration

Basic Caddyfile Setup

{
    auto_https off
    admin off
}

:8080 {
    log {
        output stdout
        format console
        level DEBUG
    }

    route {
        waf {
            # Anomaly threshold will block request if the score is => the threshold
            anomaly_threshold 10

            # Rate limiting: 1000 requests per 1 minute
            rate_limit 1000 1m 5m

            # Rules and blacklists
            rule_file rules.json
            ip_blacklist_file ip_blacklist.txt
            dns_blacklist_file dns_blacklist.txt

            # Country blocking (requires MaxMind GeoIP2 database)
            block_countries GeoLite2-Country.mmdb RU CN KP

            # Whitelist countries (requires MaxMind GeoIP2 database)
            # whitelist_countries GeoLite2-Country.mmdb US

            # Set Log Severity
            log_severity info

            # Set Log JSON output
            log_json

            # Set Log JSON file path
            log_path debug.json
        }
        respond "Hello, world! This is caddy-waf" 200
    }
}

βš™οΈ Configuration Options

Option Description Example
anomaly_threshold Sets the anomaly score threshold. anomaly_threshold 20
rule_file JSON file containing WAF rules rule_file rules.json
ip_blacklist_file File with blocked IPs/CIDR ranges ip_blacklist_file blacklist.txt
dns_blacklist_file File with blocked domains dns_blacklist_file domains.txt
rate_limit Rate limiting config rate_limit 100 1m
block_countries Country blocking config block_countries GeoLite2-Country.mmdb RU CN KP
whitelist_countries Country whitelisting config whitelist_countries GeoLite2-Country.mmdb US
log_severity Sets the minimum logging severity level for this module. log_severity debug
log_json Enables JSON log output log_json
log_path JSON debug log debug.json

πŸ“œ Rules Format (rules.json)

Rules are defined in a JSON file. Each rule specifies a pattern to match, targets to inspect, and actions to take.

[
    {
        "id": "wordpress-brute-force",
        "phase": 2,
        "pattern": "(?i)(?:wp-login\\.php|xmlrpc\\.php).*?(?:username=|pwd=)",
        "targets": ["URI", "ARGS"],
        "severity": "HIGH",
        "action": "block",
        "score": 8,
        "description": "Block brute force attempts targeting WordPress login and XML-RPC endpoints."
    }
]

Rule Fields

Field Description Example
id Unique identifier for the rule, used to track and manage security policies. sql_injection
phase Processing phase, executed in order (1 - request headers, 2 - body, 3 - response headers, 4 - response body). 1
pattern Regular expression pattern for detecting threats, allows precise filtering of malicious payloads. (?i)(?:insert)
targets Specifies areas of the request to inspect for malicious content (URI, ARGS, BODY, HEADERS, COOKIES). ["ARGS", "BODY"]
severity Rule severity indicates the risk level of the threat (CRITICAL, HIGH, MEDIUM, LOW). CRITICAL
action Defines the action to take when the rule matches (block or log). If unspecified, the default action is block. default: block
score Score added to the anomaly detection system when a rule matches, contributing to overall request evaluation. 10
description Provides a human-readable explanation of the rule's purpose and conditions it checks for. Block SQL injection attempts.
mode Optional field that allows switching between strict blocking and passive logging. block or log
enabled Boolean flag to enable or disable specific rules dynamically. true
log_detail Additional logging level for matched rules, providing extended information during forensic analysis. true or false

Rules can be fine-tuned by adjusting the score and severity values, allowing greater flexibility in blocking decisions. More complex patterns targeting multiple request areas can be designed using regexes, ensuring multi-layered defense strategies are effectively enforced.


πŸ›‘οΈ Protected Attack Types

  1. SQL Injection
    • Basic SELECT/UNION injections
    • Time-based injection attacks
    • Boolean-based injections
  2. Cross-Site Scripting (XSS)
    • Script tag injection
    • Event handler injection
    • SVG-based XSS
  3. Path Traversal
    • Directory traversal attempts
    • Encoded path traversal
    • Double-encoded traversal
  4. Remote Code Execution (RCE)
    • Command injection
    • Shell command execution
    • System command execution
  5. Log4j Exploits
    • JNDI lookup attempts
    • Nested expressions
  6. Protocol Attacks
    • Git repository access
    • Environment file access
    • Configuration file access
  7. Scanner Detection
    • Common vulnerability scanners
    • Web application scanners
    • Network scanning tools

🚫 Blacklist Formats

IP Blacklist (ip_blacklist.txt)

192.168.1.1
10.0.0.0/8
2001:db8::/32

DNS Blacklist (dns_blacklist.txt)

malicious.com
evil.example.org

⏱️ Rate Limiting

Configure rate limits using requests count and time window:

# 100 requests per minute
rate_limit 100 1m 5m

# 10 requests per second
rate_limit 10 1s 5m

# 1000 requests per hour
rate_limit 1000 1h 5m

# note the last 5m is the cleanup routine cycle to remove stale address from memory

🌍 Country Blocking

Block traffic from specific countries using ISO country codes:

# Block requests from Russia, China, and North Korea
block_countries /path/to/GeoLite2-Country.mmdb RU CN KP

πŸ”„ Dynamic Updates

Rules and blacklists can be updated without server restart:

  1. Modify rules.json or blacklist files.
  2. Reload Caddy: caddy reload.

πŸ§ͺ Testing

Basic Testing

A test.sh script is included in this repository to perform a comprehensive security test suite. This script sends a series of forged curl requests, each designed to simulate a different type of attack.

caddy-waf % ./test.sh
WAF Security Test Suite
Target: http://localhost:8080
Date: Mar  7 Gen 2025 01:12:22 CET
----------------------------------------
[βœ—] SQL Injection - SQL Server Version                           [200]
[βœ—] SQL Injection - SQL Server Time Delay                        [200]
[βœ“] SQL Injection - Oracle Time Delay                            [403]
[βœ—] SQL Injection - Error Based 1                                [200]
[βœ—] SQL Injection - Error Based 2                                [200]
[βœ—] SQL Injection - Error Based 3                                [200]
[βœ—] SQL Injection - MySQL user                                   [200]
[βœ—] SQL Injection - PostgreSQL user                              [200]
[βœ—] SQL Injection - Case Variation                               [200]
[βœ—] SQL Injection - Whitespace Variation                         [200]
[βœ—] SQL Injection - Obfuscation Variation                        [200]
[βœ—] SQL Injection - Unicode Variation                            [200]
[βœ—] SQL Injection - Triple URL Encoded Variation                 [200]
[βœ—] SQL Injection - OOB DNS Lookup                               [200]
[βœ—] SQL Injection - Oracle OOB DNS Lookup                        [200]
[βœ“] SQL Injection - Header - Basic Select                        [403]
[βœ“] SQL Injection - Cookie - Basic Select                        [403]
[βœ—] SQL Injection - Header - Basic Select                        [200]
[βœ—] SQL Injection - JSON body                                    [200]
[βœ“] XSS - Basic Script Tag                                       [403]

[βœ“] XSS - IMG Onerror                                            [403]
[βœ“] XSS - JavaScript Protocol                                    [403]
[βœ“] XSS - SVG Onload                                             [403]
[βœ“] XSS - Anchor Tag JavaScript                                  [403]
[βœ“] XSS - URL Encoded Script                                     [403]
[βœ“] XSS - Double URL Encoded                                     [403]
[βœ“] XSS - URL Encoded IMG                                        [403]
[βœ“] XSS - Body Onload                                            [403]
[βœ“] XSS - Input Onfocus Autofocus                                [403]
[βœ“] XSS - Breaking Out of Attribute                              [403]
[βœ“] XSS - HTML Encoded                                           [403]
[βœ“] XSS - IFRAME srcdoc                                          [403]
[βœ“] XSS - Details Tag                                            [403]
[βœ“] XSS - HTML Comment Breakout                                  [403]
[βœ“] Path Traversal - Basic                                       [403]

[βœ“] Path Traversal - Double Dot                                  [403]
[βœ“] Path Traversal - Triple Dot                                  [403]
[βœ“] Path Traversal - URL Encoded                                 [403]
[βœ—] Path Traversal - Double URL Encoded                          [200]
[βœ“] Path Traversal - Mixed Slashes                               [403]
[βœ—] Path Traversal - UTF-8 Encoded                               [200]
[βœ“] Path Traversal - Encoded and Literal                         [403]
[βœ“] Path Traversal - Mixed Encoding                              [403]
[βœ—] Path Traversal - Multiple Slashes                            [200]
[βœ“] RCE - Basic Command                                          [403]

[βœ“] RCE - Base64 Command                                         [403]
[βœ—] RCE - Backticks                                              [200]
[βœ—] RCE - List Files                                             [200]
[βœ—] RCE - Uname                                                  [200]
[βœ—] RCE - ID                                                     [200]
[βœ“] RCE - whoami Command                                         [403]
[βœ“] RCE - Echo Test                                              [403]
[βœ—] RCE - Hex Encoded Command                                    [200]
[βœ“] RCE - Curl Request                                           [403]
[βœ“] RCE - Wget Request                                           [403]
[βœ—] RCE - Ping                                                   [200]
[βœ—] RCE - PowerShell Command                                     [200]
[βœ—] Log4j - JNDI LDAP                                            [200]

[βœ—] Log4j - Environment                                          [200]
[βœ—] Log4j - JNDI RMI                                             [200]
[βœ—] Log4j - System Property                                      [200]
[βœ—] Log4j - Lowercase                                            [200]
[βœ—] Log4j - Uppercase                                            [200]
[βœ—] Log4j - Date                                                 [200]
[βœ“] Log4j - Base64                                               [403]
[βœ—] Log4j - Partial Lookup                                       [200]
[βœ—] Log4j - URL Encoded                                          [200]
[βœ“] Header - SQL Injection                                       [403]

[βœ“] Header - XSS Cookie                                          [403]
[βœ“] Header - Path Traversal                                      [403]
[βœ“] Header - Custom X-Attack                                     [403]
[βœ—] Header -  X-Forwarded-Host                                   [200]
[βœ“] Header - User-Agent SQL                                      [403]
[βœ—] Header -  Host Spoof                                         [200]
[βœ“] Header -  Accept-Language                                    [403]
[βœ“] Protocol - Git Access                                        [403]

[βœ“] Protocol - Env File                                          [403]
[βœ“] Protocol - htaccess                                          [403]
[βœ—] Protocol - Web.config Access                                 [200]
[βœ“] Protocol - Java Web Descriptor                               [403]
[βœ“] Protocol - SVN Access                                        [403]
[βœ—] Protocol - Robots.txt                                        [200]
[βœ—] Protocol - VS Code Settings                                  [200]
[βœ—] Protocol - config.php Access                                 [200]
[βœ—] Protocol - Apache Server Status                              [200]
[βœ“] Valid - Homepage                                             [200]

[βœ“] Valid - API Endpoint                                         [200]
[βœ“] Scanner - SQLMap                                             [403]

[βœ“] Scanner - Acunetix                                           [403]
[βœ“] Scanner - Nikto                                              [403]
[βœ“] Scanner - Nmap                                               [403]
[βœ“] Scanner - Dirbuster                                          [403]
[βœ“] Valid - Health Check                                         [200]

[βœ“] Valid - Chrome Browser                                       [200]
[βœ—] Scanner -  Burp Suite                                        [200]

[βœ“] Scanner - OWASP ZAP                                          [403]
[βœ“] Scanner - Nessus                                             [403]
[βœ“] Scanner - Qualys                                             [403]
[βœ“] Scanner -  Wfuzz                                             [403]
[βœ“] Scanner -  OpenVAS                                           [403]
----------------------------------------
Results Summary
Total Tests: 100
Passed: 57
Failed: 43
Pass Percentage: 57%
Warning:  Test pass percentage is below 90%. Review the failures!

Detailed results saved to: waf_test_results.log

Load Testing

ab -n 1000 -c 100 http://localhost:8080/

🐳 Docker Support

A Dockerfile is included to simplify building a Docker image with the Caddy server and WAF middleware. Here's how to use it:

# Build the Docker image
docker build -t caddy-waf .

# Run the Docker container
docker run -p 8080:8080 caddy-waf

🐍 Rule/Blacklist Population Scripts

Three Python scripts are provided in this repository to help automate the population of your rules and blacklists:

get_owasp_rules.py

This script fetches the OWASP core rules and converts them into the JSON format required for the WAF rules.

python3 get_owasp_rules.py

get_blacklisted_ip.py

This script downloads the blacklisted IPs from several external sources.

python3 get_blacklisted_ip.py

get_blacklisted_dns.py

This script downloads blacklisted domains from various sources.

python3 get_blacklisted_dns.py

🌐 Combining Caddy Modules for Enhanced Security

Did you know you can combine caddy-waf, caddy-mib, and caddy-mlf to create a robust multi-layered security solution for your web applications? By chaining these modules, you can leverage their unique features to provide comprehensive protection against web attacks, abusive behavior, and suspicious traffic patterns.

πŸ”— Chain Overview

By chaining these modules, you can set up a flow where each layer contributes to filtering, banning, and analyzing traffic for maximum security:

Module Role in the Chain Repository Link
caddy-waf Acts as the first gate, inspecting and filtering malicious requests based on anomaly scores, rate limits, and blacklists. GitHub: caddy-waf
caddy-mib Handles IP banning for repeated errors, such as 404 or 500, to prevent brute force or abusive access attempts. GitHub: caddy-mib
caddy-mlf Provides an additional layer of protection by analyzing request attributes and marking/blocking suspicious traffic based on anomaly thresholds. GitHub: caddy-mlf

πŸ”§ Example Configuration

Here’s an example configuration to chain the modules:

Flow:

  1. caddy-waf: Listens on localhost:8080 and forwards requests to caddy-mib.
  2. caddy-mib: Listens on localhost:8081 and forwards requests to caddy-mlf.
  3. caddy-mlf: Listens on localhost:8082 and returns a 200 OK response for legitimate requests or forwards requests to your origin applications.

πŸ“œ License

This project is licensed under the AGPLv3 License.


πŸ™ Contributing

Contributions are welcome! Please open an issue or submit a pull request.