NGINX / RPM Repository

NGINX Honeypot 2.0: Zero-Latency Bot Blocking Without External Scripts

by ,


We have by far the largest RPM repository with NGINX module packages and VMODs for Varnish. If you want to install NGINX, Varnish, and lots of useful performance/security software with smooth yum upgrades for production use, this is the repository for you.
Active subscription is required.

The enterprise approach to IP blacklisting that eliminates shell scripts, sudo, and fcgiwrap


In a previous article, we explored how NGINX honeypots can catch and ban malicious bots by routing requests to a FastCGI script. That approach works—but it has inherent limitations that become painful at scale:

  • Process overhead: Every honeypot hit spawns fcgiwrap → bash → ipset
  • Attack surface: sudo-enabled shell scripts are a security liability
  • Latency: Script execution adds milliseconds to what should be microseconds
  • Complexity: fcgiwrap sockets, sudoers files, CGI scripts—too many moving parts
  • No observability: How many bots are you catching? Which traps are most effective?

What if you could achieve the same result—and more—with a single NGINX directive?

The Solution: NGINX IPSet Access Module

The NGINX IPSet Access Module integrates libipset directly into NGINX. No external processes. No shell scripts. No sudo. Just native C code running at wire speed.

# Old way: 6 lines + external script + sudoers + fcgiwrap service
location /config.php {
    fastcgi_intercept_errors off;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME /usr/local/libexec/block-ip.cgi;
    fastcgi_pass unix:/run/fcgiwrap/fcgiwrap-nginx.sock;
    keepalive_timeout 0;
}

# New way: 2 lines, zero dependencies
location /config.php {
    ipset_autoadd honeypot;
    return 200 "OK";
}

The difference isn’t just aesthetic. It’s architectural.

Prerequisites: Keep Your FirewallD Setup

The ipset infrastructure from the previous article remains unchanged. The module manipulates ipsets—it doesn’t replace them. If you haven’t set up your ipsets yet:

# Create ipsets (same as before)
firewall-cmd --permanent --new-ipset=honeypot4 --type=hash:ip --option=maxelem=1000000 --option=family=inet --option=hashsize=4096
firewall-cmd --permanent --new-ipset=honeypot6 --type=hash:ip --option=maxelem=1000000 --option=family=inet6 --option=hashsize=4096
firewall-cmd --permanent --zone=drop --add-source=ipset:honeypot4 
firewall-cmd --permanent --zone=drop --add-source=ipset:honeypot6 
firewall-cmd --reload

The ipsets work with FirewallD to drop all future connections at the kernel level. NGINX handles detection; the firewall handles enforcement.

Installing the Module

As our entire repository, the module is available for any RPM-based system and under both x86_64 and arm64 architectures.

# Install GetPageSpeed repository
sudo dnf -y install https://extras.getpagespeed.com/release-latest.rpm

sudo dnf install nginx-module-ipset-access

# Enable in nginx.conf (before http block)
load_module modules/ngx_http_ipset_access_module.so;

Grant NGINX the capability to modify ipsets:

sudo setcap cap_net_admin+ep /usr/sbin/nginx

Honeypot Configuration: The Modern Way

Here’s a complete honeypot configuration that replaces the entire fcgiwrap approach:

load_module modules/ngx_http_ipset_access_module.so;

