Caddy module
Add one ipscanner directive to a site block and every visitor is checked before reverse_proxy. It starts in monitor mode and blocks nothing until you switch.
Free, MIT licenseCaddy 2.8+
Access log
Traffic classes
Every visitor gets one class. Block a class from the dashboard, or list it in block_classes.
Setup
Caddy reads it from IPSCANNER_API_KEY, or from api_key in the Caddyfile.
export IPSCANNER_API_KEY=<your API key>With xcaddy or in Docker. Needs Caddy 2.8 or later.
xcaddy build --with github.com/ipscanner/ipscanner-caddy
./caddy list-modules | grep ipscannerThe site holds the policy and shows the traffic. Copy its Site ID.
Add a siteSite ID
site_4fQ8nZ2kLm7xR1vT9cBw
No global order line: it runs before basic_auth and reverse_proxy.
example.com {
ipscanner {
site_id site_4fQ8nZ2kLm7xR1vT9cBw
}
reverse_proxy localhost:8080
}Without a site
Set the mode and blocked classes in the config. A matcher such as ipscanner /app/* limits the check to some paths.
example.com {
ipscanner {
mode enforce
block_classes malicious_automation tor
}
reverse_proxy localhost:8080
}Placeholders
Set by the directive for log_append, vars matchers and headers.
| Placeholder | Values |
|---|---|
{http.vars.ipscanner.status}ok, error, timeout, backoff, skipped | ok, error, timeout, backoff, skipped |
{http.vars.ipscanner.class}Traffic class, set when the status is ok | Traffic class, set when the status is ok |
{http.vars.ipscanner.action}allow, flag, block, would_flag, would_block | allow, flag, block, would_flag, would_block |
log_append ipscanner_class {http.vars.ipscanner.class}
log_append ipscanner_action {http.vars.ipscanner.action}
@flagged vars ipscanner.action flag
request_header @flagged X-Review 1Client IP
The module checks the client IP Caddy resolved. Trust your proxies, and trusted_proxies_strict stops a visitor from adding a fake hop.
{
servers {
trusted_proxies static 10.0.0.0/8
trusted_proxies_strict
}
}Settings
All optional. The JSON config uses the same names.
| Subdirective | Default | What it does |
|---|---|---|
api_keyAPI key. Without one, every request passes. | {env.IPSCANNER_API_KEY} | API key. Without one, every request passes. |
site_idDashboard site that sets mode and policy. | Empty | Dashboard site that sets mode and policy. |
modeMonitor or enforce. With a site, only monitor applies. | monitor | Monitor or enforce. With a site, only monitor applies. |
block_classesBlocked in enforce mode without a site. | malicious_automation | Blocked in enforce mode without a site. |
timeoutBudget for one check. | 1.5s | Budget for one check. |
cache_ttlHow long a verdict is reused per visitor. | 10m | How long a verdict is reused per visitor. |
cache_sizeVerdict cache entries. | 10000 | Verdict cache entries. |
policy_ttlHow long the site policy is cached. | 30s | How long the site policy is cached. |
skip_pathsRegex of paths that are never checked. | Static assets | Regex of paths that are never checked. |
block_messageText of the 403 page. | Blocked by IPScanner edge guard. | Text of the 403 page. |
debugDebug headers and one log line per decision. | Off | Debug headers and one log line per decision. |
api_urlAPI base URL. | https://ipscanner.io | API base URL. |
Always passes
skip_paths.Questions
Build Caddy with the module, read a week of decisions, then block.