v0.7.1 · updated · GitHub

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

FlagShortDefaultDescription
--limit-l100Max HTTP requests per IP in window before block
--window-w1mSliding window (per-second buckets)
--duration-d10mBan duration (0 = permanent)
--auth-limit10401/403 before block
--notfound-limit50404 before block
--detect-confidence8Min confidence 1-10 for pattern-based blocks (0 disables only this block path; it is not a dry run)
--firewall-backendautoauto|iptables|docker|nftables|hybrid
--dry-runfalseReport would-be blocks and emit would_block audit events without changing firewall or state
--iptables-timeout10sPer-invocation iptables timeout
--country-blockISO codes to block immediately (e.g. CN,RU)
--no-blocklistfalseDisable 8 blocklist feeds
--blocklist-refresh6hRefresh interval (min 1h, 0 off)
--cache-dir~/.cache/…/blocklistsBlocklist cache dir
--geoip-dbGeoIP mmdb path (auto-discovery)
--no-auto-downloadfalseDisable GeoIP auto-download
--never-blockCIDRs/IPs never block (protect your IP!)
--never-block-fileFile with CIDRs (one per line, # comment)
--audit-log/var/log/…-audit.jsonlJSON lines audit (0600)
--audit-syslogForward audit events to a syslog UDP address
--webhook-url / --audit-webhookAsync webhook notification; provider: generic, slack, discord, pagerduty
--pagerduty-routing-keyPagerDuty routing key (required for PagerDuty, never logged)
--audit-timeout / --audit-retries5s / 2Per-delivery timeout and bounded retries
--audit-rate-limit0Minimum notification interval per IP
--state-file/var/lib/…/blocked.jsonPersist blocks across restarts
--subnet-limit0/24 or /64 distributed scan threshold
--rps-anomaly0EWMA RPS spike factor (audit alert only; requires --audit-log)
--cred-stuffing-limit0Distinct IPs failing same path (audit alert only; requires --audit-log)
--ua-rotation10UA rotation threshold
--trust-forwardedfalseUse 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.