caddywaf

package module
v0.0.0-...-0b814de Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 1, 2025 License: AGPL-3.0 Imports: 26 Imported by: 0

README

🛡️ Caddy WAF Middleware

A robust, highly customizable, and feature-rich Web Application Firewall (WAF) middleware for the Caddy web server. This middleware provides advanced protection against a comprehensive range of web-based threats, seamlessly integrating with Caddy and offering flexible configuration options to secure your applications effectively.

Tests CodeQL Build, Run and Validate

🛡️ Core Protections

  • Regex-Based Filtering: Deep URL, data & header inspection using powerful regex rules.
  • Blacklisting: Blocks malicious IPs, domains & optionally TOR exit nodes.
  • Geo-Blocking: Restricts access by country using GeoIP.
  • Rate Limiting: Prevents abuse via customizable IP request limits.
  • Anomaly Scoring: Dynamically blocks requests based on cumulative rule matches.
  • Multi-Phase Inspection: Analyzes traffic throughout the request lifecycle.
  • Sensitive Data Redaction: Removes private info from logs.
  • Custom Response Handling: Tailored responses for blocked requests.
  • Detailed Monitoring: JSON endpoint for performance tracking & analysis.
  • Dynamic Config Reloads: Seamless updates without restarts.
  • File Watchers: Automatic reloads on rule/blacklist changes.
  • Observability: Seamless integration with ELK stack and Prometheus.
  • Rules generator: powered by custom GPT, try it here

Simple at a glance UI :) demo

🚀 Quick Start

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

Example Output:

2025/01/29 13:50:49.791 INFO    Provisioning WAF middleware     {"log_level": "info", "log_path": "debug.json", "log_json": true, "anomaly_threshold": 10}
2025/01/29 12:50:49.918 INFO    http.handlers.waf       Tor exit nodes updated  {"count": 1093}
2025/01/29 13:50:49.918 INFO    WAF middleware version  {"version": "v0.0.0-20250128221917-c99e875aaf7c"}
2025/01/29 13:50:49.918 INFO    Rate limit configuration        {"requests": 100, "window": 10, "cleanup_interval": 300, "paths": ["/api/v1/.*", "/admin/.*"], "match_all_paths": false}
2025/01/29 13:50:49.918 WARN    GeoIP database not found. Country blocking/whitelisting will be disabled        {"path": "GeoLite2-Country.mmdb"}
2025/01/29 13:50:50.359 INFO    IP blacklist loaded     {"path": "ip_blacklist.txt", "valid_entries": 223770, "invalid_entries": 0, "total_lines": 223770}
2025/01/29 13:50:50.489 INFO    DNS blacklist loaded    {"path": "dns_blacklist.txt", "valid_entries": 854479, "total_lines": 854479}
2025/01/29 13:50:50.490 INFO    WAF rules loaded successfully   {"total_rules": 33, "rule_counts": "Phase 1: 17 rules, Phase 2: 16 rules, Phase 3: 0 rules, Phase 4: 0 rules, "}
2025/01/29 13:50:50.490 INFO    WAF middleware provisioned successfully

📑 Table of Contents

  1. 🚀 Installation
  2. 🛠️ Basic Configuration
  3. 📚 Full Documentation
  4. 📜 License
  5. 🙏 Contributing

🚀 Installation

# Step 1: Clone the caddy-waf repository from GitHub
git clone https://github.com/4831c0/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/4831c0/caddy-waf
go mod tidy

# Step 5: Download the GeoLite2 Country database (required for country blocking/whitelisting)
wget https://git.io/GeoLite2-Country.mmdb

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

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

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

🛠️ Basic Configuration

Here's a minimal Caddyfile example to get started:

{
    auto_https off
    admin localhost:2019
}

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

    handle {
        header -Server
    }

    route {
        # WAF Plugin runs on all requests first
        waf {
            metrics_endpoint /waf_metrics
            rule_file rules.json
            ip_blacklist_file ip_blacklist.txt
            dns_blacklist_file dns_blacklist.txt
        }

        # Match the waf metrics endpoint specifically and stop processing
        @wafmetrics path /waf_metrics
        handle @wafmetrics {
            # Do not respond here so it goes to the WAF plugin
        }

        # All other requests, respond with "Hello World"
        handle {
            respond "Hello world!" 200
        }
    }
}

For more detailed configuration options, rules format, and usage instructions, please refer to the Full Documentation.


📚 Full Documentation

