Optimize IPset integration for maximum performance in live attack monitor
PROBLEM:
Live attack monitor was calling CSF unnecessarily for every block,
causing performance overhead during DDoS attacks. The code was creating
a new temporary IPset (live_monitor_$$) instead of using CSF's existing
chain_DENY IPset, resulting in:
- IPset add failures (IP already in CSF's set)
- Unnecessary CSF fallback calls
- Slower blocking due to CSF overhead
- Duplicate blocking attempts
ROOT CAUSE:
Lines 68-86: Created unique per-process IPset instead of detecting/using
CSF's existing chain_DENY IPset
THE FIX:
1. Smart IPset Detection (lines 67-103):
✓ Detects CSF's chain_DENY IPset FIRST (preferred)
✓ Uses chain_DENY directly if found
✓ Falls back to temporary live_monitor_$$ if no CSF
✓ Auto-detects timeout support capability
✓ Never destroys CSF's permanent IPset on cleanup (line 141)
2. Aggressive IPset Prioritization (lines 855-911):
block_ip_temporary():
✓ ALWAYS tries IPset first if available
✓ Uses -exist flag to handle duplicates gracefully
✓ For CSF chain_DENY without timeout: Adds to IPset immediately,
then calls CSF in background for timeout management
✓ CSF only used as fallback if IPset unavailable
block_ip_permanent():
✓ Adds to IPset immediately for instant blocking
✓ CSF called after for persistent management
✓ Handles both timeout/no-timeout IPsets
3. Subnet Blocking Optimization (lines 2307-2320):
✓ Uses $IPSET_NAME variable instead of hardcoded "blocklist"
✓ IPset subnet block happens FIRST (instant)
✓ CSF called in background after IPset
PERFORMANCE BENEFITS:
✓ Kernel-level blocking (IPset) instead of userspace (CSF)
✓ Instant blocking during DDoS attacks
✓ No CSF overhead for every block
✓ Integrates with CSF's existing infrastructure
✓ Backward compatible (works without CSF)
TESTED:
✓ Bash syntax validation passed
✓ Files synced (main + v2)
✓ All blocking paths prioritize IPset
This commit is contained in:
@@ -65,21 +65,38 @@ echo "0" > "$TEMP_DIR/event_counter"
|
||||
echo "0" > "$TEMP_DIR/total_blocks"
|
||||
|
||||
# IPset configuration
|
||||
IPSET_NAME="live_monitor_$$"
|
||||
IPSET_NAME=""
|
||||
IPSET_AVAILABLE=0
|
||||
IPSET_SUPPORTS_TIMEOUT=0
|
||||
|
||||
# Initialize IPset for fast blocking (if available)
|
||||
if command -v ipset &>/dev/null; then
|
||||
# Create temporary IPset with 1-hour default timeout
|
||||
if ipset create "$IPSET_NAME" hash:ip timeout 3600 maxelem 65536 2>/dev/null; then
|
||||
# Check if CSF's chain_DENY IPset exists (preferred - already integrated with CSF)
|
||||
if ipset list chain_DENY &>/dev/null 2>&1; then
|
||||
IPSET_NAME="chain_DENY"
|
||||
IPSET_AVAILABLE=1
|
||||
|
||||
# Add iptables rule to block IPs in the set
|
||||
iptables -I INPUT -m set --match-set "$IPSET_NAME" src -j DROP 2>/dev/null
|
||||
|
||||
echo "✓ IPset initialized: $IPSET_NAME (fast blocking enabled)" >> "$TEMP_DIR/debug.log"
|
||||
# Check if chain_DENY supports timeouts
|
||||
if ipset list chain_DENY | grep -q "^Type:.*timeout"; then
|
||||
IPSET_SUPPORTS_TIMEOUT=1
|
||||
echo "✓ Using CSF IPset: chain_DENY (with timeout support)" >> "$TEMP_DIR/debug.log"
|
||||
else
|
||||
echo "✓ Using CSF IPset: chain_DENY (no timeout support, will use CSF for temp blocks)" >> "$TEMP_DIR/debug.log"
|
||||
fi
|
||||
else
|
||||
echo "✗ IPset creation failed - falling back to CSF" >> "$TEMP_DIR/debug.log"
|
||||
# No CSF IPset found, create our own temporary one
|
||||
IPSET_NAME="live_monitor_$$"
|
||||
if ipset create "$IPSET_NAME" hash:ip timeout 3600 maxelem 65536 2>/dev/null; then
|
||||
IPSET_AVAILABLE=1
|
||||
IPSET_SUPPORTS_TIMEOUT=1
|
||||
|
||||
# Add iptables rule to block IPs in the set
|
||||
iptables -I INPUT -m set --match-set "$IPSET_NAME" src -j DROP 2>/dev/null
|
||||
|
||||
echo "✓ IPset initialized: $IPSET_NAME (fast blocking enabled)" >> "$TEMP_DIR/debug.log"
|
||||
else
|
||||
echo "✗ IPset creation failed - falling back to CSF" >> "$TEMP_DIR/debug.log"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "✗ IPset not available - using CSF for blocking" >> "$TEMP_DIR/debug.log"
|
||||
@@ -120,8 +137,8 @@ cleanup() {
|
||||
# Wait a moment for background jobs
|
||||
sleep 1
|
||||
|
||||
# Clean up IPset and iptables rule if we created them
|
||||
if [ "$IPSET_AVAILABLE" -eq 1 ]; then
|
||||
# Clean up IPset and iptables rule ONLY if we created them (not CSF's chain_DENY)
|
||||
if [ "$IPSET_AVAILABLE" -eq 1 ] && [ "$IPSET_NAME" != "chain_DENY" ]; then
|
||||
echo "Removing IPset firewall rules..."
|
||||
iptables -D INPUT -m set --match-set "$IPSET_NAME" src -j DROP 2>/dev/null
|
||||
ipset destroy "$IPSET_NAME" 2>/dev/null
|
||||
@@ -847,33 +864,41 @@ block_ip_temporary() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Use IPset for instant blocking if available
|
||||
# PRIORITY 1: Use IPset for instant kernel-level blocking (performance critical)
|
||||
if [ "$IPSET_AVAILABLE" -eq 1 ]; then
|
||||
echo "Blocking $ip for ${hours}h: $reason"
|
||||
if ipset add "$IPSET_NAME" "$ip" timeout "$seconds" 2>/dev/null; then
|
||||
echo "✓ $ip blocked via IPset (auto-expires in ${hours}h)"
|
||||
echo "$ip" >> "$TEMP_DIR/blocked_ips_cache"
|
||||
|
||||
# Update counter atomically
|
||||
increment_block_counter 1
|
||||
|
||||
return 0
|
||||
# Try IPset with timeout if supported
|
||||
if [ "$IPSET_SUPPORTS_TIMEOUT" -eq 1 ]; then
|
||||
if ipset add "$IPSET_NAME" "$ip" timeout "$seconds" -exist 2>/dev/null; then
|
||||
echo "✓ $ip blocked via IPset $IPSET_NAME (expires in ${hours}h)"
|
||||
echo "$ip" >> "$TEMP_DIR/blocked_ips_cache"
|
||||
increment_block_counter 1
|
||||
return 0
|
||||
fi
|
||||
else
|
||||
echo "✗ Warning: IPset add failed (IP may already be blocked)"
|
||||
return 1
|
||||
# IPset without timeout (CSF's chain_DENY) - add to IPset for instant block,
|
||||
# then let CSF manage the timeout removal
|
||||
if ipset add "$IPSET_NAME" "$ip" -exist 2>/dev/null; then
|
||||
echo "$ip" >> "$TEMP_DIR/blocked_ips_cache"
|
||||
increment_block_counter 1
|
||||
|
||||
# Let CSF manage the timeout in background (IPset already blocking)
|
||||
if command -v csf &>/dev/null; then
|
||||
csf -td "$ip" "$seconds" "$reason" >/dev/null 2>&1 &
|
||||
echo "✓ $ip blocked via IPset $IPSET_NAME (CSF managing timeout: ${hours}h)"
|
||||
else
|
||||
echo "✓ $ip blocked via IPset $IPSET_NAME (permanent - no CSF timeout)"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Fallback to CSF if IPset not available
|
||||
# FALLBACK: CSF-only blocking (slower, but still works)
|
||||
if command -v csf &>/dev/null; then
|
||||
echo "Blocking $ip for ${hours}h: $reason"
|
||||
if csf -td "$ip" "$seconds" "$reason" >/dev/null 2>&1; then
|
||||
echo "✓ $ip blocked via CSF"
|
||||
echo "✓ $ip blocked via CSF (expires in ${hours}h)"
|
||||
echo "$ip" >> "$TEMP_DIR/blocked_ips_cache"
|
||||
|
||||
# Update counter atomically
|
||||
increment_block_counter 1
|
||||
|
||||
return 0
|
||||
else
|
||||
echo "✗ Warning: CSF block failed for $ip"
|
||||
@@ -881,7 +906,7 @@ block_ip_temporary() {
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "✗ Error: CSF not available"
|
||||
echo "✗ Error: No blocking method available"
|
||||
return 1
|
||||
}
|
||||
|
||||
@@ -911,13 +936,18 @@ block_ip_permanent() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Permanent blocks always use CSF (IPset is temp-only for this script)
|
||||
# But we can add to IPset with max timeout as well for immediate effect
|
||||
# PRIORITY: Add to IPset immediately for instant kernel-level blocking
|
||||
if [ "$IPSET_AVAILABLE" -eq 1 ]; then
|
||||
# Add to IPset with 24-hour timeout for immediate blocking
|
||||
ipset add "$IPSET_NAME" "$ip" timeout 86400 2>/dev/null
|
||||
if [ "$IPSET_SUPPORTS_TIMEOUT" -eq 1 ]; then
|
||||
# IPset with timeout - use max timeout (24 hours)
|
||||
ipset add "$IPSET_NAME" "$ip" timeout 86400 -exist 2>/dev/null
|
||||
else
|
||||
# IPset without timeout (CSF's chain_DENY) - permanent add
|
||||
ipset add "$IPSET_NAME" "$ip" -exist 2>/dev/null
|
||||
fi
|
||||
fi
|
||||
|
||||
# CSF for persistent management (runs after IPset for immediate effect)
|
||||
if command -v csf &>/dev/null; then
|
||||
echo "Permanently blocking $ip: $reason"
|
||||
if csf -d "$ip" "$reason" >/dev/null 2>&1; then
|
||||
@@ -2279,16 +2309,16 @@ monitor_network_attacks() {
|
||||
for subnet in "${!hostile_subnets[@]}"; do
|
||||
local subnet_ip_count=${hostile_subnets[$subnet]}
|
||||
if [ "$subnet_ip_count" -ge 10 ]; then
|
||||
# Block entire /24 subnet via IPset
|
||||
# Block entire /24 subnet via IPset (PRIORITY) then CSF
|
||||
local subnet_cidr="${subnet}.0/24"
|
||||
if ! grep -q "^${subnet_cidr}\$" "$TEMP_DIR/blocked_subnets" 2>/dev/null; then
|
||||
echo "$subnet_cidr" >> "$TEMP_DIR/blocked_subnets"
|
||||
(
|
||||
# Add to IPset if available
|
||||
if command -v ipset &>/dev/null && ipset list blocklist &>/dev/null 2>&1; then
|
||||
ipset add blocklist "$subnet_cidr" -exist 2>/dev/null
|
||||
# PRIORITY: Add to IPset for instant kernel-level blocking
|
||||
if [ "$IPSET_AVAILABLE" -eq 1 ]; then
|
||||
ipset add "$IPSET_NAME" "$subnet_cidr" -exist 2>/dev/null
|
||||
fi
|
||||
# Also add to CSF
|
||||
# CSF for persistent management (runs in background after IPset)
|
||||
if command -v csf &>/dev/null; then
|
||||
csf -d "$subnet_cidr" "SUBNET_DDOS:${subnet_ip_count}IPs" 2>/dev/null
|
||||
fi
|
||||
|
||||
@@ -65,21 +65,38 @@ echo "0" > "$TEMP_DIR/event_counter"
|
||||
echo "0" > "$TEMP_DIR/total_blocks"
|
||||
|
||||
# IPset configuration
|
||||
IPSET_NAME="live_monitor_$$"
|
||||
IPSET_NAME=""
|
||||
IPSET_AVAILABLE=0
|
||||
IPSET_SUPPORTS_TIMEOUT=0
|
||||
|
||||
# Initialize IPset for fast blocking (if available)
|
||||
if command -v ipset &>/dev/null; then
|
||||
# Create temporary IPset with 1-hour default timeout
|
||||
if ipset create "$IPSET_NAME" hash:ip timeout 3600 maxelem 65536 2>/dev/null; then
|
||||
# Check if CSF's chain_DENY IPset exists (preferred - already integrated with CSF)
|
||||
if ipset list chain_DENY &>/dev/null 2>&1; then
|
||||
IPSET_NAME="chain_DENY"
|
||||
IPSET_AVAILABLE=1
|
||||
|
||||
# Add iptables rule to block IPs in the set
|
||||
iptables -I INPUT -m set --match-set "$IPSET_NAME" src -j DROP 2>/dev/null
|
||||
|
||||
echo "✓ IPset initialized: $IPSET_NAME (fast blocking enabled)" >> "$TEMP_DIR/debug.log"
|
||||
# Check if chain_DENY supports timeouts
|
||||
if ipset list chain_DENY | grep -q "^Type:.*timeout"; then
|
||||
IPSET_SUPPORTS_TIMEOUT=1
|
||||
echo "✓ Using CSF IPset: chain_DENY (with timeout support)" >> "$TEMP_DIR/debug.log"
|
||||
else
|
||||
echo "✓ Using CSF IPset: chain_DENY (no timeout support, will use CSF for temp blocks)" >> "$TEMP_DIR/debug.log"
|
||||
fi
|
||||
else
|
||||
echo "✗ IPset creation failed - falling back to CSF" >> "$TEMP_DIR/debug.log"
|
||||
# No CSF IPset found, create our own temporary one
|
||||
IPSET_NAME="live_monitor_$$"
|
||||
if ipset create "$IPSET_NAME" hash:ip timeout 3600 maxelem 65536 2>/dev/null; then
|
||||
IPSET_AVAILABLE=1
|
||||
IPSET_SUPPORTS_TIMEOUT=1
|
||||
|
||||
# Add iptables rule to block IPs in the set
|
||||
iptables -I INPUT -m set --match-set "$IPSET_NAME" src -j DROP 2>/dev/null
|
||||
|
||||
echo "✓ IPset initialized: $IPSET_NAME (fast blocking enabled)" >> "$TEMP_DIR/debug.log"
|
||||
else
|
||||
echo "✗ IPset creation failed - falling back to CSF" >> "$TEMP_DIR/debug.log"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "✗ IPset not available - using CSF for blocking" >> "$TEMP_DIR/debug.log"
|
||||
@@ -120,8 +137,8 @@ cleanup() {
|
||||
# Wait a moment for background jobs
|
||||
sleep 1
|
||||
|
||||
# Clean up IPset and iptables rule if we created them
|
||||
if [ "$IPSET_AVAILABLE" -eq 1 ]; then
|
||||
# Clean up IPset and iptables rule ONLY if we created them (not CSF's chain_DENY)
|
||||
if [ "$IPSET_AVAILABLE" -eq 1 ] && [ "$IPSET_NAME" != "chain_DENY" ]; then
|
||||
echo "Removing IPset firewall rules..."
|
||||
iptables -D INPUT -m set --match-set "$IPSET_NAME" src -j DROP 2>/dev/null
|
||||
ipset destroy "$IPSET_NAME" 2>/dev/null
|
||||
@@ -847,33 +864,41 @@ block_ip_temporary() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Use IPset for instant blocking if available
|
||||
# PRIORITY 1: Use IPset for instant kernel-level blocking (performance critical)
|
||||
if [ "$IPSET_AVAILABLE" -eq 1 ]; then
|
||||
echo "Blocking $ip for ${hours}h: $reason"
|
||||
if ipset add "$IPSET_NAME" "$ip" timeout "$seconds" 2>/dev/null; then
|
||||
echo "✓ $ip blocked via IPset (auto-expires in ${hours}h)"
|
||||
echo "$ip" >> "$TEMP_DIR/blocked_ips_cache"
|
||||
|
||||
# Update counter atomically
|
||||
increment_block_counter 1
|
||||
|
||||
return 0
|
||||
# Try IPset with timeout if supported
|
||||
if [ "$IPSET_SUPPORTS_TIMEOUT" -eq 1 ]; then
|
||||
if ipset add "$IPSET_NAME" "$ip" timeout "$seconds" -exist 2>/dev/null; then
|
||||
echo "✓ $ip blocked via IPset $IPSET_NAME (expires in ${hours}h)"
|
||||
echo "$ip" >> "$TEMP_DIR/blocked_ips_cache"
|
||||
increment_block_counter 1
|
||||
return 0
|
||||
fi
|
||||
else
|
||||
echo "✗ Warning: IPset add failed (IP may already be blocked)"
|
||||
return 1
|
||||
# IPset without timeout (CSF's chain_DENY) - add to IPset for instant block,
|
||||
# then let CSF manage the timeout removal
|
||||
if ipset add "$IPSET_NAME" "$ip" -exist 2>/dev/null; then
|
||||
echo "$ip" >> "$TEMP_DIR/blocked_ips_cache"
|
||||
increment_block_counter 1
|
||||
|
||||
# Let CSF manage the timeout in background (IPset already blocking)
|
||||
if command -v csf &>/dev/null; then
|
||||
csf -td "$ip" "$seconds" "$reason" >/dev/null 2>&1 &
|
||||
echo "✓ $ip blocked via IPset $IPSET_NAME (CSF managing timeout: ${hours}h)"
|
||||
else
|
||||
echo "✓ $ip blocked via IPset $IPSET_NAME (permanent - no CSF timeout)"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Fallback to CSF if IPset not available
|
||||
# FALLBACK: CSF-only blocking (slower, but still works)
|
||||
if command -v csf &>/dev/null; then
|
||||
echo "Blocking $ip for ${hours}h: $reason"
|
||||
if csf -td "$ip" "$seconds" "$reason" >/dev/null 2>&1; then
|
||||
echo "✓ $ip blocked via CSF"
|
||||
echo "✓ $ip blocked via CSF (expires in ${hours}h)"
|
||||
echo "$ip" >> "$TEMP_DIR/blocked_ips_cache"
|
||||
|
||||
# Update counter atomically
|
||||
increment_block_counter 1
|
||||
|
||||
return 0
|
||||
else
|
||||
echo "✗ Warning: CSF block failed for $ip"
|
||||
@@ -881,7 +906,7 @@ block_ip_temporary() {
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "✗ Error: CSF not available"
|
||||
echo "✗ Error: No blocking method available"
|
||||
return 1
|
||||
}
|
||||
|
||||
@@ -911,13 +936,18 @@ block_ip_permanent() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Permanent blocks always use CSF (IPset is temp-only for this script)
|
||||
# But we can add to IPset with max timeout as well for immediate effect
|
||||
# PRIORITY: Add to IPset immediately for instant kernel-level blocking
|
||||
if [ "$IPSET_AVAILABLE" -eq 1 ]; then
|
||||
# Add to IPset with 24-hour timeout for immediate blocking
|
||||
ipset add "$IPSET_NAME" "$ip" timeout 86400 2>/dev/null
|
||||
if [ "$IPSET_SUPPORTS_TIMEOUT" -eq 1 ]; then
|
||||
# IPset with timeout - use max timeout (24 hours)
|
||||
ipset add "$IPSET_NAME" "$ip" timeout 86400 -exist 2>/dev/null
|
||||
else
|
||||
# IPset without timeout (CSF's chain_DENY) - permanent add
|
||||
ipset add "$IPSET_NAME" "$ip" -exist 2>/dev/null
|
||||
fi
|
||||
fi
|
||||
|
||||
# CSF for persistent management (runs after IPset for immediate effect)
|
||||
if command -v csf &>/dev/null; then
|
||||
echo "Permanently blocking $ip: $reason"
|
||||
if csf -d "$ip" "$reason" >/dev/null 2>&1; then
|
||||
@@ -2279,16 +2309,16 @@ monitor_network_attacks() {
|
||||
for subnet in "${!hostile_subnets[@]}"; do
|
||||
local subnet_ip_count=${hostile_subnets[$subnet]}
|
||||
if [ "$subnet_ip_count" -ge 10 ]; then
|
||||
# Block entire /24 subnet via IPset
|
||||
# Block entire /24 subnet via IPset (PRIORITY) then CSF
|
||||
local subnet_cidr="${subnet}.0/24"
|
||||
if ! grep -q "^${subnet_cidr}\$" "$TEMP_DIR/blocked_subnets" 2>/dev/null; then
|
||||
echo "$subnet_cidr" >> "$TEMP_DIR/blocked_subnets"
|
||||
(
|
||||
# Add to IPset if available
|
||||
if command -v ipset &>/dev/null && ipset list blocklist &>/dev/null 2>&1; then
|
||||
ipset add blocklist "$subnet_cidr" -exist 2>/dev/null
|
||||
# PRIORITY: Add to IPset for instant kernel-level blocking
|
||||
if [ "$IPSET_AVAILABLE" -eq 1 ]; then
|
||||
ipset add "$IPSET_NAME" "$subnet_cidr" -exist 2>/dev/null
|
||||
fi
|
||||
# Also add to CSF
|
||||
# CSF for persistent management (runs in background after IPset)
|
||||
if command -v csf &>/dev/null; then
|
||||
csf -d "$subnet_cidr" "SUBNET_DDOS:${subnet_ip_count}IPs" 2>/dev/null
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user