http {
    # Block anyone already in honeypot
    ipset_blacklist honeypot4;
    ipset_status 444;  # Close connection silently

    server {
        listen 80;
        server_name example.com;

        # Your real content
        location / {
            root /var/www/html;
        }

        # === HONEYPOT TRAPS ===

        # WordPress upload vulnerabilities (the original use case)
        location ~ ^/wp-content/.*\.php$ {
            ipset_autoadd honeypot4 timeout=86400;
            return 200 "OK";
        }

        # WordPress config and install
        location = /wp-config.php {
            ipset_autoadd honeypot4 timeout=86400;
            return 200 "OK";
        }

        location = /wp-admin/install.php {
            ipset_autoadd honeypot4 timeout=86400;
            return 200 "OK";
        }

        # Database admin tools that should never exist
        location ~* ^/(phpmyadmin|pma|mysql|adminer)/ {
            ipset_autoadd honeypot4 timeout=604800;  # 1 week ban
            return 200 "OK";
        }

        # Shell and exploit scanners get extra punishment
        location ~ ^/(shell|cmd|backdoor|c99|r57|eval)\.(php|txt)$ {
            ipset_autoadd honeypot4 timeout=2592000;  # 30 day ban
            return 200 "OK";
        }

        # Config file hunters
        location ~ ^/\.(env|git|svn|htaccess|htpasswd) {
            ipset_autoadd honeypot4 timeout=86400;
            return 200 "OK";
        }
    }
}

That’s it. No CGI scripts. No sudoers configuration. No fcgiwrap service. When a bot hits any honeypot location, their IP is added to the ipset in microseconds, and their connection is terminated.

Beyond Honeypots: What Script-Based Solutions Can’t Do

The module doesn’t just simplify honeypots—it enables capabilities that would be impractical with external scripts.

Built-in Rate Limiting with Auto-Ban

Catch abusers automatically without log parsing:

# 100 requests/minute; violators banned for 30 minutes
ipset_ratelimit rate=100 window=60s autoban=ratelimited ban_time=1800;

This runs in shared memory. Every worker sees the same counters. No external daemons, no log tailing, no race conditions.

JavaScript Challenge for Bot Detection

Stop headless browsers and scripted attacks:

ipset_challenge on;
ipset_challenge_difficulty 2;  # ~500ms solve time

First-time visitors must solve a proof-of-work puzzle. Real browsers handle it transparently. Automated scripts fail. No CAPTCHAs, no third-party services.

Host Header Validation (Built-in)

Remember the host header vulnerability protection from the previous article? The module makes it trivial:

# Catch requests with invalid Host headers
map $http_host $valid_host {
    example.com 1;
    www.example.com 1;
    default 0;
}

server {
    listen 80 default_server;

    if ($valid_host = 0) {
        # Invalid host → honeypot
        set $trap 1;
    }

    location @honeypot {
        ipset_autoadd honeypot4 timeout=86400;
        return 444;
    }
}

Production Observability

Know what’s happening in real-time:

location = /metrics {
    ipset_metrics;
    allow 10.0.0.0/8;
    deny all;
}

location = /_stats {
    ipset_stats;
    allow 127.0.0.1;
    deny all;
}

The /metrics endpoint exports Prometheus-compatible metrics:

nginx_ipset_requests_total{result="blocked"} 15234
nginx_ipset_autoadd_total{result="success"} 892
nginx_ipset_cache_total{result="hit"} 2847293

Build dashboards. Set alerts. Know exactly how many bots you’re catching and which honeypots are most effective.

Performance: Why It Matters at Scale

The old approach processes each honeypot hit like this:

Request → NGINX → FastCGI socket → fcgiwrap → bash → sudo → ipset CLI → response

That’s 5-15ms minimum. Under attack, you’re spawning hundreds of processes per second.

The module does this:

Request → NGINX (in-process libipset call) → response

That’s 50-100 microseconds. Two orders of magnitude faster. And the lookup cache means repeated checks for the same IP hit shared memory—not the kernel.

Cache Hit Rates

The module maintains an LRU cache of ipset lookups:

ipset_cache_ttl 60s;  # Cache results for 60 seconds

In production, expect 95%+ cache hit rates. That means 95% of requests never touch the kernel for ipset queries.

Migration Checklist

