Denylist

Official Stable

Block requests based on IP addresses, CIDR ranges, or custom patterns with real-time updates.

Version: 0.2.0 Author: Sentinel Core Team License: Apache-2.0 Protocol: vv2 View Source

Quick Install

Cargo
cargo install sentinel-agent-denylist

Protocol v2 Features

As of v0.2.0, the Denylist agent supports protocol v2 with:

  • Capability negotiation: Reports supported features during handshake
  • Health reporting: Exposes health status for monitoring
  • Metrics export: Counter metrics for requests processed and blocked
  • gRPC transport: Optional high-performance gRPC transport via --grpc-address
  • Lifecycle hooks: Graceful shutdown and drain handling

Overview

The Denylist agent provides real-time request blocking based on IP addresses, CIDR ranges, user agents, or custom request attributes. Essential for blocking known malicious actors and implementing access control policies.

Features

  • IP Blocking: Block individual IPs or CIDR ranges
  • User-Agent Blocking: Block known bad bots, scrapers, and scanners
  • Header Matching: Block based on any request header value
  • Path Blocking: Block requests to specific paths or path patterns
  • Query Parameter Blocking: Block requests with specific query parameters
  • Regex Support: Use regular expressions for flexible pattern matching
  • Hot Reload: Update deny rules without restarting
  • Metrics: Track blocked requests per rule
  • Audit Tags: Add custom tags for logging and analytics

Installation

The easiest way to install this agent is via the Sentinel bundle command:

# Install just this agent
sentinel bundle install denylist

# Or install all available agents
sentinel bundle install --all

The bundle command automatically downloads the correct binary for your platform and places it in ~/.sentinel/agents/.

Using Cargo

cargo install sentinel-agent-denylist

Using Docker

docker pull ghcr.io/raskell-io/sentinel-agent-denylist:latest

Docker Compose

services:
  denylist-agent:
    image: ghcr.io/raskell-io/sentinel-agent-denylist:latest
    volumes:
      - /var/run/sentinel:/var/run/sentinel
      - ./denylist.txt:/etc/sentinel/denylist.txt:ro
    environment:
      - SOCKET_PATH=/var/run/sentinel/denylist.sock

Configuration

Add the agent to your Sentinel configuration:

agent "denylist" {
    socket "/var/run/sentinel/denylist.sock"
    timeout 50ms
    fail-open false

    config {
        file "/etc/sentinel/denylist.txt"
        reload-interval 60s
    }
}

Configuration Options

OptionTypeDefaultDescription
filestring-Path to denylist file
reload-intervalduration60sHow often to check for file changes
block-response-codeinteger403HTTP status code for blocked requests
block-messagestring"Access denied"Response body for blocked requests

Command Line Options

OptionEnv VarDescriptionDefault
--socketAGENT_SOCKETUnix socket path/tmp/sentinel-denylist.sock
--grpc-addressGRPC_ADDRESSgRPC listen address (e.g., 0.0.0.0:50051)-
--fileDENYLIST_FILEPath to denylist file(required)
--reload-intervalRELOAD_INTERVALCheck interval for file changes60s
--block-codeBLOCK_CODEHTTP status for blocked requests403
--block-messageBLOCK_MESSAGEResponse body messageAccess denied
--verbose, -vVERBOSEEnable debug loggingfalse

Denylist File Format

The denylist file supports multiple rule types. Each rule is on its own line, with comments starting with #.

IP Addresses and CIDR Ranges

# Single IP addresses
192.168.1.100
10.0.0.50

# CIDR ranges
172.16.0.0/16
10.0.0.0/8

# IPv6
2001:db8::1
2001:db8::/32

User-Agent Blocking

Block requests based on User-Agent header patterns:

# Block specific user agents (exact match)
ua:sqlmap
ua:nikto
ua:nessus

# Block user agent patterns (regex)
ua:/(?i)bot|crawler|spider/
ua:/(?i)python-requests|curl|wget/
ua:/(?i)masscan|nmap|zgrab/

Header Blocking

Block requests based on any header value:

