mirror of
https://github.com/fabriziosalmi/caddy-waf.git
synced 2026-07-30 06:15:57 -04:00
responseRecorder accumulated the entire upstream response body in an in-memory bytes.Buffer before releasing a single byte to the client, with no ceiling. A single unauthenticated request for a large or streaming resource made the Caddy process's heap grow in step with the response size, so an attacker could OOM-kill the process and take down every site served by that instance (CWE-400, CVSS 3.1 7.5). The response body is now buffered only when a Phase 4 rule can use it, and only up to max_response_body_size (new, default 10 MiB); past the budget, or on an upstream flush, the recorder releases what it holds and streams the rest. A released response is not evaluated by Phase 4 and is logged at warn instead. Measured on a 512 MiB response: heap allocated during ServeHTTP drops from 1535 MiB to 0 MiB, with all 512 MiB still delivered. Also fixes a Phase 4 block swallowing the custom_response body, and makes max_request_body_size settable from the Caddyfile. Reported by @EQSTLab. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
99 lines
3.5 KiB
Plaintext
99 lines
3.5 KiB
Plaintext
# Example Caddyfile for the caddy-waf module (http.handlers.waf).
|
|
# Every directive below is recognised by the Caddyfile parser in config.go.
|
|
|
|
{
|
|
auto_https off
|
|
admin localhost:2019
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Example 1: minimal setup
|
|
# ---------------------------------------------------------------------------
|
|
example.com {
|
|
route {
|
|
waf {
|
|
rule_file rules.json
|
|
ip_blacklist_file ip_blacklist.txt
|
|
dns_blacklist_file dns_blacklist.txt
|
|
metrics_endpoint /waf_metrics
|
|
|
|
anomaly_threshold 10
|
|
|
|
log_severity info
|
|
log_path /var/log/caddy/waf.log
|
|
}
|
|
|
|
respond "Hello, World — protected by Caddy WAF" 200
|
|
}
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Example 2: API gateway with rate limiting, GeoIP, ASN block, and
|
|
# custom responses
|
|
# ---------------------------------------------------------------------------
|
|
api.example.com {
|
|
route {
|
|
waf {
|
|
metrics_endpoint /waf_metrics
|
|
anomaly_threshold 15
|
|
|
|
# One or more rule files (the directive may repeat).
|
|
rule_file rules/sql-injection.json
|
|
rule_file rules/xss.json
|
|
rule_file rules/lfi.json
|
|
|
|
# Per-IP rate limiting on /api/* and /admin/*.
|
|
rate_limit {
|
|
requests 100
|
|
window 10s
|
|
cleanup_interval 5m
|
|
paths ^/api/.* ^/admin/.*
|
|
match_all_paths false
|
|
}
|
|
|
|
# Country and ASN gating (require GeoLite2 MMDB files).
|
|
block_countries GeoLite2-Country.mmdb RU CN KP
|
|
block_asns GeoLite2-ASN.mmdb 14618 16509
|
|
|
|
# Tor exit-node blocking; refreshed every 24h, retries every hour
|
|
# on transient failures.
|
|
tor {
|
|
enabled true
|
|
tor_ip_blacklist_file tor_blacklist.txt
|
|
update_interval 24h
|
|
retry_on_failure true
|
|
retry_interval 1h
|
|
}
|
|
|
|
# IP and DNS blacklists.
|
|
ip_blacklist_file ip_blacklist.txt
|
|
dns_blacklist_file dns_blacklist.txt
|
|
|
|
# Custom responses for blocked requests.
|
|
# form 1: status content-type "inline body…"
|
|
# form 2: status content-type /path/to/body.file
|
|
custom_response 403 application/json error.json
|
|
custom_response 429 text/plain "Slow down."
|
|
|
|
# Body size ceilings, in bytes.
|
|
# max_request_body_size caps request body reads (io.LimitReader).
|
|
# max_response_body_size caps how much of the response body is held
|
|
# in memory for Phase 4 inspection; past
|
|
# this the response is streamed through
|
|
# un-inspected instead of buffered.
|
|
# Both default to 10 MiB (10485760).
|
|
max_request_body_size 10485760
|
|
max_response_body_size 10485760
|
|
|
|
# Logging.
|
|
log_severity info
|
|
log_json
|
|
log_path /var/log/caddy/waf.log
|
|
log_buffer 2000
|
|
redact_sensitive_data
|
|
}
|
|
|
|
reverse_proxy localhost:8080
|
|
}
|
|
}
|