#!/bin/bash ################################################################################ # CT_LIMIT Optimizer - Intelligent Connection Limit Calculator ################################################################################ # Purpose: Analyze real traffic patterns to recommend optimal CT_LIMIT # Method: # 1. Analyze Apache logs for legitimate concurrent connection patterns # 2. Check current active connections per IP # 3. Identify CDNs, bots, and legitimate high-traffic sources # 4. Calculate safe CT_LIMIT that won't block real users # 5. Provide CSF configuration recommendations ################################################################################ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" source "$SCRIPT_DIR/lib/common-functions.sh" source "$SCRIPT_DIR/lib/system-detect.sh" source "$SCRIPT_DIR/lib/bot-signatures.sh" source "$SCRIPT_DIR/lib/reference-db.sh" # Require root if [ "$EUID" -ne 0 ]; then print_error "This script must be run as root" exit 1 fi # Analysis configuration ANALYSIS_HOURS=${1:-24} # Default: analyze last 24 hours TEMP_ANALYSIS="/tmp/ct-limit-analysis-$$" mkdir -p "$TEMP_ANALYSIS" # Color definitions BOLD='\033[1m' DIM='\033[2m' NC='\033[0m' ################################################################################ # Functions ################################################################################ cleanup() { rm -rf "$TEMP_ANALYSIS" 2>/dev/null } trap cleanup EXIT get_current_ct_limit() { if [ -f "/etc/csf/csf.conf" ]; then grep "^CT_LIMIT" /etc/csf/csf.conf | cut -d'=' -f2 | tr -d '"' | tr -d ' ' else echo "Not configured" fi } detect_cdn_usage() { local domain="$1" # Check DNS for CDN providers local dns_result=$(dig +short "$domain" 2>/dev/null | head -5) # Check for common CDN patterns if echo "$dns_result" | grep -qiE "(cloudflare|cdn77|akamai|fastly|cloudfront|sucuri)"; then echo "yes" return fi # Check nameservers local ns=$(dig +short NS "$domain" 2>/dev/null) if echo "$ns" | grep -qiE "(cloudflare|cdn|akamai)"; then echo "yes" return fi echo "no" } detect_caching() { local doc_root="$1" local caching_score=0 # Check for Redis if systemctl is-active redis &>/dev/null || pgrep redis-server &>/dev/null; then ((caching_score+=3)) fi # Check for Memcached if systemctl is-active memcached &>/dev/null || pgrep memcached &>/dev/null; then ((caching_score+=3)) fi # Check for WordPress caching plugins if [ -d "$doc_root/wp-content/plugins" ]; then local cache_plugins=0 [ -d "$doc_root/wp-content/plugins/wp-rocket" ] && ((cache_plugins++)) [ -d "$doc_root/wp-content/plugins/w3-total-cache" ] && ((cache_plugins++)) [ -d "$doc_root/wp-content/plugins/wp-super-cache" ] && ((cache_plugins++)) [ -d "$doc_root/wp-content/plugins/litespeed-cache" ] && ((cache_plugins++)) [ -d "$doc_root/wp-content/plugins/wp-fastest-cache" ] && ((cache_plugins++)) ((caching_score+=cache_plugins)) fi # Check for .htaccess caching rules if [ -f "$doc_root/.htaccess" ]; then if grep -q "mod_expires\|mod_cache\|Cache-Control" "$doc_root/.htaccess"; then ((caching_score++)) fi fi echo "$caching_score" } detect_site_type() { local domain="$1" local doc_root="$2" # Check if WordPress if grep -q "^WP|$domain|" "$SYSREF_DB" 2>/dev/null; then echo "wordpress" return fi # Check for ecommerce indicators if [ -d "$doc_root" ]; then if [ -f "$doc_root/wp-content/plugins/woocommerce/woocommerce.php" ] || \ [ -d "$doc_root/skin/frontend" ] || \ [ -f "$doc_root/app/Mage.php" ] || \ [ -d "$doc_root/catalog" ]; then echo "ecommerce" return fi # Check for frameworks if [ -f "$doc_root/composer.json" ] || [ -d "$doc_root/vendor" ]; then echo "framework" return fi # Count PHP files to determine complexity local php_count=$(find "$doc_root" -maxdepth 3 -name "*.php" 2>/dev/null | wc -l) if [ "$php_count" -gt 50 ]; then echo "dynamic" elif [ "$php_count" -gt 5 ]; then echo "moderate" else echo "static" fi else echo "unknown" fi } calculate_site_complexity() { local domain="$1" local doc_root="$2" local site_type="$3" # Base complexity score (1-10) local complexity=1 # WordPress adds complexity if [ "$site_type" = "wordpress" ]; then # Check plugin count local wp_data=$(grep "^WP|$domain|" "$SYSREF_DB" 2>/dev/null) if [ -n "$wp_data" ]; then local plugin_count=$(echo "$wp_data" | cut -d'|' -f6) # More plugins = more concurrent connections needed complexity=$((complexity + (plugin_count / 5))) fi complexity=$((complexity + 3)) # WordPress admin/ajax fi # Ecommerce needs higher limits if [ "$site_type" = "ecommerce" ]; then complexity=$((complexity + 5)) # Shopping cart, checkout, etc. fi # Framework/dynamic sites if [ "$site_type" = "framework" ] || [ "$site_type" = "dynamic" ]; then complexity=$((complexity + 2)) fi # Check for Ajax-heavy sites (lots of .js files) if [ -d "$doc_root" ]; then local js_count=$(find "$doc_root" -maxdepth 2 -name "*.js" 2>/dev/null | wc -l) if [ "$js_count" -gt 20 ]; then complexity=$((complexity + 2)) # Ajax-heavy = more concurrent fi fi # REDUCE complexity if good caching in place local caching_score=$(detect_caching "$doc_root") if [ "$caching_score" -gt 0 ]; then # Good caching reduces connection needs local cache_reduction=$((caching_score / 2)) complexity=$((complexity - cache_reduction)) [ "$complexity" -lt 1 ] && complexity=1 fi # REDUCE complexity if CDN is in use local has_cdn=$(detect_cdn_usage "$domain") if [ "$has_cdn" = "yes" ]; then # CDN handles static assets, reduces direct connections complexity=$((complexity - 2)) [ "$complexity" -lt 1 ] && complexity=1 fi # Cap at 10 [ "$complexity" -gt 10 ] && complexity=10 echo "$complexity" } analyze_per_site_traffic() { print_status "Analyzing per-site traffic patterns..." # Create per-site analysis file echo "DOMAIN|SITE_TYPE|COMPLEXITY|MAX_CONN|AVG_CONN|UNIQUE_IPS|TOTAL_REQUESTS" > "$TEMP_ANALYSIS/per_site_analysis.txt" # Get all active domains from sysref grep "^DOMAIN|" "$SYSREF_DB" 2>/dev/null | while IFS='|' read -r _ domain owner doc_root log_path php_ver is_primary relation target status_code status_text health; do # Skip aliases and unknowns [ "$owner" = "unknown" ] && continue [ "$is_primary" = "no" ] && continue [ -z "$log_path" ] && continue [ ! -f "$log_path" ] && continue # Detect site type local site_type=$(detect_site_type "$domain" "$doc_root") local complexity=$(calculate_site_complexity "$domain" "$doc_root" "$site_type") # Analyze traffic for this specific domain local max_conn=0 local total_ips=0 local total_requests=0 if [ -f "$TEMP_ANALYSIS/connections_by_ip.txt" ]; then # Get stats for this domain local domain_data=$(grep "|$domain|" "$TEMP_ANALYSIS/connections_by_ip.txt") if [ -n "$domain_data" ]; then max_conn=$(echo "$domain_data" | cut -d'|' -f3 | sort -rn | head -1) total_ips=$(echo "$domain_data" | cut -d'|' -f1 | sort -u | wc -l) total_requests=$(echo "$domain_data" | cut -d'|' -f4 | awk 'BEGIN {s=0} {s+=$1} END {print s}') fi fi # Calculate average connections local avg_conn=0 if [ "$total_ips" -gt 0 ]; then avg_conn=$((total_requests / total_ips)) fi echo "$domain|$site_type|$complexity|${max_conn:-0}|${avg_conn:-0}|${total_ips:-0}|${total_requests:-0}" >> "$TEMP_ANALYSIS/per_site_analysis.txt" done print_success "Per-site analysis complete" } check_server_resources() { print_status "Checking server resources..." # Get total RAM local total_ram_mb=$(free -m | awk '/^Mem:/{print $2}') # Get CPU cores local cpu_cores=$(nproc 2>/dev/null || echo "2") # Calculate max safe connections based on resources # Rule of thumb: ~1MB RAM per connection, reserve 50% for other processes local max_conn_by_ram=$((total_ram_mb / 2)) # Also factor in CPU (roughly 50 connections per core max) local max_conn_by_cpu=$((cpu_cores * 50)) # Take the lower of the two local max_safe_conn=$max_conn_by_ram [ "$max_conn_by_cpu" -lt "$max_safe_conn" ] && max_safe_conn=$max_conn_by_cpu echo "$total_ram_mb|$cpu_cores|$max_safe_conn" > "$TEMP_ANALYSIS/server_resources.txt" print_success "Server: ${total_ram_mb}MB RAM, ${cpu_cores} cores, max safe connections: ${max_safe_conn}" } analyze_apache_logs() { local hours="$1" local cutoff_time=$(date -d "$hours hours ago" "+%d/%b/%Y:%H:%M:%S" 2>/dev/null) print_status "Analyzing Apache access logs (last $hours hours)..." # Use system-detected log directory (no fallback - rely on system-detect.sh) local log_dir="${SYS_LOG_DIR}" local total_logs=0 if [ -z "$log_dir" ] || [ ! -d "$log_dir" ]; then print_warning "Apache log directory not found or not detected: $log_dir" print_warning "System detection may have failed. Check SYS_LOG_DIR." return 1 fi # Analyze each domain's access patterns echo "IP|DOMAIN|MAX_CONCURRENT|TOTAL_REQUESTS|USER_AGENT" > "$TEMP_ANALYSIS/connections_by_ip.txt" # Track hourly patterns > "$TEMP_ANALYSIS/hourly_traffic.txt" find "$log_dir" -type f \( -name "*.com" -o -name "*.net" -o -name "*.org" -o -name "*.dev" \) 2>/dev/null | while read -r logfile; do local domain=$(basename "$logfile") ((total_logs++)) # Extract IP, timestamp, user agent awk -v domain="$domain" '{ # Parse: IP - - [timestamp] "METHOD URL" status bytes "ref" "UA" match($0, /^([0-9.]+).*\[([^\]]+)\].*"([^"]*)".*"([^"]*)"$/, arr) if (arr[1] != "") { ip = arr[1] timestamp = arr[2] ua = arr[4] # Extract hour for time-of-day analysis match(timestamp, /([0-9]{2}):([0-9]{2}):/, time_arr) hour = time_arr[1] if (hour != "") { hourly_count[hour]++ } # Track requests per second per IP gsub(/:.*/, "", timestamp) # Remove time, keep date key = ip "|" domain "|" timestamp count[key]++ user_agent[ip] = ua } } END { # Output hourly traffic patterns for (h in hourly_count) { print h "|" hourly_count[h] >> "/tmp/ct-limit-analysis-$$/hourly_traffic.txt" } for (key in count) { split(key, parts, "|") ip = parts[1] dom = parts[2] # Track max concurrent requests if (count[key] > max_concurrent[ip "|" dom]) { max_concurrent[ip "|" dom] = count[key] } total_requests[ip "|" dom] += count[key] } for (key in max_concurrent) { split(key, parts, "|") ip = parts[1] dom = parts[2] print ip "|" dom "|" max_concurrent[key] "|" total_requests[key] "|" user_agent[ip] } }' "$logfile" >> "$TEMP_ANALYSIS/connections_by_ip.txt" 2>/dev/null done print_success "Analyzed logs from $log_dir" } analyze_current_connections() { print_status "Analyzing current active connections..." if command -v ss &>/dev/null; then # Count current connections per IP ss -tn state established 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | sort | uniq -c | sort -rn > "$TEMP_ANALYSIS/current_connections.txt" elif command -v netstat &>/dev/null; then netstat -tn | grep ESTABLISHED | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn > "$TEMP_ANALYSIS/current_connections.txt" else print_warning "Neither ss nor netstat available" return 1 fi print_success "Current connection analysis complete" } classify_ip_behavior() { local ip="$1" local user_agent="$2" local max_concurrent="$3" local total_requests="$4" # Classify using bot signatures local bot_type=$(classify_bot_type "$user_agent") # Additional classification local classification="unknown" # Known good sources if [ "$bot_type" = "legit" ]; then classification="legitimate_bot" elif [ "$bot_type" = "ai" ]; then classification="ai_crawler" elif [ "$bot_type" = "monitor" ]; then classification="monitoring_service" # CDN detection elif [[ "$user_agent" =~ (Cloudflare|CloudFront|Akamai|Fastly|Sucuri) ]]; then classification="cdn" # High request rate but legitimate elif [ "$max_concurrent" -gt 50 ] && [ "$total_requests" -gt 1000 ]; then if [[ "$user_agent" =~ (Chrome|Firefox|Safari|Edge) ]]; then classification="high_traffic_user" else classification="potential_scraper" fi # Normal user elif [ "$max_concurrent" -lt 20 ]; then classification="normal_user" else classification="moderate_user" fi echo "$classification" } calculate_percentile() { local percentile="$1" local data_file="$2" # Sort data and get Nth percentile local count=$(wc -l < "$data_file") local position=$(awk -v p="$percentile" -v c="$count" 'BEGIN {printf "%.0f", (p/100) * c}') [ "$position" -lt 1 ] && position=1 sort -n "$data_file" | sed -n "${position}p" } generate_recommendation() { print_banner "CT_LIMIT Optimizer - Analysis Results" echo "" # Parse analysis data local max_legitimate=0 local max_bot=0 local max_cdn=0 local total_ips=0 echo "Analyzing connection patterns..." echo "" # Create summary files > "$TEMP_ANALYSIS/legitimate_connections.txt" > "$TEMP_ANALYSIS/bot_connections.txt" > "$TEMP_ANALYSIS/all_connections.txt" # Skip header tail -n +2 "$TEMP_ANALYSIS/connections_by_ip.txt" | while IFS='|' read -r ip domain max_concurrent total_requests user_agent; do [ -z "$ip" ] && continue local classification=$(classify_ip_behavior "$ip" "$user_agent" "$max_concurrent" "$total_requests") ((total_ips++)) # Track max connections by type case "$classification" in legitimate_bot|ai_crawler|monitoring_service|cdn) echo "$max_concurrent" >> "$TEMP_ANALYSIS/bot_connections.txt" [ "$max_concurrent" -gt "$max_bot" ] && max_bot=$max_concurrent ;; normal_user|moderate_user|high_traffic_user) echo "$max_concurrent" >> "$TEMP_ANALYSIS/legitimate_connections.txt" [ "$max_concurrent" -gt "$max_legitimate" ] && max_legitimate=$max_concurrent ;; esac echo "$max_concurrent" >> "$TEMP_ANALYSIS/all_connections.txt" done # Calculate statistics local legit_count=$(wc -l < "$TEMP_ANALYSIS/legitimate_connections.txt" 2>/dev/null || echo "0") local bot_count=$(wc -l < "$TEMP_ANALYSIS/bot_connections.txt" 2>/dev/null || echo "0") echo -e "${BOLD}Connection Analysis Summary:${NC}" echo "──────────────────────────────────────────────────────────────" echo " Total unique IPs analyzed: $total_ips" echo " Legitimate users: $legit_count" echo " Bots/CDNs/Crawlers: $bot_count" echo "" # Show per-site analysis if [ -f "$TEMP_ANALYSIS/per_site_analysis.txt" ]; then local site_count=$(tail -n +2 "$TEMP_ANALYSIS/per_site_analysis.txt" | wc -l) if [ "$site_count" -gt 0 ]; then echo -e "${BOLD}Per-Site Analysis (All $site_count Sites Checked):${NC}" echo "──────────────────────────────────────────────────────────────" printf " %-30s %-12s %5s %8s %8s\n" "DOMAIN" "TYPE" "CMPLX" "MAX_CONN" "UNIQ_IPs" echo " $(printf '─%.0s' {1..70})" tail -n +2 "$TEMP_ANALYSIS/per_site_analysis.txt" | sort -t'|' -k4 -rn | head -15 | while IFS='|' read -r domain site_type complexity max_conn avg_conn unique_ips total_requests; do # Truncate long domain names local short_domain=$(echo "$domain" | cut -c1-28) [ ${#domain} -gt 28 ] && short_domain="${short_domain}.." # Color code by complexity local color="${NC}" if [ "$complexity" -ge 7 ]; then color="${HIGH_COLOR}" elif [ "$complexity" -ge 4 ]; then color="${MEDIUM_COLOR}" fi printf " ${color}%-30s %-12s %5s %8s %8s${NC}\n" \ "$short_domain" "$site_type" "$complexity" "$max_conn" "$unique_ips" done local remaining=$((site_count - 15)) if [ "$remaining" -gt 0 ]; then echo " ... and $remaining more sites analyzed" fi echo "" # Identify high-complexity sites that need extra headroom local high_complexity_sites=$(tail -n +2 "$TEMP_ANALYSIS/per_site_analysis.txt" | awk -F'|' '$3 >= 7 {print $1}' | wc -l) if [ "$high_complexity_sites" -gt 0 ]; then echo -e "${MEDIUM_COLOR} ⚠️ $high_complexity_sites high-complexity sites detected${NC}" echo " (WordPress/Ecommerce/Framework - need higher CT_LIMIT)" echo "" fi fi fi # Calculate percentiles for legitimate traffic if [ -s "$TEMP_ANALYSIS/legitimate_connections.txt" ]; then local p95=$(calculate_percentile 95 "$TEMP_ANALYSIS/legitimate_connections.txt") local p99=$(calculate_percentile 99 "$TEMP_ANALYSIS/legitimate_connections.txt") echo -e "${BOLD}Legitimate User Connection Patterns:${NC}" echo " Max concurrent from single IP: $max_legitimate" echo " 95th percentile: $p95 concurrent connections" echo " 99th percentile: $p99 concurrent connections" echo "" fi # Check current connections if [ -s "$TEMP_ANALYSIS/current_connections.txt" ]; then local current_max=$(head -1 "$TEMP_ANALYSIS/current_connections.txt" | awk '{print $1}') local current_max_ip=$(head -1 "$TEMP_ANALYSIS/current_connections.txt" | awk '{print $2}') echo -e "${BOLD}Current Active Connections:${NC}" echo " Highest right now: $current_max connections from $current_max_ip" echo "" fi # Server resource limits if [ -f "$TEMP_ANALYSIS/server_resources.txt" ]; then IFS='|' read -r total_ram cpu_cores max_safe_conn < "$TEMP_ANALYSIS/server_resources.txt" echo -e "${BOLD}Server Resource Limits:${NC}" echo " RAM: ${total_ram}MB | CPU: ${cpu_cores} cores" echo " Max safe connections (hardware): $max_safe_conn" echo "" fi # Time-of-day patterns if [ -f "$TEMP_ANALYSIS/hourly_traffic.txt" ]; then local peak_hour=$(awk -F'|' '{if($2>max){max=$2; hour=$1}} END{print hour}' "$TEMP_ANALYSIS/hourly_traffic.txt") local peak_requests=$(awk -F'|' '{if($2>max){max=$2}} END{print max}' "$TEMP_ANALYSIS/hourly_traffic.txt") local avg_requests=$(awk -F'|' '{sum+=$2; count++} END{if(count>0) print int(sum/count)}' "$TEMP_ANALYSIS/hourly_traffic.txt") if [ -n "$peak_hour" ] && [ "$peak_requests" -gt "$((avg_requests * 2))" ]; then echo -e "${BOLD}Traffic Patterns:${NC}" echo " Peak hour: ${peak_hour}:00 (${peak_requests} requests)" echo " Average: ${avg_requests} requests/hour" echo " Peak is ${MEDIUM_COLOR}$((peak_requests * 100 / avg_requests))% above average${NC}" echo " ${DIM}→ CT_LIMIT should handle peak, not average${NC}" echo "" fi fi # Optimization opportunities local opt_count=0 echo -e "${BOLD}Optimization Opportunities:${NC}" # Check for sites without CDN local no_cdn=$(tail -n +2 "$TEMP_ANALYSIS/per_site_analysis.txt" 2>/dev/null | while IFS='|' read -r domain site_type complexity max_conn avg_conn unique_ips total_requests; do if [ "$complexity" -ge 6 ]; then local has_cdn=$(detect_cdn_usage "$domain" 2>/dev/null) if [ "$has_cdn" = "no" ]; then echo "$domain" ((opt_count++)) fi fi done | head -3) if [ -n "$no_cdn" ]; then echo " 📦 CDN Recommended for:" echo "$no_cdn" | while read -r domain; do echo " • $domain (would reduce CT_LIMIT need)" done fi # Check for WordPress without caching local no_cache=$(tail -n +2 "$TEMP_ANALYSIS/per_site_analysis.txt" 2>/dev/null | grep "wordpress" | while IFS='|' read -r domain site_type complexity max_conn avg_conn unique_ips total_requests; do # Get doc root from sysref local doc_root=$(grep "^DOMAIN|$domain|" "$SYSREF_DB" 2>/dev/null | cut -d'|' -f4) if [ -n "$doc_root" ]; then local cache_score=$(detect_caching "$doc_root" 2>/dev/null) if [ "$cache_score" -eq 0 ]; then echo "$domain" fi fi done | head -3) if [ -n "$no_cache" ]; then echo " ⚡ Caching Recommended for:" echo "$no_cache" | while read -r domain; do echo " • $domain (WP Rocket, Redis, or W3 Total Cache)" done fi if [ -z "$no_cdn" ] && [ -z "$no_cache" ]; then echo " ✅ Sites are well-optimized (CDN + caching in place)" fi echo "" # Current CSF setting local current_ct=$(get_current_ct_limit) echo -e "${BOLD}Current CSF Configuration:${NC}" echo " CT_LIMIT = $current_ct" echo "" # Generate recommendation echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo -e "${BOLD}📊 RECOMMENDED CT_LIMIT VALUES${NC}" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" # Calculate safe recommendations local conservative=$((max_legitimate + 20)) local balanced=$((max_legitimate + 10)) local aggressive=$((max_legitimate + 5)) # Factor in site complexity - high-complexity sites need more headroom if [ -f "$TEMP_ANALYSIS/per_site_analysis.txt" ]; then local avg_complexity=$(tail -n +2 "$TEMP_ANALYSIS/per_site_analysis.txt" | awk -F'|' '{sum+=$3; count++} END {if(count>0) print int(sum/count); else print 0}') local max_complexity=$(tail -n +2 "$TEMP_ANALYSIS/per_site_analysis.txt" | awk -F'|' '{if($3>max) max=$3} END {print max+0}') # Add complexity buffer (0-20 based on average complexity) local complexity_buffer=$((avg_complexity * 2)) conservative=$((conservative + complexity_buffer)) balanced=$((balanced + (complexity_buffer / 2))) # If we have ecommerce sites, be extra conservative local has_ecommerce=$(tail -n +2 "$TEMP_ANALYSIS/per_site_analysis.txt" | grep -c "ecommerce") if [ "$has_ecommerce" -gt 0 ]; then conservative=$((conservative + 15)) balanced=$((balanced + 10)) fi fi # Minimum safety thresholds [ "$conservative" -lt 100 ] && conservative=100 [ "$balanced" -lt 80 ] && balanced=80 [ "$aggressive" -lt 50 ] && aggressive=50 # Cap at server's max safe capacity if [ -f "$TEMP_ANALYSIS/server_resources.txt" ]; then IFS='|' read -r total_ram cpu_cores max_safe_conn < "$TEMP_ANALYSIS/server_resources.txt" if [ "$conservative" -gt "$max_safe_conn" ]; then conservative=$max_safe_conn echo " ${MEDIUM_COLOR}Note: Conservative capped at server max ($max_safe_conn)${NC}" >&2 fi if [ "$balanced" -gt "$max_safe_conn" ]; then balanced=$max_safe_conn fi fi echo -e "${BOLD}1. CONSERVATIVE${NC} (Recommended for high-traffic sites)" echo " CT_LIMIT = $conservative" echo " • Allows headroom for traffic spikes" echo " • Won't block legitimate users" echo " • Good protection against moderate attacks" echo "" echo -e "${BOLD}2. BALANCED${NC} (Recommended for most servers) ⭐" echo " CT_LIMIT = $balanced" echo " • Balances security and usability" echo " • Based on 99th percentile + buffer" echo " • Blocks most attack traffic" echo "" echo -e "${BOLD}3. AGGRESSIVE${NC} (Only if under active attack)" echo " CT_LIMIT = $aggressive" echo " • Tight connection limits" echo " • May affect some legitimate users" echo " • Maximum DDoS protection" echo "" # Whitelist recommendations if [ "$bot_count" -gt 0 ]; then echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo -e "${BOLD}⚠️ WHITELIST RECOMMENDATIONS${NC}" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo "Found bots/crawlers with high connection counts." echo "Consider whitelisting these IPs to prevent blocking:" echo "" # Show top legitimate bots tail -n +2 "$TEMP_ANALYSIS/connections_by_ip.txt" | while IFS='|' read -r ip domain max_concurrent total_requests user_agent; do [ -z "$ip" ] && continue local classification=$(classify_ip_behavior "$ip" "$user_agent" "$max_concurrent" "$total_requests") if [[ "$classification" =~ (legitimate_bot|ai_crawler|monitoring_service) ]]; then local bot_name=$(echo "$user_agent" | grep -oE '(Googlebot|Bingbot|Slurp|DuckDuckBot|Baiduspider|YandexBot|SemrushBot|AhrefsBot|facebookexternalhit|Twitterbot|LinkedInBot|UptimeRobot|Pingdom)' | head -1) [ -z "$bot_name" ] && bot_name="Bot" if [ "$max_concurrent" -gt "$balanced" ]; then printf " • %-15s (%-20s) %3d connections\n" "$ip" "$bot_name" "$max_concurrent" fi fi done | sort -t'(' -k2 -u | head -10 echo "" echo "To whitelist: Add to /etc/csf/csf.ignore or use: csf -a " fi echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo -e "${BOLD}🔧 HOW TO APPLY${NC}" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo "1. Edit CSF configuration:" echo " ${BOLD}nano /etc/csf/csf.conf${NC}" echo "" echo "2. Find CT_LIMIT line and change to recommended value:" echo " ${BOLD}CT_LIMIT = \"$balanced\"${NC}" echo "" echo "3. Enable SYNFLOOD protection (if not already):" echo " ${BOLD}SYNFLOOD = \"1\"${NC}" echo "" echo "4. Apply changes:" echo " ${BOLD}csf -r${NC}" echo "" echo "5. Monitor effectiveness:" echo " ${BOLD}watch -n 2 'csf -g | tail -20'${NC}" echo "" # Save recommendation to file cat > "$TEMP_ANALYSIS/recommendation.txt" </dev/null 2>&1 print_success "CT_LIMIT updated to $new_limit and CSF restarted!" echo "" echo "Monitor effectiveness with: csf -g | tail -20" } ################################################################################ # Main ################################################################################ main() { # Check for auto mode local AUTO_MODE=0 if [ "${1:-}" = "--auto" ] || [ "${1:-}" = "-a" ]; then AUTO_MODE=1 fi if [ "${AUTO_MODE:-0}" -eq 0 ]; then clear print_banner "CT_LIMIT Optimizer - Intelligent Connection Limit Calculator" echo "" echo "This tool analyzes your actual traffic patterns to recommend" echo "an optimal CT_LIMIT that protects against DDoS without blocking" echo "legitimate users, bots, and CDNs." echo "" echo "Analysis period: Last $ANALYSIS_HOURS hours" echo "" read -p "Press Enter to start analysis or Ctrl+C to cancel..." echo "" else echo "Running CT_LIMIT analysis in auto mode..." echo "" fi # Check if sysref database exists, build if needed if [ ! -f "$SYSREF_DB" ] || [ ! -s "$SYSREF_DB" ]; then print_status "Building system reference database (first run)..." build_reference_database >/dev/null 2>&1 fi # Run analysis check_server_resources analyze_apache_logs "$ANALYSIS_HOURS" analyze_per_site_traffic analyze_current_connections # Generate and show recommendations generate_recommendation # Apply automatically in auto mode, otherwise ask if [ "${AUTO_MODE:-0}" -eq 1 ]; then # Extract balanced value from recommendation local balanced=$(grep "2. BALANCED" -A1 "$TEMP_ANALYSIS/recommendation.txt" | grep "CT_LIMIT" | grep -oE '[0-9]+') if [ -n "$balanced" ]; then echo "" echo "Auto-applying BALANCED recommendation..." apply_recommendation "$balanced" else print_error "Could not determine balanced recommendation value" return 1 fi else # Offer to apply echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" read -p "Would you like to apply the BALANCED recommendation automatically? (y/n): " apply if [[ "$apply" =~ ^[Yy] ]]; then # Extract balanced value from recommendation local balanced=$(grep "2. BALANCED" -A1 "$TEMP_ANALYSIS/recommendation.txt" | grep "CT_LIMIT" | grep -oE '[0-9]+') if [ -n "$balanced" ]; then apply_recommendation "$balanced" else print_error "Could not determine balanced recommendation value" fi else echo "" echo "No changes made. You can apply manually using the commands above." fi fi echo "" if [ "${AUTO_MODE:-0}" -eq 0 ]; then print_success "Analysis complete!" fi } main "$@"