Skip to content

Instantly share code, notes, and snippets.

@benlacey57
Created April 26, 2025 23:27
Show Gist options
  • Select an option

  • Save benlacey57/46a6f279989b39faf7fa841f8b212d35 to your computer and use it in GitHub Desktop.

Select an option

Save benlacey57/46a6f279989b39faf7fa841f8b212d35 to your computer and use it in GitHub Desktop.
Analyse Server Logs and Automatically Block IPs
#!/bin/bash
# Configuration
LOG_FILE="/var/log/apache2/access.log"
ERROR_LOG="/var/log/apache2/error.log"
HTACCESS_FILE="/var/www/html/.htaccess"
BLOCK_LOG="/var/log/ip_blocks.log"
DEFAULT_BLOCK_DAYS=7
MAX_FAILED_ATTEMPTS=5
THRESHOLD_PERIOD="10 minutes"
# Ensure block log exists
touch "$BLOCK_LOG"
# Function to add IP to .htaccess with expiration date
block_ip() {
local ip=$1
local days=${2:-$DEFAULT_BLOCK_DAYS}
local expiry=$(date -d "+$days days" +"%Y-%m-%d")
# Check if IP is already blocked
if grep -q "Deny from $ip # EXPIRES:" "$HTACCESS_FILE"; then
echo "IP $ip is already blocked."
return
fi
# Add IP to .htaccess with expiration date
echo "Deny from $ip # EXPIRES: $expiry" >> "$HTACCESS_FILE"
# Log the block
echo "$(date +"%Y-%m-%d %H:%M:%S") - BLOCKED: $ip until $expiry" >> "$BLOCK_LOG"
echo "Blocked IP $ip until $expiry"
}
# Function to remove expired blocks
remove_expired_blocks() {
local today=$(date +"%Y-%m-%d")
local temp_file=$(mktemp)
# Create a temporary file without expired blocks
grep -v "# EXPIRES: " "$HTACCESS_FILE" > "$temp_file" || true
while IFS= read -r line; do
if [[ $line == *"# EXPIRES:"* ]]; then
expiry_date=$(echo "$line" | grep -o "EXPIRES: [0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}" | cut -d' ' -f2)
ip=$(echo "$line" | grep -o "Deny from [0-9.]*" | cut -d' ' -f3)
# Compare dates
if [[ $(date -d "$today" +%s) -le $(date -d "$expiry_date" +%s) ]]; then
# Not expired, keep it
echo "$line" >> "$temp_file"
else
# Expired, log removal
echo "$(date +"%Y-%m-%d %H:%M:%S") - UNBLOCKED: $ip (expired)" >> "$BLOCK_LOG"
echo "Removed expired block for IP $ip"
fi
else
echo "$line" >> "$temp_file"
fi
done < <(grep "# EXPIRES: " "$HTACCESS_FILE")
# Replace .htaccess with updated content
cat "$temp_file" > "$HTACCESS_FILE"
rm "$temp_file"
}
# Function to detect suspicious activity in logs
detect_suspicious_activity() {
# Look for 404 errors that exceed threshold
echo "Scanning for excessive 404 errors..."
grep "\" 404 " "$LOG_FILE" | awk '{print $1}' | sort | uniq -c | sort -nr | while read count ip; do
if [ "$count" -ge "$MAX_FAILED_ATTEMPTS" ]; then
echo "Detected $count 404 errors from IP $ip"
block_ip "$ip" "$DEFAULT_BLOCK_DAYS"
fi
done
# Look for failed login attempts
echo "Scanning for failed login attempts..."
grep "Failed login" "$ERROR_LOG" | awk '{print $(NF-3)}' | sort | uniq -c | sort -nr | while read count ip; do
if [ "$count" -ge "$MAX_FAILED_ATTEMPTS" ]; then
echo "Detected $count failed login attempts from IP $ip"
block_ip "$ip" "$DEFAULT_BLOCK_DAYS"
fi
done
# Look for SQL injection attempts
echo "Scanning for SQL injection attempts..."
grep -E "SELECT|UNION|INSERT|DROP|UPDATE.*FROM" "$LOG_FILE" | awk '{print $1}' | sort | uniq | while read ip; do
echo "Detected potential SQL injection attempt from IP $ip"
block_ip "$ip" "$DEFAULT_BLOCK_DAYS"
done
# Look for excessive requests in short time period
echo "Scanning for rate limiting violations..."
last_period=$(date -d "-$THRESHOLD_PERIOD" +"%Y:%H:%M:%S")
awk -v start_time="$last_period" '$4 >= start_time {print $1}' "$LOG_FILE" | sort | uniq -c | sort -nr | while read count ip; do
if [ "$count" -ge 100 ]; then # 100 requests in threshold period
echo "Rate limit exceeded: $count requests from IP $ip"
block_ip "$ip" "$DEFAULT_BLOCK_DAYS"
fi
done
}
# Process command line arguments
if [ "$1" == "--help" ] || [ "$1" == "-h" ]; then
echo "Usage: $0 [OPTIONS]"
echo "Options:"
echo " --block IP DAYS Block specified IP for DAYS days"
echo " --unblock IP Remove block for specified IP"
echo " --list List all currently blocked IPs"
echo " --scan Scan logs for suspicious activity"
echo " --clean Remove expired blocks"
echo " --help Show this help message"
exit 0
fi
# Create the block section in .htaccess if it doesn't exist
if ! grep -q "# BEGIN IP BLOCKS" "$HTACCESS_FILE"; then
# Backup existing .htaccess
cp "$HTACCESS_FILE" "${HTACCESS_FILE}.bak"
# Add block section
echo "" >> "$HTACCESS_FILE"
echo "# BEGIN IP BLOCKS" >> "$HTACCESS_FILE"
echo "order allow,deny" >> "$HTACCESS_FILE"
echo "allow from all" >> "$HTACCESS_FILE"
echo "# END IP BLOCKS" >> "$HTACCESS_FILE"
fi
case "$1" in
--block)
if [ -z "$2" ]; then
echo "Error: IP address required"
exit 1
fi
block_days=${3:-$DEFAULT_BLOCK_DAYS}
block_ip "$2" "$block_days"
;;
--unblock)
if [ -z "$2" ]; then
echo "Error: IP address required"
exit 1
fi
sed -i "/Deny from $2 # EXPIRES:/d" "$HTACCESS_FILE"
echo "$(date +"%Y-%m-%d %H:%M:%S") - MANUAL UNBLOCK: $2" >> "$BLOCK_LOG"
echo "Unblocked IP $2"
;;
--list)
echo "Currently blocked IPs:"
grep "Deny from" "$HTACCESS_FILE" | sed 's/Deny from \([0-9.]*\) # EXPIRES: \(.*\)/\1 until \2/'
;;
--scan)
detect_suspicious_activity
;;
--clean)
remove_expired_blocks
;;
*)
# Default: do everything
remove_expired_blocks
detect_suspicious_activity
;;
esac
# Restart web server to apply changes
if command -v apachectl &> /dev/null; then
apachectl graceful
elif command -v systemctl &> /dev/null; then
systemctl reload apache2 || systemctl reload httpd
else
service apache2 reload || service httpd reload
fi
echo "Done. Check $BLOCK_LOG for block history."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment