No description
  • Go 48.5%
  • Python 42.2%
  • HTML 6.4%
  • Shell 2.5%
  • Dockerfile 0.3%
Find a file
Carlos Latorre c28ad3c4f4
Some checks failed
Release Caddy Middleware / build-and-release (amd64, darwin) (push) Has been cancelled
Release Caddy Middleware / build-and-release (amd64, linux) (push) Has been cancelled
Release Caddy Middleware / build-and-release (amd64, windows) (push) Has been cancelled
Release Caddy Middleware / build-and-release (arm64, darwin) (push) Has been cancelled
Release Caddy Middleware / build-and-release (arm64, linux) (push) Has been cancelled
Release Caddy Middleware / create-release (push) Has been cancelled
fix: stream Server-Sent Events instead of buffering them
The responseRecorder buffered the entire response body so the WAF could
inspect it in phase 4, only flushing to the client in copyResponse after the
upstream closed. For a text/event-stream response the upstream never closes,
so the browser received no events and features backed by EventStream stalled
(e.g. Navidrome showing server uptime as unknown).

Detect text/event-stream in WriteHeader and switch the recorder to
pass-through mode: writes go straight to the client and are flushed
immediately, so events arrive in real time. Normal responses are still
buffered and inspected. Streamed responses skip phase-4 body inspection,
which is unavoidable for an unbounded stream.
2026-06-12 20:20:15 +02:00
.github build: upgrade to Go 1.25 and Caddy v2.10.2 (security fix) 2025-12-06 23:15:40 +01:00
docs docs: rewrite README and docs for 1:1 accuracy with codebase, bump to v0.3.1 2026-04-26 23:38:39 +02:00
rules Minor improvements. 2025-01-24 18:31:54 +01:00
ui ui fix 2025-02-03 00:39:15 +01:00
.dockerignore Create .dockerignore 2025-01-03 19:48:13 +01:00
.fossa.yml ci: add FOSSA configuration file 2026-01-17 19:53:11 +01:00
.gitignore chore: update .gitignore with go, python and ide patterns 2026-04-27 09:38:54 +02:00
.golangci.yml Refactor: Fix absolute path in test.caddyfile and resolve TODO in .golangci.yml 2025-12-06 22:28:18 +01:00
assets.go Security: Implement hardening improvements (LimitReader, GeoIP Fail-Open, UI Decoupling, Go Version) 2025-12-06 22:46:11 +01:00
assets_stub.go Security: Implement hardening improvements (LimitReader, GeoIP Fail-Open, UI Decoupling, Go Version) 2025-12-06 22:46:11 +01:00
benchmark.py ab benchmark initial test script added for performance evaluations 2025-01-22 15:23:37 +01:00
blacklist.go fix: improve robustness and error handling across codebase 2026-01-17 18:59:59 +01:00
blacklist_test.go fix: add trie instantiation on top 2025-10-19 22:32:58 +03:00
CADDY_MODULE_REGISTRATION.md docs: fix installation documentation - caddy add-package not registered (fixes #87) 2026-01-17 18:19:07 +01:00
Caddyfile Bump WAF version to v0.0.5 and ensure proper module registration 2025-04-30 11:38:01 +02:00
caddyfile.example docs: rewrite README and docs for 1:1 accuracy with codebase, bump to v0.3.1 2026-04-26 23:38:39 +02:00
caddytest.py Create caddytest.py 2025-02-02 00:42:18 +01:00
caddywaf.go feat: expose log_id to Caddy replacer for access log correlation, bump to v0.3.3 2026-04-29 10:21:39 +02:00
caddywaf_test.go chore: renamed country block to country blacklisting for consistency 2025-10-12 16:04:26 +03:00
CHANGELOG.md deps: patch 3 critical and 10 high Dependabot alerts, bump to v0.3.2 2026-04-26 23:51:44 +02:00
check_waf_config.py Add debugging tools for WAF configuration and anomaly threshold testing 2025-04-30 11:19:17 +02:00
CODE_OF_CONDUCT.md Create CODE_OF_CONDUCT.md 2025-01-02 19:12:49 +01:00
common_test.go fix: add trie instantiation on top 2025-10-19 22:32:58 +03:00
config.go Feat: Implement ASN Blocking (#73) 2025-12-06 22:18:10 +01:00
config_test.go chore: renamed country block to country blacklisting for consistency 2025-10-12 16:04:26 +03:00
CONTRIBUTING.md Update CONTRIBUTING.md 2025-01-22 23:47:26 +01:00
debug_test_results.py Add debugging tools for WAF configuration and anomaly threshold testing 2025-04-30 11:19:17 +02:00
debug_waf.go refactor: apply SOTA patterns (Atomic HitCount, Zero-Copy Body, Low-Lock RateLimit) 2025-12-06 22:52:01 +01:00
debug_waf.py Add debugging tools for WAF configuration and anomaly threshold testing 2025-04-30 11:19:17 +02:00
dns_blacklist.txt Fix CIDR cannot be inserted into Trie 2025-01-25 00:36:59 +01:00
doc.go docs: Add caddy add-package installation method to documentation 2026-01-06 10:32:44 +00:00
docker-compose.yml fix https://github.com/fabriziosalmi/caddy-waf/issues/56 2025-07-13 18:38:40 +02:00
Dockerfile fix https://github.com/fabriziosalmi/caddy-waf/issues/56 2025-07-13 18:38:40 +02:00
e2e.py e2e test added with specific rules_test.json rules file. 2025-01-26 22:03:32 +01:00
error.json custom response example added on Caddyfile (commented) and in the sample JSON source file error.json. 2025-01-25 19:18:35 +01:00
geoip.go Feat: Implement ASN Blocking (#73) 2025-12-06 22:18:10 +01:00
geoip_test.go fix: add trie instantiation on top 2025-10-19 22:32:58 +03:00
get_blacklisted_dns.py Update get_blacklisted_dns.py 2025-01-07 14:36:41 +01:00
get_blacklisted_ip.py Refactor IP extraction and handling in get_blacklisted_ip.py 2025-10-16 00:42:00 +02:00
get_caddy_feeds.py initial 2025-01-19 00:00:25 +01:00
get_owasp_rules.py Rename owasp2json.py to get_owasp_rules.py 2025-01-05 01:37:51 +01:00
get_spiderlabs_rules.py Create get_spiderlabs_rules.py 2025-01-13 12:46:33 +01:00
get_vulnerability_rules.py Create get_vulnerability_rules.py 2025-01-13 12:46:57 +01:00
GNUmakefile Security: Implement hardening improvements (LimitReader, GeoIP Fail-Open, UI Decoupling, Go Version) 2025-12-06 22:46:11 +01:00
go.mod deps: patch 3 critical and 10 high Dependabot alerts, bump to v0.3.2 2026-04-26 23:51:44 +02:00
go.sum deps: patch 3 critical and 10 high Dependabot alerts, bump to v0.3.2 2026-04-26 23:51:44 +02:00
handler.go fix: stream Server-Sent Events instead of buffering them 2026-06-12 20:20:15 +02:00
handler_test.go refactor: apply SOTA patterns (Atomic HitCount, Zero-Copy Body, Low-Lock RateLimit) 2025-12-06 22:52:01 +01:00
helpers.go fix: resolve duplicate response headers and support CIDR notation in IP blacklist 2026-02-22 20:00:24 +01:00
helpers_test.go fix: use X-Forwarded-For for country/ASN blocking behind proxies 2026-01-05 18:13:01 +00:00
install.sh install script improved, minor rules.go fixes 2025-01-22 14:28:14 +01:00
install_with_modules.sh Add installation script for Caddy with WAF and module support 2025-04-30 11:25:56 +02:00
ip_blacklist.txt Fix CIDR cannot be inserted into Trie 2025-01-25 00:36:59 +01:00
it_test.go fix: lint errors 2025-10-22 23:04:48 +03:00
LICENSE Initial commit 2025-01-02 13:13:56 +01:00
log.json blacklisting tests added and updated 2025-01-26 14:35:48 +01:00
logging.go fix: lint errors 2025-10-22 23:04:48 +03:00
logging_test.go refactor(trie): switched to an ext implementation 2025-10-10 23:50:13 +03:00
MODULE.md docs: rewrite README and docs for 1:1 accuracy with codebase, bump to v0.3.1 2026-04-26 23:38:39 +02:00
ratelimiter.go fix: improve robustness and error handling across codebase 2026-01-17 18:59:59 +01:00
ratelimiter_test.go test: upd ip blacklist test 2025-10-12 13:12:13 +03:00
README.md docs: rewrite README and docs for 1:1 accuracy with codebase, bump to v0.3.1 2026-04-26 23:38:39 +02:00
request.go fix: restore full request body for large payloads (closes #76) 2025-12-08 07:30:22 +01:00
request_body_test.go fix: restore full request body for large payloads (closes #76) 2025-12-08 07:30:22 +01:00
request_test.go Security: Implement hardening improvements (LimitReader, GeoIP Fail-Open, UI Decoupling, Go Version) 2025-12-06 22:46:11 +01:00
response.go fix: stream Server-Sent Events instead of buffering them 2026-06-12 20:20:15 +02:00
response_test.go fix: lint errors 2025-10-22 23:04:48 +03:00
rules-browser-friendly.json Update WAF configuration and add browser-friendly rules 2025-04-30 08:57:22 +02:00
rules.go fix: improve robustness and error handling across codebase 2026-01-17 18:59:59 +01:00
rules.json Update WAF configuration and add browser-friendly rules 2025-04-30 08:57:22 +02:00
rules_test.go Security: Implement hardening improvements (LimitReader, GeoIP Fail-Open, UI Decoupling, Go Version) 2025-12-06 22:46:11 +01:00
rules_test.json e2e test added with specific rules_test.json rules file. 2025-01-26 22:03:32 +01:00
sample_rules.json Fix: Update CI to use test.caddyfile, block Nikto, and use stable GeoIP URL 2025-12-06 22:37:32 +01:00
SECURITY.md docs: release v0.1.4 preparation (changelog, security, readme) 2025-12-06 23:13:17 +01:00
test.caddyfile Refactor: Fix absolute path in test.caddyfile and resolve TODO in .golangci.yml 2025-12-06 22:28:18 +01:00
test.py More test added (550 total). Rules files optimized to working rules only (backward ocmpatible) + browser integrity newer rules. 2025-01-25 22:39:56 +01:00
test_anomalythreshold.py Add debugging tools for WAF configuration and anomaly threshold testing 2025-04-30 11:19:17 +02:00
tor.go fix: improve robustness and error handling across codebase 2026-01-17 18:59:59 +01:00
tor_blacklist.txt action fix 2025-01-15 18:33:59 +01:00
tor_test.go feat: add golangci linter rules 2025-10-11 22:36:20 +03:00
types.go Fix import formatting in caddywaf.go and types.go for gci linter 2026-01-06 10:47:34 +00:00
types_test.go feat!!: switch to go-trie 2025-10-10 23:21:41 +03:00

Caddy WAF

A Web Application Firewall middleware for the Caddy web server, written in Go.

Tests CodeQL Build, Run and Validate

  • Module ID: http.handlers.waf
  • Go module path: github.com/fabriziosalmi/caddy-waf
  • Current version: v0.3.0 (see caddywaf.goconst wafVersion)
  • License: AGPL-3.0

Overview

caddy-waf is an HTTP handler middleware that inspects requests and responses across four well-defined phases, applies a regular-expression rule set with anomaly scoring, enforces IP/DNS/ASN/country blacklists and whitelists, performs token-bucket-style rate limiting, and exposes a JSON metrics endpoint.

The middleware is implemented as a single Caddy module registered under the ID http.handlers.waf. It can be configured through the Caddyfile or directly via JSON.

Capabilities

Capability Implementation
Regex rule engine Go regexp package (RE2, linear-time guarantee). Compiled patterns are cached per rule ID.
Multi-phase inspection Phase 1 (request headers and pre-request checks), Phase 2 (request body), Phase 3 (response headers), Phase 4 (response body).
Anomaly scoring Each rule contributes its score to a per-request total; requests are blocked when the total reaches anomaly_threshold.
Explicit actions Rule mode of block or log. block short-circuits the request; log records the match and continues.
IP blacklist Plain IPs and CIDR ranges (IPv4 and IPv6) stored in a prefix trie (go-iptrie).
DNS blacklist Exact-match (case-insensitive) host lookup.
GeoIP country block / whitelist MaxMind GeoLite2 Country MMDB.
ASN block MaxMind GeoLite2 ASN MMDB.
Tor exit-node block Periodic fetch from https://check.torproject.org/torbulkexitlist.
Rate limiting Per-IP, sliding-window, optional per-path matching with regex patterns.
Custom block responses Per-status-code response with custom Content-Type, headers, and body (inline or from file).
Sensitive data redaction Optional redaction of sensitive query parameters and log fields.
Hot reload fsnotify watchers on rule files, IP blacklist, and DNS blacklist.
Metrics endpoint JSON document exposed at the configured metrics_endpoint path.
Asynchronous logging Buffered log channel with synchronous fallback when the buffer is full.

Engineering notes

  • Linear-time regex: rules are compiled by Go's regexp (RE2). No catastrophic backtracking.
  • Wait-free counters: per-rule hit counts use atomic.Int64 stored in a sync.Map.
  • Bounded body reads: request bodies are read through io.LimitReader (max_request_body_size, default 10 MiB) and restored with io.MultiReader so downstream handlers still see the full body.
  • Zero-copy body string: the body is exposed to rule matching via unsafe.String to avoid an allocation per request.
  • Circuit breaker for GeoIP: geoip_fail_open controls whether a GeoIP lookup failure blocks the request or allows it through.
  • Panic recovery: ServeHTTP installs a deferred recovery that returns 500 Internal Server Error on panic.

Quick start

The fastest path to a working build:

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

The script ensures Go and xcaddy are installed, clones the repository, downloads the GeoLite2 database, builds Caddy with the caddy-waf module, and starts the server.

A representative provisioning log:

INFO  Provisioning WAF middleware     {"log_level":"info","log_path":"debug.json","log_json":true,"anomaly_threshold":20}
INFO  http.handlers.waf  Tor exit nodes updated  {"count":1093}
INFO  WAF middleware version  {"version":"v0.3.0"}
INFO  Rate limit configuration  {"requests":100,"window":10,"cleanup_interval":300,"paths":["/api/v1/.*"],"match_all_paths":false}
WARN  GeoIP database not found. Country blacklisting/whitelisting will be disabled  {"path":"GeoLite2-Country.mmdb"}
INFO  IP blacklist loaded     {"path":"ip_blacklist.txt","valid_entries":223770,"invalid_entries":0,"total_lines":223770}
INFO  DNS blacklist loaded    {"path":"dns_blacklist.txt","valid_entries":854479,"total_lines":854479}
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, "}
INFO  WAF middleware provisioned successfully

Installation

Requirements

  • Go 1.25 or newer (go.mod declares go 1.25)
  • Caddy v2.10.x or newer (current build uses github.com/caddyserver/caddy/v2 v2.10.2)
  • xcaddy for building Caddy with plugins
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
xcaddy build --with github.com/fabriziosalmi/caddy-waf
./caddy list-modules | grep waf   # expect: http.handlers.waf

Option 2 — Quick script

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

Option 3 — Build from source

git clone https://github.com/fabriziosalmi/caddy-waf.git
cd caddy-waf
go mod tidy
wget https://git.io/GeoLite2-Country.mmdb           # optional, only for GeoIP
xcaddy build --with github.com/fabriziosalmi/caddy-waf=./
./caddy fmt --overwrite
./caddy run

caddy add-package

This module is not registered in Caddy's official package registry; caddy add-package github.com/fabriziosalmi/caddy-waf will fail with HTTP 400: ... is not a registered Caddy module package path. Use one of the build options above. See docs/add-package-guide.md for details.


Minimal Caddyfile

{
    auto_https off
    admin localhost:2019
}

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

    route {
        waf {
            metrics_endpoint   /waf_metrics
            rule_file          rules.json
            ip_blacklist_file  ip_blacklist.txt
            dns_blacklist_file dns_blacklist.txt
        }

        @wafmetrics path /waf_metrics
        handle @wafmetrics {
            # Falls through to the WAF handler, which serves metrics as JSON.
        }

        handle {
            respond "Hello world!" 200
        }
    }
}

A fully annotated example is provided in Caddyfile and caddyfile.example.


Documentation

Document Topic
docs/installation.md All installation methods.
docs/configuration.md Caddyfile and JSON directives, request lifecycle, blocking precedence.
docs/rules.md rules.json schema, target identifiers, regex semantics.
docs/blacklists.md IP and DNS blacklist file formats.
docs/ratelimit.md Rate-limit block, path matching, behavior.
docs/geoblocking.md Country block / whitelist, ASN block, fallback behavior.
docs/attacks.md Attack categories addressed by the bundled rule sets.
docs/dynamicupdates.md File watchers, reload semantics, scope of each reload.
docs/metrics.md /waf_metrics JSON schema.
docs/prometheus.md Bridging the JSON metrics to Prometheus and Grafana.
docs/caddy-waf-elk.md Shipping logs to an ELK stack with Filebeat.
docs/scripts.md Helper Python scripts for rule and blacklist generation.
docs/testing.md Running the bundled test.py suite.
docs/docker.md Building and running with Docker / Docker Compose.
docs/add-package-guide.md Status of caddy add-package registration.
docs/caddytest.md The caddytest.py traffic-generation tool.

Project layout

.
├── caddywaf.go            Module registration, lifecycle, file watchers, metrics endpoint
├── handler.go             ServeHTTP, phase dispatch, blocking decisions
├── config.go              Caddyfile directive parsing
├── rules.go               Rule loading, validation, regex caching, hit accounting
├── request.go             Target extraction (URI, headers, body, JSON paths, ...)
├── response.go            Response recorder, block writer
├── blacklist.go           IP and DNS blacklist loaders and lookups
├── ratelimiter.go         Per-IP / per-path sliding-window rate limiter
├── geoip.go               MaxMind country and ASN lookups, with optional cache
├── tor.go                 Periodic Tor exit-node list fetcher
├── logging.go             Async log worker, sensitive-data redaction
├── helpers.go             IP parsing helpers
├── types.go               Public types (Middleware, Rule, RateLimit, ...)
├── doc.go                 Package documentation
├── rules.json             Default rule set (used by Caddyfile)
├── rules/                 Modular rule files by category
├── ip_blacklist.txt       Default IP blacklist
├── dns_blacklist.txt      Default DNS blacklist
├── tor_blacklist.txt      Tor exit-node cache (auto-managed)
├── docs/                  Documentation
└── *_test.go              Unit and integration tests

Testing

make test            # go test -v ./...
make it              # go test -v ./... -tags=it (integration)
make lint            # golangci-lint run
make test-integration # runs test.py inside a python:3.9-slim container

The repository also ships Python suites covering offensive payloads (test.py), traffic generation (caddytest.py), and benchmarking (benchmark.py). See docs/testing.md and docs/caddytest.md.


Contributing

Pull requests are welcome. The project values:

  1. Tests that exercise the change.
  2. Rule contributions placed under rules/ using the documented JSON schema.
  3. Documentation kept in sync with code (every directive change must be reflected in docs/configuration.md and the relevant topic page).

See CONTRIBUTING.md for the workflow and CODE_OF_CONDUCT.md.

Security

For vulnerability disclosure see SECURITY.md. Reports may be sent to fabrizio.salmi@gmail.com or as a private GitHub Security Advisory; please do not open a public issue.

License

AGPL-3.0. See LICENSE.