Multi-layer IP blocking and intrusion prevention for Linux servers.
# Clone the repository
git clone https://github.com/supamanluva/ipblock.git
cd ipblock
# Run the master setup script
sudo ./setup.shThat's it! The setup script will:
- Install prerequisites (ipset, iptables)
- Download and apply scanner blocklists (FireHOL Level 1-3)
- Configure port scan detection with honeypot traps
- Set up kernel-level rate limiting
- Install log-based rate limiter (cron-based, 3-strike system)
- Optionally configure fail2ban
- Set up weekly automatic updates
- Save rules for persistence across reboots
| Layer | Protection | Response |
|---|---|---|
| Scanner Blocklists | Known malicious IPs | Instant block |
| Country Blocking | Geographic filtering | Instant block |
| Port Scan Detection | Honeypot traps | Permanent ban |
| Docker Protection | DOCKER-USER chain rules | Instant block |
| Docker Port Filter | Only expose needed ports | Port blocked |
| Rate Limiting | Connection floods | Temp block (24h) |
| fail2ban | Brute force attacks | Temp block (24h) |
If you prefer to set up components individually:
Before running scripts manually, ensure you have:
-
ipset - IP set administration tool
sudo apt install ipset
-
iptables - Firewall administration tool (usually pre-installed)
sudo apt install iptables
-
curl - Tool for downloading files
sudo apt install curl
Scripts must be run with root privileges because they:
- Creates and modifies ipset tables
- Modifies iptables firewall rules
- Flushes existing firewall configurations
For easier execution and cron automation, install the script to /usr/local/bin/:
sudo cp update-scanner-block.sh /usr/local/bin/update-scanner-blocklist.sh
sudo chmod +x /usr/local/bin/update-scanner-blocklist.shThen you can run it from anywhere:
sudo update-scanner-blocklist.shsudo bash update-scanner-block.shchmod +x update-scanner-block.sh
sudo ./update-scanner-block.sh- Creates ipsets for scanners, country blocking, and whitelist
- Sets up connection tracking - allows all established/related connections (critical!)
- Downloads FireHOL IP blocklists (Levels 1, 2, and 3)
- Downloads country-specific IP blocks (optional, configurable)
- Adds static scanner ranges to the blocklist
- Whitelists your current public IP, VPN (10.8.0.0/16) and LAN (192.168.50.0/24) networks
- Deduplicates and cleans IP entries
- Updates firewall rules to:
- Allow all ESTABLISHED and RELATED connections (your outbound traffic responses)
- Allow localhost traffic
- Allow whitelisted IPs
- Block only NEW incoming connections from scanners (not responses to your requests)
- Optionally block country IPs (if configured)
Edit the BLOCK_MODE variable in the script:
disabled- Downloads and loads blocklists but doesn't apply any firewall rules (for testing)incoming(default) - Blocks NEW incoming connections only. Safe for workstations.router- Blocks both incoming and forwarding. Use on routers/gateways/firewalls.
Set ENABLE_LOGGING="yes" in the script to log blocked packets. View logs with:
sudo dmesg | grep "SCANNER-BLOCKED"Or monitor in real-time:
sudo tail -f /var/log/kern.log | grep "SCANNER-BLOCKED"To block specific countries, edit the script and set the BLOCK_COUNTRIES variable at the top:
# Example: Block China, Russia, Iran, and North Korea
BLOCK_COUNTRIES="cn ru ir kp"Use ISO 3166-1 alpha-2 country codes (two-letter codes). Common examples:
| Code | Country | Code | Country | Code | Country |
|---|---|---|---|---|---|
cn |
China | ru |
Russia | ir |
Iran |
kp |
North Korea | br |
Brazil | in |
India |
pk |
Pakistan | tr |
Turkey | ua |
Ukraine |
vn |
Vietnam | id |
Indonesia | th |
Thailand |
Full list available at: https://www.ipdeny.com/ipblocks/
- Downloads aggregated IP ranges from IPDeny.com for specified countries
- Creates a separate
country_blockipset - Adds iptables rules to DROP traffic from/to blocked countries
- Whitelist takes priority - your VPN/LAN will never be blocked
Based on analysis of real-world port scan and attack data, these countries are the most common sources of malicious traffic targeting servers:
| Code | Country | Attack Share | Common Attack Types |
|---|---|---|---|
cn |
China | ~17% | Port scanning, brute force, botnets |
ru |
Russia | ~8% | Port scanning, exploit attempts |
vn |
Vietnam | ~5% | SSH brute force, scanning |
id |
Indonesia | ~4% | Brute force, web exploits |
ir |
Iran | ~3% | Scanning, credential stuffing |
uz |
Uzbekistan | ~2% | SSH brute force |
bd |
Bangladesh | ~2% | Scanning, brute force |
eg |
Egypt | ~2% | Scanning, web exploits |
dz |
Algeria | ~1% | Scanning |
by |
Belarus | ~1% | Scanning, exploit attempts |
az |
Azerbaijan | ~1% | Scanning |
kp |
North Korea | <1% | State-sponsored scanning |
Recommended configuration (blocks ~45% of malicious traffic):
BLOCK_COUNTRIES="cn ru vn id ir uz bd eg dz by az kp"Note: The US (~30% of scan traffic) is not recommended for blocking since it hosts most legitimate services (GitHub, CDNs, APIs, etc.).
Leave the variable empty:
BLOCK_COUNTRIES=""- Script automatically detects and whitelists your current public IP
- Whitelist rules are applied FIRST before any blocking rules
- VPN (10.8.0.0/16) and LAN (192.168.50.0/24) are always allowed
- If you lose connection, flush rules:
sudo iptables -F INPUT && sudo iptables -F FORWARD
- Have physical or console access to your server
- Understand the networks being whitelisted
- Test in a non-production environment first
- Know how to restore access if something goes wrong
The iptables rules created by this script are not persistent across reboots by default. To make them persistent:
sudo apt install iptables-persistent
sudo netfilter-persistent savesudo apt install ipset-persistent
sudo service netfilter-persistent saveRECOMMENDED: Run this script at least once per week to keep blocklists up-to-date. FireHOL and IPDeny lists are regularly updated with new threats and country IP ranges.
-
Install script to system path (if not already done):
sudo cp update-scanner-block.sh /usr/local/bin/update-scanner-blocklist.sh sudo chmod +x /usr/local/bin/update-scanner-blocklist.sh
-
Open root crontab:
sudo crontab -e
-
Add one of the schedules below to the crontab file:
Recommended schedules:
Run weekly (Sundays at 3 AM):
0 3 * * 0 /usr/local/bin/update-scanner-blocklist.sh >> /var/log/scanner-block.log 2>&1
Run daily at 3 AM (for high-security environments):
0 3 * * * /usr/local/bin/update-scanner-blocklist.sh >> /var/log/scanner-block.log 2>&1
Run twice weekly (Sunday and Wednesday at 3 AM):
0 3 * * 0,3 /usr/local/bin/update-scanner-blocklist.sh >> /var/log/scanner-block.log 2>&1
-
Save and exit the crontab editor (in nano:
Ctrl+X, thenY, thenEnter) -
Verify crontab is set:
sudo crontab -l
If the script fails:
- Check if ipset is installed:
which ipset - Check if you have root access:
sudo -v - View current ipset tables:
sudo ipset list - View current iptables rules:
sudo iptables -L -n -v - Check country code is valid: visit https://www.ipdeny.com/ipblocks/
- Test country download manually:
curl -s https://www.ipdeny.com/ipblocks/data/aggregated/cn-aggregated.zone
Your script uses FireHOL Level 1, 2, and 3. More lists available at:
- Website: https://iplists.firehol.org/
- Available lists: Malware, botnets, anonymous proxies, tor exits, and more
- Usage: Simply add more
curlcommands in the script pointing to other.netsetfiles
- Website: https://www.ipdeny.com/ipblocks/
- Aggregated zones: Optimized for better performance (used in this script)
- All zones archive: Download all countries at once with
all-zones.tar.gz - IPv6 support: Available separately
To add more threat intelligence, insert these after the Level 3 download:
echo "Downloading FireHOL Anonymous Proxies..."
curl -s http://iplists.firehol.org/files/firehol_anonymous.netset >> "$TMP"
echo "Downloading FireHOL Webserver..."
curl -s http://iplists.firehol.org/files/firehol_webserver.netset >> "$TMP"In addition to static blocklists, this project includes dynamic rate limiting to detect and block IPs that hammer your server. Three layers of protection work together:
sudo ./install-rate-limiting.shThis installs all three protection layers with sensible defaults.
Real-time protection using iptables modules. No log parsing needed.
Script: rate-limit-iptables.sh
Features:
- Recent module: Max 30 new connections/minute per IP
- Connlimit: Max 50 concurrent connections per IP
- Hashlimit: Sophisticated rate limiting with burst protection
Usage:
sudo ./rate-limit-iptables.shConfiguration (edit the script):
MAX_CONNECTIONS_PER_MINUTE=30 # Adjust based on your traffic
BURST_LIMIT=10 # Allow initial burst
PROTECTED_PORTS="22,80,443,8080" # Ports to protect (empty = all)Monitor:
sudo dmesg -w | grep -E 'RATE-LIMITED|CONNLIMIT|HASHLIMIT'
cat /proc/net/xt_recent/RATE_LIMITAnalyzes access logs every 5 minutes and uses a 3-strike system before blocking.
Script: log-rate-limiter.sh
Detection Methods:
- High request rate (>200 requests/5 min)
- 404 scanning (>10 not-found requests/min)
- Suspicious path access (wp-admin, .env, .git, etc.)
- Auth failures (SSH brute force)
- Repeated blocked attempts in kernel log
Strike System:
- First offense → Strike 1 (logged)
- Second offense → Strike 2 (logged)
- Third offense → 24-hour IP ban
Usage:
# Manual run
sudo ./log-rate-limiter.sh
# Install with cron (runs every 5 minutes)
sudo cp log-rate-limiter.sh /usr/local/bin/log-rate-limiter
echo "*/5 * * * * root /usr/local/bin/log-rate-limiter >> /var/log/rate-limiter.log 2>&1" | sudo tee /etc/cron.d/log-rate-limiterConfiguration (edit the script):
REQUESTS_PER_MINUTE=60 # Max requests per minute
REQUESTS_PER_5MIN=200 # Max requests per 5 minutes
BLOCK_DURATION=86400 # Block duration (24 hours)
ERROR_THRESHOLD=20 # Max errors per minute
REPEATED_404_THRESHOLD=10 # Max 404s per minuteView offenders:
cat /var/lib/ipblock/offenders.log
cat /var/lib/ipblock/strikes.dbIndustry-standard intrusion prevention that monitors logs for malicious patterns.
Script: fail2ban-setup.sh
Features:
- SSH brute force protection (3 attempts = 24h ban)
- HTTP authentication failures
- Bot/scanner detection
- Recidive detection (repeat offenders get week-long bans)
- Uses ipset for efficient blocking
Custom Jails Included:
| Jail | Description | Max Retries | Ban Time |
|---|---|---|---|
sshd |
SSH failures | 3 | 24 hours |
sshd-aggressive |
SSH repeat offenders | 1 | 7 days |
http-hammer |
HTTP request flood | 100/min | 24 hours |
http-scanner |
Vulnerability scanning | 5 | 24 hours |
recidive |
Repeat offenders | 3 bans | 7 days |
Usage:
sudo ./fail2ban-setup.shManage fail2ban:
fail2ban-client status # Show all jails
fail2ban-client status sshd # Specific jail status
fail2ban-client set sshd banip IP # Manually ban
fail2ban-client set sshd unbanip IP # Manually unban
fail2ban-client reload # Reload configScript: show-blocked.sh
Shows all currently blocked IPs across all ipsets with remaining ban time:
sudo ./show-blocked.shOutput includes:
- Scanner blocklist entries
- Country blocks
- Rate-limited IPs (with expiry time)
- fail2ban blocks (with expiry time)
- Recent block events
- Statistics
Block an IP for 24 hours:
sudo ipset add rate_limited 1.2.3.4Block an IP permanently (until next blocklist update):
sudo ipset add scanners 1.2.3.4Unblock an IP:
sudo ipset del rate_limited 1.2.3.4
sudo ipset del fail2ban 1.2.3.4Check if IP is blocked:
sudo ipset test rate_limited 1.2.3.4
sudo ipset test scanners 1.2.3.4| ipset Name | Purpose | Timeout |
|---|---|---|
scanners |
FireHOL + static blocklists | Permanent |
country_block |
Country-based blocking | Permanent |
rate_limited |
Log-based rate limiter | 24 hours |
fail2ban |
fail2ban bans | 24 hours |
whitelist |
Never blocked IPs | Permanent |
Real-time block monitoring:
sudo dmesg -w | grep -E 'SCANNER-BLOCKED|RATE-LIMITED|FAIL2BAN-BLOCKED'Rate limiter logs:
sudo tail -f /var/log/rate-limiter.logfail2ban logs:
sudo tail -f /var/log/fail2ban.logFor a typical web server:
-
Run the master installer:
sudo ./install-rate-limiting.sh
-
Tune thresholds based on your traffic:
- Low-traffic site: Lower thresholds (20 req/min)
- High-traffic site: Higher thresholds (100+ req/min)
-
Add to cron for regular updates:
# Update blocklists weekly 0 3 * * 0 /usr/local/bin/update-scanner-blocklist.sh >> /var/log/scanner-block.log 2>&1 # Rate limiter runs every 5 minutes (installed automatically)
-
Whitelist your monitoring/health check IPs:
sudo ipset add whitelist YOUR_MONITORING_IP
Automatically detect and permanently block IPs that port scan your server.
sudo ./portscan-detect.sh| Method | Description | Action |
|---|---|---|
| Stealth Scans | NULL, XMAS, SYN/FIN scans | Logged & dropped |
| Honeypot Ports | Connections to unused trap ports | Instant permanent block |
| Rapid Scanning | 5+ ports in 60 seconds | Logged & dropped |
| PSD Module | Kernel-level scan detection | Logged & dropped |
Any connection to these ports results in an instant permanent ban:
| Port | Service | Port | Service |
|---|---|---|---|
| 23 | Telnet | 3389 | RDP |
| 135-139 | NetBIOS | 5432 | PostgreSQL |
| 445 | SMB | 5900 | VNC |
| 1433-1434 | MS-SQL | 6379 | Redis |
| 3306 | MySQL | 27017 | MongoDB |
portscan-detect.sh:
HONEYPOT_PORTS="23,135,137,138,139,445,1433,1434,3306,3389,5432,5900,6379,11211,27017"Blocked IPs are saved hourly and restored on reboot via /etc/cron.d/ipblock-persist:
- Saves to
/etc/ipblock/portscan_blocked.save - Restores rules and blocked IPs on boot
Docker published ports bypass the standard INPUT chain entirely — traffic goes through FORWARD → DOCKER-USER instead. This means standard iptables/ipset rules in the INPUT chain do not protect Docker containers with published ports.
The update-scanner-block.sh script automatically adds blocking rules to the DOCKER-USER chain, ensuring all ipset blocklists also protect Docker services.
Traffic to Docker containers is filtered through the same blocklists:
| Rule | Action |
|---|---|
| Established connections | RETURN (allow) |
| Whitelist IPs | RETURN (allow) |
| Country-blocked IPs | DROP |
| Known scanners | DROP |
| Port scan offenders | DROP |
| Allowed ports (80,443,81) | RETURN (allow) |
| Other ports from external IPs | DROP |
| Everything else | RETURN (allow) |
The script flushes and rebuilds DOCKER-USER on each run:
# Rules are applied in order (first match wins)
iptables -A DOCKER-USER -m state --state ESTABLISHED,RELATED -j RETURN
iptables -A DOCKER-USER -m set --match-set whitelist src -j RETURN
iptables -A DOCKER-USER -m set --match-set country_block src -j DROP
iptables -A DOCKER-USER -m set --match-set scanners src -j DROP
iptables -A DOCKER-USER -m set --match-set portscan_blocked src -j DROP
# Port filtering (added by docker-port-filter.sh):
iptables -A DOCKER-USER -p tcp -m multiport --dports 80,443,81 --ctstate NEW -j RETURN
iptables -A DOCKER-USER -p tcp --ctstate NEW ! internal-sources -j DROP
iptables -A DOCKER-USER -p udp --ctstate NEW ! internal-sources -j DROP
iptables -A DOCKER-USER -j RETURNThe docker-port-filter.sh script adds port-level access control to Docker containers. Docker bypasses UFW/INPUT chain entirely, so without this, every published port is accessible from the internet regardless of your firewall settings.
Configure allowed ports in docker-port-filter.sh:
ALLOWED_PORTS="80,443,81" # Only these ports reachable from internetAll other Docker-published ports (Portainer, Zipline, app backends, etc.) remain accessible:
- Between containers on Docker networks
- From localhost
- From private IP ranges (10.x, 172.16-31.x, 192.168.x)
To add or change allowed ports:
# Edit the script
nano docker-port-filter.sh
# Re-apply
sudo ./docker-port-filter.sh# Check DOCKER-USER rules
sudo iptables -L DOCKER-USER -v -n
# See blocked packets (counters > 0 means it's working)
sudo iptables -L DOCKER-USER -v -n | grep DROPNote: Docker must be running for the
DOCKER-USERchain to exist. The script skips Docker protection if the chain is not found.
# Show all blocks with summary
sudo ./show-blocked.sh
# List specific ipsets
sudo ipset list scanners # FireHOL blocklist
sudo ipset list country_block # Country blocks
sudo ipset list rate_limited # Rate limited (24h)
sudo ipset list fail2ban # fail2ban blocks
sudo ipset list portscan_blocked # Port scanners (permanent)
sudo ipset list whitelist # Whitelisted IPs# Block an IP (24 hours)
sudo ipset add rate_limited 1.2.3.4
# Block a port scanner (permanent)
sudo ipset add portscan_blocked 1.2.3.4
# Block permanently in scanner list
sudo ipset add scanners 1.2.3.4
# Unblock an IP
sudo ipset del rate_limited 1.2.3.4
sudo ipset del portscan_blocked 1.2.3.4
sudo ipset del scanners 1.2.3.4
# Whitelist an IP (never blocked)
sudo ipset add whitelist 1.2.3.4sudo ipset test scanners 1.2.3.4
sudo ipset test portscan_blocked 1.2.3.4
sudo ipset test rate_limited 1.2.3.4# All blocked traffic
sudo dmesg -w | grep -E 'BLOCKED|SCAN|LIMIT|HONEYPOT'
# Scanner blocks only
sudo dmesg -w | grep 'SCANNER-BLOCKED'
# Port scan detections
sudo dmesg -w | grep -E 'PORTSCAN|HONEYPOT'
# Rate limiting
sudo dmesg -w | grep -E 'RATE-LIMITED|HASHLIMIT|CONNLIMIT'# Kernel/firewall logs
sudo dmesg | grep -E 'BLOCKED|SCAN' | tail -50
# Rate limiter log
sudo tail -f /var/log/rate-limiter.log
# Port scan log
sudo tail -f /var/log/portscan.log
# fail2ban log
sudo tail -f /var/log/fail2ban.log
# Offender logs
cat /var/lib/ipblock/offenders.log
cat /var/lib/ipblock/portscan_offenders.log# View iptables packet counts (INPUT chain)
sudo iptables -L INPUT -v -n | grep -E 'scanners|rate_limited|fail2ban|portscan'
# View Docker protection packet counts (DOCKER-USER chain)
sudo iptables -L DOCKER-USER -v -n | grep -E 'scanners|country_block|portscan'
# Count entries in each ipset
for set in scanners country_block rate_limited fail2ban portscan_blocked whitelist; do
echo "$set: $(sudo ipset list $set 2>/dev/null | grep -c '^[0-9]' || echo 0) entries"
donefail2ban-client status # All jails
fail2ban-client status sshd # SSH jail
fail2ban-client set sshd banip IP # Manual ban
fail2ban-client set sshd unbanip IP # Manual unban
fail2ban-client reload # Reload config| Script | Purpose |
|---|---|
setup.sh |
Master setup - run this first! |
update-scanner-block.sh |
Download and apply FireHOL blocklists |
portscan-detect.sh |
Setup port scan detection rules |
portscan-log-analyzer.sh |
Analyze logs for scan patterns (cron) |
rate-limit-iptables.sh |
Setup iptables rate limiting |
log-rate-limiter.sh |
Analyze logs for hammering (cron) |
fail2ban-setup.sh |
Install and configure fail2ban |
docker-port-filter.sh |
Restrict Docker ports accessible from internet |
install-rate-limiting.sh |
Installer for rate limiting only |
show-blocked.sh |
Display all blocked IPs |
check-blocking-status.sh |
Check firewall status |
verify-setup.sh |
Verify setup is complete |
test-country-block.sh |
Test country blocking |
| ipset Name | Purpose | Timeout | Source |
|---|---|---|---|
scanners |
FireHOL + static IPs | Permanent | update-scanner-block.sh |
country_block |
Country IP ranges | Permanent | update-scanner-block.sh |
rate_limited |
HTTP hammering | 24 hours | log-rate-limiter.sh |
fail2ban |
Brute force attacks | 24 hours | fail2ban |
portscan_blocked |
Port scanners | Permanent | portscan-detect.sh |
whitelist |
Never blocked | Permanent | update-scanner-block.sh |
Located in /etc/cron.d/:
# /etc/cron.d/ipblock-persist
0 * * * * root ipset save portscan_blocked > /etc/ipblock/portscan_blocked.save
@reboot root sleep 10 && /path/to/portscan-detect.sh
# /etc/cron.d/log-rate-limiter
*/5 * * * * root /usr/local/bin/log-rate-limiter >> /var/log/rate-limiter.log
# Recommended: weekly blocklist update (add to root crontab)
0 3 * * 0 /path/to/update-scanner-block.sh >> /var/log/scanner-block.log 2>&1