# Block specific header values (exact match)
header:X-Forwarded-For:192.168.1.100
header:X-API-Key:revoked-key-12345

# Block header patterns (regex)
header:Referer:/(?i)spam-domain\.com/
header:X-Custom:/malicious-pattern/

# Block if header is present (any value)
header:X-Debug-Mode:*

Path Blocking

Block requests to specific paths:

# Exact path match
path:/admin
path:/.env
path:/.git/config

# Path prefix
path:/api/internal/*
path:/debug/*

# Path patterns (regex)
path:/(?i)phpmyadmin/
path:/\.php$
path:/wp-(admin|login)/

Query Parameter Blocking

Block requests with specific query parameters:

# Block if parameter exists with any value
query:debug
query:admin_override

# Block specific parameter values
query:token:revoked-token-123
query:api_key:blocked-key

# Block parameter patterns (regex)
query:callback:/^javascript:/
query:redirect:/(?i)evil\.com/

Combined Rules with Tags

Add audit tags for tracking and analytics:

# Rules with custom tags
192.168.1.100 [tag:known-attacker]
ua:sqlmap [tag:scanner,tag:sqli-tool]
path:/admin [tag:admin-access]
header:X-Suspicious:* [tag:suspicious-header,tag:investigate]

Rule Syntax Summary

TypeSyntaxExample
IP<ip> or <cidr>192.168.1.100, 10.0.0.0/8
User-Agentua:<pattern>ua:sqlmap, ua:/bot/
Headerheader:<name>:<value>header:X-Key:bad-key
Pathpath:<pattern>path:/admin, path:/\.env/
Queryquery:<param>[:<value>]query:debug, query:token:abc
Regex/<pattern>//(?i)pattern/
Tags[tag:<name>][tag:blocked]

Response

When a request is blocked:

HTTP/1.1 403 Forbidden
Content-Type: application/json
X-Blocked-By: denylist
X-Blocked-Rule: ip
X-Blocked-Pattern: 192.168.1.100

{"error": "access_denied", "reason": "ip_blocked"}

Response Headers

HeaderDescription
X-Blocked-ByAlways denylist
X-Blocked-RuleRule type: ip, ua, header, path, query
X-Blocked-PatternThe pattern that matched

Block Reasons by Rule Type

Rule TypeReason
IP/CIDRip_blocked
User-Agentuser_agent_blocked
Headerheader_blocked
Pathpath_blocked
Queryquery_blocked

Test Payloads

Test IP Blocking

# Add an IP to denylist
echo "127.0.0.1" >> /etc/sentinel/denylist.txt

# Test (should be blocked)
curl -i http://localhost:8080/api/test

Test User-Agent Blocking

# Add user agent rule
echo "ua:curl" >> /etc/sentinel/denylist.txt

# Test (should be blocked)
curl -i http://localhost:8080/api/test

Response:

HTTP/1.1 403 Forbidden
X-Blocked-By: denylist
X-Blocked-Rule: ua
X-Blocked-Pattern: curl

{"error": "access_denied", "reason": "user_agent_blocked"}

Test Path Blocking

# Add path rule
echo "path:/.env" >> /etc/sentinel/denylist.txt

# Test (should be blocked)
curl -i http://localhost:8080/.env

Response:

HTTP/1.1 403 Forbidden
X-Blocked-By: denylist
X-Blocked-Rule: path
X-Blocked-Pattern: /.env

{"error": "access_denied", "reason": "path_blocked"}

Examples

Basic IP Denylist

agent "denylist" {
    socket "/var/run/sentinel/denylist.sock"

    config {
        file "/etc/sentinel/blocked-ips.txt"
        block-response-code 403
    }
}

blocked-ips.txt:

# Known malicious IPs
192.168.1.100
10.0.0.50

# Entire subnet
172.16.0.0/16

Block Security Scanners

Block known vulnerability scanners and penetration testing tools:

scanners.txt:

# Security scanners
ua:sqlmap [tag:scanner]
ua:nikto [tag:scanner]
ua:nessus [tag:scanner]
ua:nmap [tag:scanner]
ua:masscan [tag:scanner]
ua:zgrab [tag:scanner]
ua:gobuster [tag:scanner]
ua:dirbuster [tag:scanner]

# Generic scanner patterns
ua:/(?i)scanner|exploit|attack/ [tag:scanner]

Block Bad Bots and Scrapers

bad-bots.txt:

# Known bad bots
ua:/(?i)ahrefsbot|semrushbot|dotbot/ [tag:seo-bot]
ua:/(?i)mj12bot|blexbot/ [tag:seo-bot]

# Generic bot patterns
ua:/(?i)bot|crawler|spider/ [tag:bot]

# Headless browsers (often used for scraping)
ua:/(?i)headless|phantomjs|selenium/ [tag:scraper]

# Empty user agent
ua: [tag:no-ua]

Protect Sensitive Paths

sensitive-paths.txt:

# Configuration files
path:/.env [tag:config-exposure]
path:/.git/* [tag:config-exposure]
path:/config.php [tag:config-exposure]
path:/wp-config.php [tag:config-exposure]

# Admin and debug paths
path:/admin [tag:admin-access]
path:/debug [tag:debug-access]
path:/actuator/* [tag:actuator-exposure]

# Backup files
path:/\.bak$/ [tag:backup-file]
path:/\.backup$/ [tag:backup-file]
path:/\.old$/ [tag:backup-file]

# PHP and ASP files (if not using those)
path:/\.php$/ [tag:unexpected-extension]
path:/\.asp$/ [tag:unexpected-extension]

Block Suspicious Query Parameters

query-blocks.txt:

# Debug/admin parameters
query:debug [tag:debug-param]
query:admin [tag:admin-param]
query:test [tag:test-param]

# Known attack patterns
query:callback:/^javascript:/ [tag:xss-attempt]
query:redirect:/(?i)(https?:)?\/\// [tag:open-redirect]
query:url:/(?i)(https?:)?\/\// [tag:ssrf-attempt]

# SQL injection indicators
query:id:/' OR / [tag:sqli-attempt]
query:id:/UNION SELECT/ [tag:sqli-attempt]

Block Revoked API Keys

revoked-keys.txt:

# Revoked API keys
header:X-API-Key:key_abc123_revoked [tag:revoked-key]
header:X-API-Key:key_def456_compromised [tag:revoked-key]
header:Authorization:Bearer tok_expired_xyz [tag:expired-token]

# Block specific origins
header:Origin:/(?i)malicious-site\.com/ [tag:blocked-origin]

Comprehensive Security Denylist

Combine multiple rule types for comprehensive protection:

security-denylist.txt:

# =====================
# IP Blocks
# =====================
# Known threat IPs (update regularly)
192.168.100.0/24 [tag:threat-intel]

# =====================
# Scanner Detection
# =====================
ua:sqlmap [tag:scanner,tag:high-risk]
ua:nikto [tag:scanner,tag:high-risk]
ua:/(?i)nmap|masscan|zgrab/ [tag:scanner]

# =====================
# Bot Management
# =====================
ua:/(?i)bot|crawler|spider/ [tag:bot]
ua: [tag:no-user-agent]

# =====================
# Path Protection
# =====================
path:/.env [tag:sensitive-file]
path:/.git/* [tag:sensitive-file]
path:/admin [tag:admin-path]
path:/\.php$/ [tag:blocked-extension]

# =====================
# Query Protection
# =====================
query:debug [tag:debug-param]
query:callback:/^javascript:/ [tag:xss]

# =====================
# Header Checks
# =====================
header:X-Debug:* [tag:debug-header]

With Custom Response

agent "denylist" {
    socket "/var/run/sentinel/denylist.sock"

    config {
        file "/etc/sentinel/denylist.txt"
        block-response-code 451
        block-message "Access restricted in your region"
    }
}
AgentIntegration
AuthBlock IPs before auth processing
WAFCombine with attack detection
ModSecurityFull WAF with IP reputation

Note: For country-level blocking, use Sentinel’s built-in GeoIP filtering instead.