For complete documentation, including configuration options, rule format details, protected attack types, testing strategies, and more, please refer to the /docs directory in this repository.

📑 Table of Contents
  1. Installation - Instructions for installing the Caddy WAF middleware.
  2. Configuration Options - Detailed explanation of all available configuration settings.
  3. Rules Format (rules.json) - A comprehensive guide to defining custom rules using the JSON format.
  4. Blacklist Formats - Documentation of the formats used for defining IP and DNS blacklists.
  5. Rate Limiting - How to configure rate limiting, including parameters and usage.
  6. Country Blocking and Whitelisting - Details on how to configure country-based blocking and whitelisting.
  7. Protected Attack Types - An overview of the wide range of web-based threats that the Caddy WAF is designed to protect against.
  8. Dynamic Updates - How to dynamically update the WAF rules and other settings without downtime.
  9. Metrics - Details about the WAF's metrics endpoint and the different metrics collected.
  10. Prometheus Metrics - Instructions on how to expose WAF metrics using the Prometheus format.
  11. ELK Observability - Instructions on how to configure caddy-waf ELK stack observability.
  12. Rule/Blacklist Population Scripts - Documentation on the provided scripts to automatically fetch, update and generate rules and blacklists.
  13. Testing - Guidance on how to test the WAF's effectiveness using the provided testing tools.
  14. Docker Support - Instructions on how to build and run the WAF using Docker.

📜 License

This project is licensed under the AGPLv3 License.


Others projects

If You like my projects, you may also like these ones:

  • patterns Automated OWASP CRS and Bad Bot Detection for Nginx, Apache, Traefik and HaProxy
  • blacklists Hourly updated domains blacklist 🚫
  • proxmox-vm-autoscale Automatically scale virtual machines resources on Proxmox hosts
  • UglyFeed Retrieve, aggregate, filter, evaluate, rewrite and serve RSS feeds using Large Language Models for fun, research and learning purposes
  • proxmox-lxc-autoscale Automatically scale LXC containers resources on Proxmox hosts
  • DevGPT Code togheter, right now! GPT powered code assistant to build project in minutes
  • websites-monitor Websites monitoring via GitHub Actions (expiration, security, performances, privacy, SEO)
  • caddy-mib Track and ban client IPs generating repetitive errors on Caddy
  • zonecontrol Cloudflare Zones Settings Automation using GitHub Actions
  • lws linux (containers) web services
  • cf-box cf-box is a set of Python tools to play with API and multiple Cloudflare accounts.
  • limits Automated rate limits implementation for web servers
  • dnscontrol-actions Automate DNS updates and rollbacks across multiple providers using DNSControl and GitHub Actions
  • proxmox-lxc-autoscale-ml Automatically scale the LXC containers resources on Proxmox hosts with AI
  • csv-anonymizer CSV fuzzer/anonymizer
  • iamnotacoder AI code generation and improvement

🙏 Contributing

Contributions are highly welcome! Feel free to open an issue or submit a pull request.

Documentation

Overview

Package caddywaf implements a Web Application Firewall (WAF) middleware for Caddy.

This package provides comprehensive security features including:

  • Regex-based filtering for URLs, data, and headers
  • IP and DNS blacklisting capabilities
  • Geographic access control
  • Rate limiting
  • Anomaly detection and scoring
  • Multi-phase request inspection
  • Real-time metrics and monitoring

The WAF integrates seamlessly with Caddy as an HTTP handler middleware and can be configured via Caddyfile or JSON configuration.

Module ID: http.handlers.waf

Package caddywaf provides Web Application Firewall (WAF) functionality as a Caddy module.

Module ID: http.handlers.waf Module type: HTTP handler middleware

This module implements comprehensive web security features including:

  • Regex-based request filtering
  • IP and DNS blacklisting
  • Geographic access control
  • Rate limiting with configurable windows
  • Anomaly detection and scoring
  • Multi-phase request inspection
  • Real-time metrics and monitoring
  • Custom response handling
  • Dynamic configuration reloading

Installation:

xcaddy build --with github.com/4831c0/caddy-waf

Basic usage in Caddyfile:

waf {
    rule_file rules.json
    ip_blacklist_file blacklist.txt
    metrics_endpoint /waf_metrics
}

For complete documentation, see: https://github.com/4831c0/caddy-waf

rules.go

Index

Constants

