IP Filtering
Regius includes an IP whitelist/blacklist middleware that allows or denies requests based on the client IP, using static lists and optional runtime (cache/DB-backed) decisions for fail2ban-style blocking.
Features
- Opt-in via
IP_FILTER_ENABLED: Applied globally (after RealIP) so denied requests short-circuit before heavier middleware runs - Allow/deny lists of IPs or CIDR ranges: e.g.
10.0.0.0/8,192.168.1.5,::1/128; bare IPs are treated as/32(IPv4) or/128(IPv6). IPv4 and IPv6 both supported - Deny-wins semantics: A matching
Denyentry always blocks; whenAllowis non-empty, any IP not inAllowis blocked. With neither list set, all IPs are allowed - Invalid entries are logged and skipped (non-fatal) — one bad CIDR won't take down the filter
- TrustProxy (default off): Reads the client IP from
X-Forwarded-For(first entry) orX-Real-IPinstead ofRemoteAddr. Only enable behind a trusted reverse proxy, otherwise the header can be spoofed to bypass the filter - Optional pluggable
IPCheckerinterface for dynamic, DB/cache-backed decisions:DecisionAllow/DecisionDenyoverride the static lists,DecisionNonedefers to them. Checker errors fail open (the static baseline still applies) - CacheIPChecker: Adapts the framework cache (Redis/Badger) for runtime block/unblock without restart; entries namespaced under
ipfilter: - Configurable block response: Blocked requests respond with a configurable status (default 403) + a JSON body and
Cache-Control: no-store
Usage
IP filtering is applied globally when IP_FILTER_ENABLED=true. No additional code is required.
Or build the middleware manually for a route group (e.g. restrict admin):
r.Group(func(mux chi.Router) {
mux.Use(a.IPFilter(regius.IPFilterConfig{
Enabled: true,
Allow: []string{"10.0.0.0/8", "192.168.1.0/24"},
Deny: []string{"10.0.0.99"},
}))
// admin routes here...
})
Runtime (fail2ban-style) blocking via a cache-backed checker:
checker := regius.NewCacheIPChecker(a.Cache, "ipfilter:")
_ = checker.Block("203.0.113.50", 3600) // block for 1 hour
_ = checker.Unblock("203.0.113.50") // unblock later
mux.Use(a.IPFilter(regius.IPFilterConfig{
Enabled: true,
Deny: []string{"198.51.100.0/24"}, // static baseline
Checker: checker, // dynamic layer on top
}))
Configuration Options
config := regius.IPFilterConfig{
Enabled: true, // Master toggle
Allow: []string{"10.0.0.0/8"}, // Only these networks pass (deny-wins)
Deny: []string{"10.0.0.99"}, // Always blocked
TrustProxy: false, // Read X-Forwarded-For/X-Real-IP (default false)
StatusCode: 403, // Block response status (default 403)
Message: "ip address not allowed", // Block response message
Checker: nil, // Optional IPChecker (e.g. CacheIPChecker)
}
Cache-Backed Checker
TTL in seconds; 0 = no expiry:
checker := regius.NewCacheIPChecker(a.Cache, "ipfilter:")
_ = checker.Block(ip, 3600) // DecisionDeny
_ = checker.Allow(ip, 0) // DecisionAllow
_ = checker.Unblock(ip) // remove decision -> defer to static lists
Environment Variables
IP_FILTER_ENABLED=false
IP_FILTER_ALLOW= # comma-separated IPs/CIDRs to permit
IP_FILTER_DENY= # comma-separated IPs/CIDRs to block (deny-wins)
IP_FILTER_TRUST_PROXY=false # read X-Forwarded-For/X-Real-IP
IP_FILTER_STATUS_CODE=403
IP_FILTER_MESSAGE=