guard
Tail a log source and auto-block IPs that exceed thresholds. Normal runs modify the firewall — every run can add iptables/nftables/DOCKER-USER rules. Use --dry-run to execute detection without firewall or state-file changes.
Requires root
Normal guard runs need sudo; --dry-run does not. Test with --never-block YOUR_IP or run blocklist refresh first.Synopsis
caddy-analyze guard [source] [flags]
# source: file, docker://name, k8s://pod -n ns, journalctl://unit, or via caddy-analyzer.json
# see Log Sources for syntax — this page covers guard flags only
Flags
| Flag | Short | Default | Description |
|---|---|---|---|
--limit | -l | 100 | Max HTTP requests per IP in window before block |
--window | -w | 1m | Sliding window (per-second buckets) |
--duration | -d | 10m | Ban duration (0 = permanent) |
--auth-limit | 10 | 401/403 before block | |
--notfound-limit | 50 | 404 before block | |
--detect-confidence | 8 | Min confidence 1-10 for pattern-based blocks (0 disables only this block path; it is not a dry run) | |
--firewall-backend | auto | auto|iptables|docker|nftables|hybrid | |
--dry-run | false | Report would-be blocks and emit would_block audit events without changing firewall or state | |
--iptables-timeout | 10s | Per-invocation iptables timeout | |
--country-block | ISO codes to block immediately (e.g. CN,RU) | ||
--no-blocklist | false | Disable 8 blocklist feeds | |
--blocklist-refresh | 6h | Refresh interval (min 1h, 0 off) | |
--cache-dir | ~/.cache/…/blocklists | Blocklist cache dir | |
--geoip-db | GeoIP mmdb path (auto-discovery) | ||
--no-auto-download | false | Disable GeoIP auto-download | |
--never-block | CIDRs/IPs never block (protect your IP!) | ||
--never-block-file | File with CIDRs (one per line, # comment) | ||
--audit-log | /var/log/…-audit.jsonl | JSON lines audit (0600) | |
--audit-syslog | Forward audit events to a syslog UDP address | ||
--webhook-url / --audit-webhook | Async webhook notification; provider: generic, slack, discord, pagerduty | ||
--pagerduty-routing-key | PagerDuty routing key (required for PagerDuty, never logged) | ||
--audit-timeout / --audit-retries | 5s / 2 | Per-delivery timeout and bounded retries | |
--audit-rate-limit | 0 | Minimum notification interval per IP | |
--state-file | /var/lib/…/blocked.json | Persist blocks across restarts | |
--subnet-limit | 0 | /24 or /64 distributed scan threshold | |
--rps-anomaly | 0 | EWMA RPS spike factor (audit alert only; requires --audit-log) | |
--cred-stuffing-limit | 0 | Distinct IPs failing same path (audit alert only; requires --audit-log) | |
--ua-rotation | 10 | UA rotation threshold | |
--trust-forwarded | false | Use last public XFF hop |
Minimal example
modifies firewall — blocks real IPs. Allowlist your IP first if on a remote host.
# allowlist yourself, then run guard on auto-discovered source
MY_IP=$(curl -s ifconfig.me)
sudo caddy-analyze guard --never-block $MY_IP --limit 50 --window 1m /var/log/caddy/access.log
Advanced example
modifies firewall — hybrid backend + country + persistent state. To safely preview the same logic, add --dry-run; the terminal shows would-be blocks and the audit sink receives would_block events.
# hybrid = INPUT + DOCKER-USER; blocks survive restart; full audit
sudo caddy-analyze guard docker://caddy \
--firewall-backend hybrid \
--country-block CN,RU,IR \
--never-block 10.0.0.0/8,192.168.1.50 \
--never-block-file /etc/caddy-analyzer/whitelist.txt \
--audit-log /var/log/caddy-analyzer-audit.jsonl \
--state-file /var/lib/caddy-analyzer/blocked.json \
--blocklist-refresh 6h \
--detect-confidence 8
# verify what was blocked:
sudo caddy-analyze unban --list
cat /var/log/caddy-analyzer-audit.jsonl | tail
Do not lock yourself out
On a remote VM, always pass --never-block $(curl -s ifconfig.me) or your office CIDR. Use --dry-run to preview blocks; it reads existing state but does not restore rules or write changes.