IP and pattern-based blocking agent for Zentinel reverse proxy.
- Block requests by client IP address or CIDR range
- Allow-list IPs and CIDR ranges that take precedence over deny rules (enables default-deny setups)
- Block requests by URL path prefix
- Block requests by User-Agent pattern
- Real-time blocking with no restart required
- Configurable via CLI or config file
# Install just this agent
zentinel bundle install denylist
# Or install all bundled agents
zentinel bundle installThe bundle command downloads the correct binary for your platform and places it in the standard location. See the bundle documentation for details.
zentinel-agent-denylist is not published on crates.io, so cargo install zentinel-agent-denylist does not
work. Install straight from the repository instead:
cargo install --git https://github.com/zentinelproxy/zentinel-agent-denylistThis builds and installs the zentinel-denylist-agent binary.
Each release ships binaries
for linux-x86_64, linux-aarch64, and darwin-aarch64:
VERSION=0.3.0
PLATFORM=linux-x86_64 # or linux-aarch64, darwin-aarch64
curl -fsSL -o zentinel-denylist-agent.tar.gz \
"https://github.com/zentinelproxy/zentinel-agent-denylist/releases/download/v${VERSION}/zentinel-denylist-agent-${VERSION}-${PLATFORM}.tar.gz"
tar -xzf zentinel-denylist-agent.tar.gz
sudo install -m 0755 zentinel-denylist-agent /usr/local/bin/git clone https://github.com/zentinelproxy/zentinel-agent-denylist
cd zentinel-agent-denylist
cargo build --releasezentinel-denylist-agent --socket /var/run/zentinel/denylist.sock \
--block-ips "192.168.1.100,10.0.0.1" \
--block-paths "/admin,/wp-admin" \
--block-user-agents "bot,scanner"| Option | Environment Variable | Description | Default |
|---|---|---|---|
--socket |
AGENT_SOCKET |
Unix socket path | /tmp/zentinel-denylist.sock |
--block-ips |
- | Comma-separated IPs or CIDR ranges to block | - |
--allow-ips |
- | Comma-separated IPs or CIDR ranges to allow (take precedence over --block-ips) |
- |
--block-paths |
- | Comma-separated path prefixes to block | - |
--block-user-agents |
- | Comma-separated User-Agent patterns to block | - |
--verbose |
RUST_LOG |
Enable verbose logging | false |
- Entries may be plain addresses (
192.168.1.100,2001:db8::1) or CIDR ranges (192.168.0.0/24,2001:db8::/32). - Allow entries are checked first: an IP matching any
--allow-ipsentry is never blocked by an IP rule, even if it also matches a--block-ipsentry. - IPv4 and IPv6 are matched independently —
0.0.0.0/0covers all of IPv4 only; add::/0to also cover IPv6. - Path and User-Agent deny rules are evaluated separately and are not bypassed by
--allow-ips.
Add to your Zentinel config.kdl:
agents {
agent "denylist" {
type "custom"
transport "unix_socket" {
path "/var/run/zentinel/denylist.sock"
}
events "request_headers"
timeout-ms 10
failure-mode "open"
}
}
routes {
route "all" {
matches { path-prefix "/" }
upstream "backend"
agents "denylist"
}
}When a request is blocked, the agent returns:
- HTTP 403 Forbidden
- Body with the block reason
zentinel-denylist-agent \
--block-ips "1.2.3.4,5.6.7.8,192.168.0.0/24"Because allow entries take precedence over deny entries, a default-deny setup is a matter of blocking both address families entirely and allowing only the IPs or ranges that should get through:
zentinel-denylist-agent \
--block-ips "0.0.0.0/0,::/0" \
--allow-ips "203.0.113.10,198.51.100.0/24"The same works through dynamic configuration pushed by the proxy (on_configure), using the keys block-ips and allow-ips:
{
"block-ips": ["0.0.0.0/0", "::/0"],
"allow-ips": ["203.0.113.10", "198.51.100.0/24"]
}zentinel-denylist-agent \
--block-paths "/admin,/wp-admin,/.env,/.git"zentinel-denylist-agent \
--block-user-agents "sqlmap,nikto,nessus,acunetix"# Run with debug logging
RUST_LOG=debug cargo run -- --socket /tmp/test.sock --block-ips "127.0.0.1"
# Run tests
cargo testApache-2.0