If you’re running the fcgiwrap-based honeypot from the previous article, here’s your upgrade path:

  1. Install the module
    sudo dnf install nginx-module-ipset-access
    
  2. Update nginx.conf
    • Add load_module modules/ngx_http_ipset_access_module.so;
    • Add ipset_blacklist honeypot4; in http or server block
    • Replace honeypot locations with ipset_autoadd directives
  3. Grant capability
    sudo setcap cap_net_admin+ep /usr/sbin/nginx
    
  4. Disable and remove old components
    sudo systemctl disable --now fcgiwrap@nginx.socket
    sudo rm /etc/sudoers.d/nginx-block-ip
    sudo rm /usr/local/libexec/block-ip.cgi
    sudo rm /usr/local/sbin/block-ip.sh
    
  5. Test and reload
    sudo nginx -t && sudo systemctl reload nginx
    

Your honeypots now run faster, more securely, and with full observability.

Enterprise Deployment: Full Security Stack

Here’s a production configuration combining all security layers:

load_module modules/ngx_http_ipset_access_module.so;

http {
    # === LAYER 1: Known Threats ===
    ipset_blacklist honeypot4 malware_ips tor_exits;
    ipset_status 444;
    ipset_cache_ttl 5m;
    ipset_fail_open off;  # Deny on ipset errors (safe default)

    # === LAYER 2: Rate Limiting ===
    ipset_ratelimit rate=120 window=60s autoban=ratelimited ban_time=1800;

    # === LAYER 3: Bot Challenge ===
    ipset_challenge on;
    ipset_challenge_difficulty 2;

    # Custom logging for security analysis
    log_format security '$remote_addr [$time_local] "$request" '
                        '$status ipset=$ipset_result matched=$ipset_matched_set';
    access_log /var/log/nginx/security.log security;

    server {
        listen 80 default_server;
        server_name _;

        # Metrics (internal only)
        location = /metrics {
            ipset_metrics;
            allow 10.0.0.0/8;
            allow 127.0.0.1;
            deny all;
        }

        # Real content
        location / {
            root /var/www/html;
            index index.html;
        }

        # === HONEYPOT NETWORK ===

        # Severity: HIGH - Shell/exploit attempts
        location ~ \.(cgi|sh|pl)$ {
            ipset_autoadd honeypot4 timeout=2592000;
            return 200 "OK";
        }

        # Severity: MEDIUM - CMS exploitation
        location ~ ^/wp-content/.*\.php$ {
            ipset_autoadd honeypot4 timeout=604800;
            return 200 "OK";
        }

        # Severity: MEDIUM - Admin tools
        location ~* ^/(phpmyadmin|adminer|mysql)/ {
            ipset_autoadd honeypot4 timeout=604800;
            return 200 "OK";
        }

        # Severity: LOW - Generic probing
        location ~ ^/\.(env|git|svn) {
            ipset_autoadd honeypot4 timeout=86400;
            return 200 "OK";
        }
    }
}

Testing in Production: Dry-Run Mode

Not ready to block? Test your configuration without affecting traffic:

ipset_blacklist suspicious_ips;
ipset_dryrun on;

Check logs for:

ipset: DRYRUN would block 203.0.113.42 (matched: suspicious_ips)

Tune your ipsets, verify your traps, then flip ipset_dryrun off; to go live.

The Bottom Line

Aspect fcgiwrap + Scripts IPSet Access Module
Latency 5-15ms 50-100µs
Dependencies fcgiwrap, bash, sudo None
Security Shell script attack surface No shell, no sudo
Observability DIY logging Prometheus metrics
Rate Limiting Requires Fail2ban Built-in
Bot Challenge Requires CAPTCHA service Built-in JS PoW
Configuration Multiple files Single nginx.conf

The honeypot concept hasn’t changed. The execution has evolved. What once required a stack of scripts and services is now a single directive in your NGINX configuration.

Your bots won’t notice the difference. Your infrastructure team will.


Ready to upgrade? The NGINX IPSet Access Module is available through the GetPageSpeed Premium Repository.

Stop feeding bots to scripts. Start feeding them to the firewall.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

This site uses Akismet to reduce spam. Learn how your comment data is processed.