View Source
const (
	TargetMethod                = "METHOD"
	TargetRemoteIP              = "REMOTE_IP"
	TargetProtocol              = "PROTOCOL"
	TargetHost                  = "HOST"
	TargetArgs                  = "ARGS"
	TargetUserAgent             = "USER_AGENT"
	TargetPath                  = "PATH"
	TargetURI                   = "URI"
	TargetBody                  = "BODY"
	TargetHeaders               = "HEADERS"          // Full request headers
	TargetResponseHeaders       = "RESPONSE_HEADERS" // Full response headers
	TargetResponseBody          = "RESPONSE_BODY"    // Full response body
	TargetFileName              = "FILE_NAME"
	TargetFileMIMEType          = "FILE_MIME_TYPE"
	TargetCookies               = "COOKIES" // All cookies
	TargetURLParamPrefix        = "URL_PARAM:"
	TargetJSONPathPrefix        = "JSON_PATH:"
	TargetContentType           = "CONTENT_TYPE"
	TargetURL                   = "URL"
	TargetCookiesPrefix         = "COOKIES:"          // Dynamic cookie extraction prefix
	TargetHeadersPrefix         = "HEADERS:"          // Dynamic header extraction prefix
	TargetResponseHeadersPrefix = "RESPONSE_HEADERS:" // Dynamic response header extraction prefix
)

Extraction Target Constants - Improved Readability and Maintainability

Variables

This section is empty.

Functions

func AddSensitiveKey

func AddSensitiveKey(key string)

Function to modify sensitiveKeys in thread-safe way

func NewResponseRecorder

func NewResponseRecorder(w http.ResponseWriter) *responseRecorder

NewResponseRecorder creates a new responseRecorder.

func RedactSensitiveData

func RedactSensitiveData(data map[string]interface{}) map[string]interface{}

func RemoveSensitiveKey

func RemoveSensitiveKey(key string)

Types

type BlacklistLoader

type BlacklistLoader struct {
	// contains filtered or unexported fields
}

BlacklistLoader handles loading IP and DNS blacklists from files.

func NewBlacklistLoader

func NewBlacklistLoader(logger *zap.Logger) *BlacklistLoader

NewBlacklistLoader creates a new BlacklistLoader with the provided logger.

func (*BlacklistLoader) LoadDNSBlacklistFromFile

func (bl *BlacklistLoader) LoadDNSBlacklistFromFile(path string, dnsBlacklist map[string]struct{}) error

LoadDNSBlacklistFromFile loads DNS entries from a file into the provided map.

func (*BlacklistLoader) LoadIPBlacklistFromFile

func (bl *BlacklistLoader) LoadIPBlacklistFromFile(path string, ipBlacklist map[string]struct{}) error

LoadIPBlacklistFromFile loads IP addresses from a file into the provided map. LoadIPBlacklistFromFile loads IP addresses from a file into the provided map.

type CIDRTrie

type CIDRTrie struct {
	// contains filtered or unexported fields
}

func NewCIDRTrie

func NewCIDRTrie() *CIDRTrie

func (*CIDRTrie) Contains

func (t *CIDRTrie) Contains(ipStr string) bool

func (*CIDRTrie) Insert

func (t *CIDRTrie) Insert(cidr string) error

type ConfigLoader

type ConfigLoader struct {
	// contains filtered or unexported fields
}

ConfigLoader structure to encapsulate loading and parsing logic

func NewConfigLoader

func NewConfigLoader(logger *zap.Logger) *ConfigLoader

func (*ConfigLoader) UnmarshalCaddyfile

func (cl *ConfigLoader) UnmarshalCaddyfile(d *caddyfile.Dispenser, m *Middleware) error

UnmarshalCaddyfile is the primary parsing function for the middleware configuration.

type ContextKeyLogId

type ContextKeyLogId string

type ContextKeyRule

type ContextKeyRule string

type CountryAccessFilter

type CountryAccessFilter struct {
	Enabled     bool     `json:"enabled"`
	CountryList []string `json:"country_list"`
	GeoIPDBPath string   `json:"geoip_db_path"`
	// contains filtered or unexported fields
}

CountryAccessFilter struct

type CustomBlockResponse

type CustomBlockResponse struct {
	StatusCode int
	Headers    map[string]string
	Body       string
}

CustomBlockResponse struct

type GeoIPHandler

type GeoIPHandler struct {
	// contains filtered or unexported fields
}

GeoIPHandler struct

func NewGeoIPHandler

