Why Block at the Network Level?
Application-level blocking (in your web app or SSH config) is good, but network-level blocking is better. When you block an IP at the firewall, the attacker's packets are dropped before they reach any application, reducing CPU load, bandwidth consumption, and log noise. For high-volume attacks like DDoS or brute force campaigns, this difference matters enormously.
Network-level blocking also protects all services on the host simultaneously. An IP blocked in iptables cannot reach your SSH, web server, database, or any other listening service. This is especially important for multi-service servers where each application would otherwise need its own blocklist.
The key is to combine reactive blocking (ban after detection) with proactive blocking (ban known threats before they attack). Threat intelligence feeds like WAYSCloud provide the data for proactive blocking, while tools like fail2ban handle reactive blocking.
Method 1: iptables (Classic Linux Firewall)
iptables is available on virtually every Linux system and provides granular control. For blocking malicious IPs, use the INPUT chain to drop inbound traffic and optionally the OUTPUT chain to prevent compromised services from communicating outbound:
# Block a single IP (inbound)
iptables -A INPUT -s 192.168.1.100 -j DROP
# Block a CIDR range
iptables -A INPUT -s 45.92.0.0/16 -j DROP
# Block outbound to a C2 server (prevents data exfiltration)
iptables -A OUTPUT -d 185.220.101.0/24 -j DROP
# Using ipset for large blocklists (MUCH faster than individual rules)
ipset create blocklist hash:ip maxelem 100000 timeout 86400
ipset add blocklist 192.168.1.100
iptables -I INPUT -m set --match-set blocklist src -j DROP
# Bulk import from WAYSCloud API into ipset
curl -s "https://ip.wayscloud.services/api/threats/live?limit=500" \
| jq -r '.threats[].ip_address' \
| while read ip; do
ipset add blocklist "$ip" 2>/dev/null
done
# Save iptables rules to persist across reboot
iptables-save > /etc/iptables/rules.v4
Performance tip: Never add thousands of individual iptables rules. Use ipset instead. A single ipset match rule can check against 100,000 IPs faster than 100 sequential iptables rules. The lookup is O(1) using hash tables.
Method 2: nftables (Modern Linux Firewall)
nftables is the replacement for iptables on modern Linux distributions (Debian 11+, Ubuntu 21.04+, RHEL 9+). It has a cleaner syntax and native set support without needing ipset:
# Create a table and chain
nft add table ip filter
nft add chain ip filter input { type filter hook input priority 0 \; }
# Create a named set for blocklist IPs
nft add set ip filter blocklist { type ipv4_addr \; flags timeout \; }
# Add IPs to the set (with 24h timeout)
nft add element ip filter blocklist { 192.168.1.100 timeout 24h }
nft add element ip filter blocklist { 45.92.1.0/24 timeout 24h }
# Create a drop rule for the set
nft add rule ip filter input ip saddr @blocklist drop
# Bulk import from WAYSCloud
curl -s "https://ip.wayscloud.services/api/threats/live?limit=500" \
| jq -r '.threats[].ip_address' \
| xargs -I {} nft add element ip filter blocklist { {} timeout 24h }
Method 3: fail2ban (Automated Reactive Blocking)
fail2ban watches log files for patterns (failed logins, exploit attempts) and automatically bans offending IPs. It is the standard tool for reactive SSH and web application protection:
# Install sudo apt install fail2ban # Debian/Ubuntu sudo yum install fail2ban # RHEL/CentOS # Configure SSH protection (/etc/fail2ban/jail.local) [sshd] enabled = true port = ssh filter = sshd logpath = /var/log/auth.log maxretry = 3 bantime = 3600 # 1 hour ban findtime = 600 # Within 10 minutes # Configure aggressive protection for repeat offenders [sshd-aggressive] enabled = true port = ssh filter = sshd logpath = /var/log/auth.log maxretry = 1 # Ban on FIRST attempt bantime = 86400 # 24 hour ban findtime = 86400 # Look at full day banaction = iptables-allports # Block ALL ports, not just SSH # Start and enable sudo systemctl enable fail2ban sudo systemctl start fail2ban # Check status sudo fail2ban-client status sshd sudo fail2ban-client banned
fail2ban can also be configured to report banned IPs to WAYSCloud, contributing to the shared threat intelligence network. See the integration guide for setup instructions.
Method 4: nginx (Web Server Level)
For web-facing servers, nginx can deny traffic from specific IPs or IP ranges before the request reaches your application:
# nginx - block specific IPs (/etc/nginx/conf.d/blocklist.conf)
# Include this in your server block
deny 192.168.1.100;
deny 45.92.0.0/16;
deny 185.220.101.0/24;
# ... add more as needed
# nginx - use geo module for large blocklists (more efficient)
geo $blocked_ip {
default 0;
192.168.1.100 1;
45.92.0.0/16 1;
185.220.101.0/24 1;
# include /etc/nginx/blocklist-ips.conf; # from file
}
server {
if ($blocked_ip) {
return 444; # Drop connection silently
}
}
# Generate blocklist from WAYSCloud API
curl -s "https://ip.wayscloud.services/api/threats/live?limit=1000" \
| jq -r '.threats[].ip_address' \
| awk '{print "deny " $1 ";"}' \
> /etc/nginx/conf.d/wayscloud-blocklist.conf
# Reload nginx
sudo nginx -t && sudo nginx -s reload
Method 5: Automated API-Based Blocking with Python
For production environments, automate the entire process with a cron job or daemon that fetches threat data and updates your firewall in real time:
#!/usr/bin/env python3
"""
WAYSCloud Threat Blocker
Fetches live threat data and blocks high-risk IPs via ipset.
Run as cron every 15 minutes: */15 * * * * /usr/local/bin/wayscloud-blocker
"""
import subprocess
import requests
import sys
API_URL = "https://ip.wayscloud.services/api/threats/live"
IPSET_NAME = "wayscloud_blocklist"
MIN_SCORE = 60 # Only block IPs with threat_score >= 60
MAX_IPS = 1000 # Maximum IPs to import per run
def fetch_threats():
"""Fetch high-risk IPs from WAYSCloud"""
try:
resp = requests.get(API_URL, params={"limit": MAX_IPS}, timeout=30)
resp.raise_for_status()
data = resp.json()
return [
t["ip_address"] for t in data.get("threats", [])
if t.get("threat_score", 0) >= MIN_SCORE
]
except Exception as e:
print(f"Error fetching threats: {e}", file=sys.stderr)
return []
def update_ipset(ips):
"""Update ipset with new IPs"""
# Ensure ipset exists
subprocess.run(
["ipset", "create", IPSET_NAME, "hash:ip",
"maxelem", "100000", "timeout", "86400"],
capture_output=True
)
for ip in ips:
subprocess.run(
["ipset", "add", IPSET_NAME, ip, "timeout", "86400"],
capture_output=True
)
print(f"Updated {IPSET_NAME}: {len(ips)} IPs added/refreshed")
if __name__ == "__main__":
ips = fetch_threats()
if ips:
update_ipset(ips)
Method 6: Bash One-Liner (Quick and Simple)
For quick ad-hoc blocking when you need to act fast:
# Block top 100 threats right now
curl -s "https://ip.wayscloud.services/api/threats/live?limit=100" \
| jq -r '.threats[].ip_address' \
| xargs -I {} iptables -A INPUT -s {} -j DROP
# Check a specific IP before blocking
curl -s "https://ip.wayscloud.services/api/v1/ip/185.220.101.1" | jq '.'
# Block all IPs from a specific country (use with caution)
curl -s "https://ip.wayscloud.services/api/country/RU" \
| jq -r '.top_ips[].ip_address' \
| head -50 \
| xargs -I {} iptables -A INPUT -s {} -j DROP
Best Practices for IP Blocking
Blocking malicious IPs is powerful but should be done carefully to avoid collateral damage:
- Use threat scores, not just presence — Not every flagged IP deserves a permanent block. WAYSCloud provides a threat score (0-100). Block IPs scoring above 60-80, monitor those scoring 30-60, and allow those below 30. This prevents blocking dynamic residential IPs that may have been temporarily compromised.
- Set ban expiration times — Use timeouts (24 hours, 7 days) rather than permanent bans. Threat IPs change frequently as attackers rotate infrastructure. Permanent bans accumulate stale entries and waste memory.
- Maintain a whitelist — Always whitelist your own IPs, monitoring services, CDN IPs (Cloudflare, AWS), payment processors, and other critical services. A false positive blocking Cloudflare will take down your entire website.
- Log before you drop — Use
LOGrules beforeDROPrules during testing so you can audit what is being blocked. This helps identify false positives quickly. - Use ipset or nftables sets — Never add thousands of individual iptables rules. Use set-based matching for O(1) lookups instead of O(n) sequential rule evaluation.
- Automate updates — Threat data goes stale within hours. Set up a cron job that refreshes your blocklist from the WAYSCloud API every 15-60 minutes for maximum protection.
- Consider geographic blocking carefully — Blocking entire countries may be appropriate for some services (e.g., a Norwegian company's admin panel), but should never be applied to public-facing content or APIs without careful analysis.
Choosing the Right Method
| Method | Best For | Scale | Automation |
|---|---|---|---|
| iptables + ipset | All-purpose network blocking | 100K+ IPs | Cron + API |
| nftables | Modern Linux distributions | 100K+ IPs | Cron + API |
| fail2ban | Reactive SSH/web protection | Per-service | Automatic |
| nginx deny | Web server level only | 10K IPs | Config reload |
| Python script | Custom logic and integration | Unlimited | Full control |
Most production setups benefit from combining methods: fail2ban for reactive protection, plus a cron-based ipset updater fed by WAYSCloud API for proactive threat blocking.