Created
April 26, 2025 23:27
-
-
Save benlacey57/46a6f279989b39faf7fa841f8b212d35 to your computer and use it in GitHub Desktop.
Analyse Server Logs and Automatically Block IPs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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