func NewGeoIPHandler(logger *zap.Logger) *GeoIPHandler

NewGeoIPHandler creates a new GeoIPHandler with a given logger

func (*GeoIPHandler) GetCountryCode

func (gh *GeoIPHandler) GetCountryCode(remoteAddr string, geoIP *maxminddb.Reader) string

getCountryCode extracts the country code for logging purposes

func (*GeoIPHandler) IsCountryInList

func (gh *GeoIPHandler) IsCountryInList(remoteAddr string, countryList []string, geoIP *maxminddb.Reader) (bool, error)

IsCountryInList checks if an IP belongs to a list of countries

func (*GeoIPHandler) LoadGeoIPDatabase

func (gh *GeoIPHandler) LoadGeoIPDatabase(path string) (*maxminddb.Reader, error)

LoadGeoIPDatabase opens the geoip database

func (*GeoIPHandler) WithGeoIPCache

func (gh *GeoIPHandler) WithGeoIPCache(ttl time.Duration)

WithGeoIPCache enables GeoIP lookup caching.

func (*GeoIPHandler) WithGeoIPLookupFallbackBehavior

func (gh *GeoIPHandler) WithGeoIPLookupFallbackBehavior(behavior string)

WithGeoIPLookupFallbackBehavior configures the fallback behavior for GeoIP lookups.

type GeoIPRecord

type GeoIPRecord struct {
	Country struct {
		ISOCode string `maxminddb:"iso_code"`
	} `maxminddb:"country"`
}

GeoIPRecord struct

type HitCount

type HitCount int

type LogEntry

type LogEntry struct {
	Level   zapcore.Level
	Message string
	Fields  []zap.Field
}

LogEntry represents a single log entry to be processed asynchronously.

type Middleware

type Middleware struct {
	RuleFiles        []string            `json:"rule_files"`
	IPBlacklistFile  string              `json:"ip_blacklist_file"`
	DNSBlacklistFile string              `json:"dns_blacklist_file"`
	AnomalyThreshold int                 `json:"anomaly_threshold"`
	CountryBlock     CountryAccessFilter `json:"country_block"`
	CountryWhitelist CountryAccessFilter `json:"country_whitelist"`
	Rules            map[int][]Rule      `json:"-"`

	LogSeverity string `json:"log_severity,omitempty"`
	LogJSON     bool   `json:"log_json,omitempty"`

	CustomResponses     map[int]CustomBlockResponse `json:"custom_responses,omitempty"`
	LogFilePath         string
	LogBuffer           int  `json:"log_buffer,omitempty"` // Add the LogBuffer field
	RedactSensitiveData bool `json:"redact_sensitive_data,omitempty"`

	MetricsEndpoint string `json:"metrics_endpoint,omitempty"`

	RateLimit RateLimit

	Tor TorConfig `json:"tor,omitempty"`

	IPBlacklistBlockCount int64 `json:"ip_blacklist_hits"`

	DNSBlacklistBlockCount int64 `json:"dns_blacklist_hits"`
	// contains filtered or unexported fields
}

Middleware is the main WAF middleware struct that implements Caddy's Module, Provisioner, Validator, and MiddlewareHandler interfaces.

It provides comprehensive web application firewall functionality including:

  • Rule-based request filtering
  • IP and DNS blacklisting
  • Geographic access control
  • Rate limiting
  • Anomaly detection
  • Custom response handling
  • Real-time metrics and monitoring

The middleware can be configured via Caddyfile or JSON and integrates seamlessly into Caddy's request processing pipeline.

func (*Middleware) CaddyModule

func (*Middleware) CaddyModule() caddy.ModuleInfo

func (*Middleware) DebugRequest

func (m *Middleware) DebugRequest(r *http.Request, state *WAFState, msg string)

DebugRequest logs detailed information about a request for debugging

func (*Middleware) DumpRulesToFile

func (m *Middleware) DumpRulesToFile(path string) error

DumpRulesToFile dumps the loaded rules to a file for inspection

func (*Middleware) Provision

func (m *Middleware) Provision(ctx caddy.Context) error

func (*Middleware) ReloadConfig

func (m *Middleware) ReloadConfig() error

func (*Middleware) ReloadRules

func (m *Middleware) ReloadRules() error

func (*Middleware) ServeHTTP

func (m *Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error

ServeHTTP implements caddyhttp.Handler. handler.go

func (*Middleware) Shutdown

func (m *Middleware) Shutdown(ctx context.Context) error

func (*Middleware) StartLogWorker

func (m *Middleware) StartLogWorker()

StartLogWorker initializes the background logging worker.

func (*Middleware) StopLogWorker

func (m *Middleware) StopLogWorker()

StopLogWorker stops the background logging worker.

func (*Middleware) UnmarshalCaddyfile

func (m *Middleware) UnmarshalCaddyfile(d *caddyfile.Dispenser) error

func (*Middleware) Validate

func (m *Middleware) Validate() error

Validate implements caddy.Validator.

type RateLimit

type RateLimit struct {
	Requests        int              `json:"requests"`
	Window          time.Duration    `json:"window"`
	CleanupInterval time.Duration    `json:"cleanup_interval"`
	Paths           []string         `json:"paths,omitempty"` // Optional paths to apply rate limit
	PathRegexes     []*regexp.Regexp `json:"-"`               // Compiled regexes for the given paths
	MatchAllPaths   bool             `json:"match_all_paths,omitempty"`
}

RateLimit struct

type RateLimiter

type RateLimiter struct {
	sync.RWMutex
	// contains filtered or unexported fields
}

RateLimiter struct

func NewRateLimiter

func NewRateLimiter(config RateLimit) (*RateLimiter, error)

NewRateLimiter creates a new RateLimiter instance.

func (*RateLimiter) GetBlockedRequests

func (rl *RateLimiter) GetBlockedRequests() int64

GetBlockedRequests returns the total number of requests blocked by this rate limiter.

func (*RateLimiter) GetTotalRequests

func (rl *RateLimiter) GetTotalRequests() int64

GetTotalRequests returns the total number of requests received by this rate limiter.

type RequestValueExtractor

type RequestValueExtractor struct {
	// contains filtered or unexported fields
}

RequestValueExtractor struct

func NewRequestValueExtractor

func NewRequestValueExtractor(logger *zap.Logger, redactSensitiveData bool) *RequestValueExtractor

NewRequestValueExtractor creates a new RequestValueExtractor with a given logger

func (*RequestValueExtractor) ExtractValue

func (rve *RequestValueExtractor) ExtractValue(target string, r *http.Request, w http.ResponseWriter) (string, error)

ExtractValue extracts values based on the target, handling comma separated targets

type Rule

type Rule struct {
	ID          string   `json:"id"`
	Phase       int      `json:"phase"`
	Pattern     string   `json:"pattern"`
	Targets     []string `json:"targets"`
	Severity    string   `json:"severity"` // Used for logging only
	Score       int      `json:"score"`
	Action      string   `json:"mode"` // CRITICAL FIX: This should map to the "mode" field in JSON
	Description string   `json:"description"`

	Priority int // New field for rule priority
	// contains filtered or unexported fields
}

Rule struct

type RuleCache

type RuleCache struct {
	// contains filtered or unexported fields
}

RuleCache caches compiled regex patterns for rules.

func NewRuleCache

func NewRuleCache() *RuleCache

NewRuleCache creates a new RuleCache.

func (*RuleCache) Get

func (rc *RuleCache) Get(ruleID string) (*regexp.Regexp, bool)

Get retrieves a compiled regex pattern from the cache.

func (*RuleCache) Set

func (rc *RuleCache) Set(ruleID string, regex *regexp.Regexp)

Set stores a compiled regex pattern in the cache.

type RuleID

type RuleID string

Define custom types for rule hits

type TorConfig

type TorConfig struct {
	Enabled            bool   `json:"enabled,omitempty"`
	TORIPBlacklistFile string `json:"tor_ip_blacklist_file,omitempty"`
	UpdateInterval     string `json:"update_interval,omitempty"`
	RetryOnFailure     bool   `json:"retry_on_failure,omitempty"` // Enable/disable retries
	RetryInterval      string `json:"retry_interval,omitempty"`   // Retry interval (e.g., "5m")
	// contains filtered or unexported fields
}

func (*TorConfig) Provision

func (t *TorConfig) Provision(ctx caddy.Context) error

Provision sets up the Tor blocking configuration.

type TrieNode

type TrieNode struct {
	// contains filtered or unexported fields
}

==================== Struct Definitions ====================

func NewTrieNode

func NewTrieNode() *TrieNode

type WAFState

type WAFState struct {
	TotalScore      int
	Blocked         bool
	StatusCode      int
	ResponseWritten bool
}

WAFState struct

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL