Files
Linux-Server-Management-Too…/modules/security/live-attack-monitor.sh
T
cschantz a466a9e99c Fix live monitor issues: filter local IPs, remove slow blocking check, clear corrupted snapshot
- Added local/private IP filtering (127.x, 10.x, 192.168.x, etc.)
- Removed is_ip_blocked() from quick actions (too slow, causing false 'no threats')
- Cleared old snapshot with corrupted SCAN/NONE attack types
- Now properly shows blockable IPs with score >= 60

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-13 23:10:58 -05:00

701 lines
24 KiB
Bash
Executable File

#!/bin/bash
################################################################################
# Live Network Security Monitor - ENHANCED with Intelligence
################################################################################
# Purpose: Real-time monitoring with bot intelligence and threat scoring
# Version: 2.0 - Intelligence Mode
# Features:
# - Bot classification using learned signatures
# - IP reputation DB integration
# - Real-time threat scoring (0-100)
# - Attack vector detection
# - Quick action blocking system
# - Ban tracking and history
################################################################################
# Get script directory
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/ip-reputation.sh"
source "$SCRIPT_DIR/lib/bot-signatures.sh"
source "$SCRIPT_DIR/lib/attack-patterns.sh"
# Require root
if [ "$EUID" -ne 0 ]; then
print_error "This script must be run as root"
exit 1
fi
# Color definitions for threat levels
CRITICAL_COLOR='\033[1;41;97m' # White on Red background
HIGH_COLOR='\033[1;31m' # Bold Red
MEDIUM_COLOR='\033[1;33m' # Bold Yellow
LOW_COLOR='\033[0;36m' # Cyan
SAFE_COLOR='\033[0;32m' # Green
INFO_COLOR='\033[0;37m' # White
BOLD='\033[1m' # Bold text
NC='\033[0m'
# Configuration
REFRESH_INTERVAL=2 # Seconds between dashboard refreshes
MAX_DISPLAY_LINES=20
THREAT_THRESHOLD_CRITICAL=80
THREAT_THRESHOLD_HIGH=60
THREAT_THRESHOLD_MEDIUM=40
# Temporary files for tracking
TEMP_DIR="/tmp/live-monitor-$$"
SNAPSHOT_DIR="/var/lib/server-toolkit/live-monitor"
mkdir -p "$TEMP_DIR" "$SNAPSHOT_DIR" 2>/dev/null
touch "$TEMP_DIR/recent_events"
touch "$TEMP_DIR/ip_data"
echo "0" > "$TEMP_DIR/event_counter"
# Save snapshot of IP data (for persistence across restarts)
save_snapshot() {
{
for ip in "${!IP_DATA[@]}"; do
echo "$ip=${IP_DATA[$ip]}"
done
} > "$SNAPSHOT_DIR/ip_data_snapshot" 2>/dev/null
}
# Cleanup function
cleanup() {
echo ""
echo "Stopping monitoring processes..."
# Save snapshot before exit
save_snapshot
# Kill all child processes
pkill -P $$ 2>/dev/null
# Wait a moment for background jobs
sleep 1
# Clean up temp directory
rm -rf "$TEMP_DIR" 2>/dev/null
# Restore cursor
command -v tput &>/dev/null && tput cnorm
echo "✓ Cleanup complete (snapshot saved)"
exit 0
}
trap cleanup EXIT INT TERM
# Statistics counters
declare -A IP_DATA # Stores: IP -> score|hits|bot_type|attacks|ban_count
declare -A ATTACK_TYPE_COUNTER
TOTAL_THREATS=0
START_TIME=$(date +%s)
MAX_TRACKED_IPS=500 # Prevent memory overflow
# Load persistent data from previous sessions if exists
if [ -f "$SNAPSHOT_DIR/ip_data_snapshot" ]; then
while IFS='=' read -r ip data; do
[ -n "$ip" ] && IP_DATA[$ip]="$data"
done < "$SNAPSHOT_DIR/ip_data_snapshot"
fi
# Hide cursor for cleaner display
command -v tput &>/dev/null && tput civis
################################################################################
# Intelligence Functions
################################################################################
# Get or create IP intelligence data
# Returns: score|hits|bot_type|attacks|ban_count|rep_score
get_ip_intelligence() {
local ip="$1"
# Check if we have cached data
if [ -n "${IP_DATA[$ip]}" ]; then
echo "${IP_DATA[$ip]}"
return 0
fi
# Query IP reputation database
local rep_data=$(lookup_ip "$ip" 2>/dev/null)
if [ -n "$rep_data" ]; then
# Parse: IP|HIT_COUNT|REP_SCORE|COUNTRY|ATTACK_FLAGS|...
IFS='|' read -r _ db_hits rep_score country attack_flags _ _ _ notes ban_count _ <<< "$rep_data"
# Initialize with learned data
local score=${rep_score:-0}
local hits=${db_hits:-0}
local bot_type="unknown"
local attacks=$(decode_attack_flags "$attack_flags" 2>/dev/null | tr ',' ' ' || echo "")
ban_count=${ban_count:-0}
# Cache it
IP_DATA[$ip]="$score|$hits|$bot_type|$attacks|$ban_count|$rep_score"
echo "${IP_DATA[$ip]}"
else
# New IP - initialize
IP_DATA[$ip]="0|0|unknown||0|0"
echo "${IP_DATA[$ip]}"
fi
}
# Update IP intelligence
update_ip_intelligence() {
local ip="$1"
local url="$2"
local user_agent="$3"
local method="${4:-GET}"
# Get current data
local current=$(get_ip_intelligence "$ip")
IFS='|' read -r score hits bot_type attacks ban_count rep_score <<< "$current"
# Increment hits
hits=$((hits + 1))
# Classify bot if unknown
if [ "$bot_type" = "unknown" ] && [ -n "$user_agent" ]; then
bot_type=$(classify_bot_type "$user_agent")
fi
# Detect attacks in URL
local new_attacks=$(detect_all_attacks "$url" "$method")
if [ -n "$new_attacks" ]; then
# Add to attack list (unique)
if [ -z "$attacks" ]; then
attacks="$new_attacks"
else
attacks="$attacks,$new_attacks"
fi
# Remove duplicates
attacks=$(echo "$attacks" | tr ',' '\n' | sort -u | tr '\n' ',' | sed 's/,$//')
# Update attack type counter
IFS=',' read -ra ATTACK_ARRAY <<< "$new_attacks"
for attack in "${ATTACK_ARRAY[@]}"; do
((ATTACK_TYPE_COUNTER["$attack"]++))
done
# Calculate attack score
local attack_score=$(calculate_attack_score "$new_attacks")
score=$((score + attack_score))
((TOTAL_THREATS++))
fi
# Request volume scoring
if [ $hits -gt 100 ]; then
score=$((score + 5))
elif [ $hits -gt 50 ]; then
score=$((score + 3))
elif [ $hits -gt 20 ]; then
score=$((score + 1))
fi
# Adjust score based on bot type
case "$bot_type" in
legit|ai|monitor)
# Legitimate bots - reduce score
score=$((score - 5))
[ $score -lt 0 ] && score=0
;;
suspicious)
# Suspicious bots - increase score
score=$((score + 10))
;;
esac
# Cap at 100
[ $score -gt 100 ] && score=100
# Check if we're tracking too many IPs (memory protection)
if [ ${#IP_DATA[@]} -ge $MAX_TRACKED_IPS ]; then
# Remove lowest scoring IPs
local to_remove=()
for check_ip in "${!IP_DATA[@]}"; do
local check_score=$(echo "${IP_DATA[$check_ip]}" | cut -d'|' -f1)
[ "$check_score" -lt 10 ] && to_remove+=("$check_ip")
done
# Remove up to 100 low-score IPs
local removed=0
for remove_ip in "${to_remove[@]}"; do
unset IP_DATA[$remove_ip]
((removed++))
[ $removed -ge 100 ] && break
done
fi
# Update cached data
IP_DATA[$ip]="$score|$hits|$bot_type|$attacks|$ban_count|$rep_score"
# Update IP reputation DB in background (if score > 0)
if [ $score -gt 0 ]; then
(update_ip_reputation "$ip" 1 "$score" 0 "Live monitor: $new_attacks" >/dev/null 2>&1) &
fi
}
# Get threat level from score
get_threat_level() {
local score="$1"
if [ "$score" -ge "$THREAT_THRESHOLD_CRITICAL" ]; then
echo "CRITICAL"
elif [ "$score" -ge "$THREAT_THRESHOLD_HIGH" ]; then
echo "HIGH"
elif [ "$score" -ge "$THREAT_THRESHOLD_MEDIUM" ]; then
echo "MEDIUM"
else
echo "LOW"
fi
}
# Get color for threat level
get_threat_color() {
local level="$1"
case "$level" in
CRITICAL) echo "$CRITICAL_COLOR" ;;
HIGH) echo "$HIGH_COLOR" ;;
MEDIUM) echo "$MEDIUM_COLOR" ;;
LOW) echo "$LOW_COLOR" ;;
SAFE) echo "$SAFE_COLOR" ;;
*) echo "$INFO_COLOR" ;;
esac
}
# Get bot color
get_bot_color() {
local bot_type="$1"
case "$bot_type" in
legit) echo "$SAFE_COLOR" ;;
ai) echo '\033[0;34m' ;; # Blue
monitor) echo "$MEDIUM_COLOR" ;;
suspicious) echo "$HIGH_COLOR" ;;
*) echo "$INFO_COLOR" ;;
esac
}
################################################################################
# Dashboard Display Functions
################################################################################
draw_header() {
clear
local uptime=$(($(date +%s) - START_TIME))
local uptime_str=$(printf "%02d:%02d:%02d" $((uptime/3600)) $((uptime%3600/60)) $((uptime%60)))
# Read event counter from file (updated by subshell)
local event_count=$(cat "$TEMP_DIR/event_counter" 2>/dev/null || echo "0")
echo -e "${CRITICAL_COLOR}╔════════════════════════════════════════════════════════════════════════════╗${NC}"
echo -e "${CRITICAL_COLOR}║ 🚨 LIVE SECURITY MONITOR - INTELLIGENCE MODE 🧠 ║${NC}"
echo -e "${CRITICAL_COLOR}╚════════════════════════════════════════════════════════════════════════════╝${NC}"
echo -e "${INFO_COLOR}Runtime: ${uptime_str} | Events: ${event_count} | Threats: ${TOTAL_THREATS} | Monitoring...${NC}"
echo ""
}
draw_intelligence_panel() {
echo -e "${HIGH_COLOR}┌─ THREAT INTELLIGENCE ──────────────────────────────────────────────────────┐${NC}"
# Get top IPs by threat score
local count=0
for ip in "${!IP_DATA[@]}"; do
IFS='|' read -r score hits bot_type attacks ban_count rep_score <<< "${IP_DATA[$ip]}"
echo "$score|$ip|$hits|$bot_type|$attacks|$ban_count|$rep_score"
done | sort -t'|' -k1 -rn | head -10 | while IFS='|' read -r score ip hits bot_type attacks ban_count rep_score; do
local level=$(get_threat_level "$score")
local color=$(get_threat_color "$level")
local bot_color=$(get_bot_color "$bot_type")
# Build status line
local status_line=$(printf "%-15s" "$ip")
status_line+=$(printf " Score:%-3s" "$score")
status_line+=$(printf " Hits:%-4s" "$hits")
# Bot type indicator
case "$bot_type" in
legit) status_line+=" ✅BOT" ;;
ai) status_line+=" 🤖AI" ;;
monitor) status_line+=" 📊MON" ;;
suspicious) status_line+=" ⚠️ SUS" ;;
*) status_line+="" ;;
esac
# Threat level
status_line+=$(printf " [%-8s]" "$level")
# Attacks
if [ -n "$attacks" ]; then
# Show first attack type
local first_attack=$(echo "$attacks" | cut -d',' -f1)
local icon=$(get_attack_icon "$first_attack")
status_line+=" $icon$(echo "$attacks" | cut -d',' -f1)"
fi
# Ban count
if [ "$ban_count" -gt 0 ]; then
status_line+=" 🚫x$ban_count"
fi
# Known threat indicator
if [ "$rep_score" -gt 0 ]; then
status_line+=" [KNOWN]"
fi
echo -e "${color}${status_line}${NC}"
done
echo -e "${HIGH_COLOR}└────────────────────────────────────────────────────────────────────────────┘${NC}"
echo ""
}
draw_attack_breakdown() {
echo -e "${MEDIUM_COLOR}┌─ ATTACK VECTORS ───────────────────────────────────────────────────────────┐${NC}"
if [ ${#ATTACK_TYPE_COUNTER[@]} -eq 0 ]; then
echo -e "${LOW_COLOR} No attacks detected yet...${NC}"
else
for attack_type in "${!ATTACK_TYPE_COUNTER[@]}"; do
local count="${ATTACK_TYPE_COUNTER[$attack_type]}"
local icon=$(get_attack_icon "$attack_type")
local color=$(get_attack_color "$attack_type")
printf "${color} ${icon} %-20s %5d${NC}\n" "$attack_type" "$count"
done | sort -t' ' -k3 -rn | head -5
fi
echo -e "${MEDIUM_COLOR}└────────────────────────────────────────────────────────────────────────────┘${NC}"
echo ""
}
draw_live_feed() {
echo -e "${HIGH_COLOR}┌─ LIVE THREAT FEED ─────────────────────────────────────────────────────────┐${NC}"
if [ -f "$TEMP_DIR/recent_events" ] && [ -s "$TEMP_DIR/recent_events" ]; then
tail -n "$MAX_DISPLAY_LINES" "$TEMP_DIR/recent_events"
else
echo -e "${LOW_COLOR} Waiting for events...${NC}"
fi
echo -e "${HIGH_COLOR}└────────────────────────────────────────────────────────────────────────────┘${NC}"
echo ""
}
draw_quick_actions() {
echo -e "${MEDIUM_COLOR}┌─ QUICK ACTIONS ────────────────────────────────────────────────────────────┐${NC}"
# Get blockable IPs (score >= 60, not already blocked)
local blockable_count=0
local blockable_ips=""
for ip in "${!IP_DATA[@]}"; do
IFS='|' read -r score hits bot_type attacks ban_count rep_score <<< "${IP_DATA[$ip]}"
# Skip if score too low
[ "$score" -lt 60 ] && continue
# Quick check - only verify if CSF/iptables commands available
# Don't check on every refresh (too slow)
blockable_count=$((blockable_count + 1))
blockable_ips+="$ip "
done
if [ $blockable_count -gt 0 ]; then
echo -e "${HIGH_COLOR} ⚠️ $blockable_count high-threat IPs ready to block${NC}"
echo -e "${MEDIUM_COLOR} Press 'b' to open blocking menu${NC}"
else
echo -e "${SAFE_COLOR} ✓ No immediate threats requiring blocks${NC}"
fi
echo -e "${INFO_COLOR} Press 'b' to block IPs | 'h' for help | 'q' to quit${NC}"
echo -e "${MEDIUM_COLOR}└────────────────────────────────────────────────────────────────────────────┘${NC}"
}
################################################################################
# Quick Action Menu
################################################################################
show_blocking_menu() {
# Pause monitoring
local monitoring_paused=1
clear
print_banner "Quick IP Blocking"
echo ""
echo "Select IPs to block (1-hour temporary ban):"
echo ""
# Build array of blockable IPs
local -a blockable_list=()
for ip in "${!IP_DATA[@]}"; do
IFS='|' read -r score hits bot_type attacks ban_count rep_score <<< "${IP_DATA[$ip]}"
# Skip if score too low or already blocked
[ "$score" -lt 60 ] && continue
is_ip_blocked "$ip" 2>/dev/null && continue
blockable_list+=("$ip|$score|$hits|$attacks")
done
if [ ${#blockable_list[@]} -eq 0 ]; then
echo "No IPs meet blocking criteria (score >= 60)"
echo ""
read -p "Press Enter to continue..."
return
fi
# Check if any IPs to block
if [ ${#blockable_list[@]} -eq 0 ]; then
echo ""
echo -e "${SAFE_COLOR}No IPs meet blocking criteria (score >= 60 and not already blocked)${NC}"
echo ""
read -p "Press Enter to continue..."
return
fi
# Sort by score
IFS=$'\n' blockable_list=($(sort -t'|' -k2 -rn <<<"${blockable_list[*]}"))
unset IFS
# Display IPs
local idx=1
for entry in "${blockable_list[@]}"; do
IFS='|' read -r ip score hits attacks <<< "$entry"
local level=$(get_threat_level "$score")
local color=$(get_threat_color "$level")
printf "${color} %2d) %-15s Score:%-3s Hits:%-5s Attacks: %s${NC}\n" \
"$idx" "$ip" "$score" "$hits" "${attacks:-none}"
((idx++))
done
echo ""
echo -e "${BOLD}Options:${NC}"
echo " 1-${#blockable_list[@]}) Block specific IP"
echo " a) Block ALL high-threat IPs (score >= 80)"
echo " 0) Cancel"
echo ""
read -p "Select option: " choice
if [ "$choice" = "0" ]; then
return
elif [ "$choice" = "a" ]; then
# Block all IPs with score >= 80
local blocked=0
for entry in "${blockable_list[@]}"; do
IFS='|' read -r ip score hits attacks <<< "$entry"
[ "$score" -lt 80 ] && continue
echo ""
block_ip_temporary "$ip" 1 "Auto-block: High threat (score $score)"
((blocked++))
done
echo ""
echo "Blocked $blocked IPs"
read -p "Press Enter to continue..."
elif [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -le ${#blockable_list[@]} ]; then
# Block specific IP
local entry="${blockable_list[$((choice-1))]}"
IFS='|' read -r ip score hits attacks <<< "$entry"
echo ""
block_ip_temporary "$ip" 1 "Manual block from live monitor (score $score)"
echo ""
read -p "Press Enter to continue..."
else
echo "Invalid option"
read -p "Press Enter to continue..."
fi
}
################################################################################
# Log Monitoring
################################################################################
monitor_apache_logs() {
# Try multiple log locations
local log_files=()
# Set default if not defined by system-detect.sh
local LOG_DIR="${SYS_LOG_DIR:-/var/log/apache2/domlogs}"
# Main access log
if [ -f "${LOG_DIR}/access_log" ]; then
log_files+=("${LOG_DIR}/access_log")
elif [ -f "/var/log/httpd/access_log" ]; then
log_files+=("/var/log/httpd/access_log")
elif [ -f "/var/log/apache2/access.log" ]; then
log_files+=("/var/log/apache2/access.log")
fi
# Domain logs (cPanel domlogs)
if [ -d "${LOG_DIR}" ]; then
# Find recent domain logs (modified in last hour)
while IFS= read -r domain_log; do
[ -f "$domain_log" ] && log_files+=("$domain_log")
done < <(find "${LOG_DIR}" -type f \( -name "*.com" -o -name "*.net" -o -name "*.org" \) 2>/dev/null | head -5)
fi
if [ ${#log_files[@]} -eq 0 ]; then
echo "ERROR: No accessible Apache log files found" >> "$TEMP_DIR/recent_events"
echo "Checked: ${LOG_DIR}, /var/log/httpd, /var/log/apache2" >> "$TEMP_DIR/recent_events"
return 1
fi
# Monitor all log files
local event_count=0
tail -f "${log_files[@]}" 2>/dev/null | while read -r line; do
# Increment event counter (update file every 10 events for performance)
((event_count++))
if [ $((event_count % 10)) -eq 0 ]; then
echo "$event_count" > "$TEMP_DIR/event_counter"
fi
# Parse Apache combined log format (supports IPv4 and IPv6)
# Note: bytes field can be - or number, so use [0-9-]+
if [[ "$line" =~ ^([0-9a-f.:]+)\ -\ -\ \[([^\]]+)\]\ \"([A-Z]+)\ ([^\"]+)\ [^\"]+\"\ ([0-9]+)\ ([0-9-]+)\ \"[^\"]*\"\ \"([^\"]+)\" ]]; then
local ip="${BASH_REMATCH[1]}"
local timestamp="${BASH_REMATCH[2]}"
local method="${BASH_REMATCH[3]}"
local url="${BASH_REMATCH[4]}"
local status="${BASH_REMATCH[5]}"
local bytes="${BASH_REMATCH[6]}"
local user_agent="${BASH_REMATCH[7]}"
# Skip local/private IPs and server's own IP
if [[ "$ip" =~ ^127\. ]] || \
[[ "$ip" =~ ^10\. ]] || \
[[ "$ip" =~ ^192\.168\. ]] || \
[[ "$ip" =~ ^172\.(1[6-9]|2[0-9]|3[01])\. ]] || \
[[ "$ip" =~ ^169\.254\. ]] || \
[[ "$ip" == "localhost" ]] || \
[[ "$ip" == "::1" ]]; then
continue
fi
# Update intelligence
update_ip_intelligence "$ip" "$url" "$user_agent" "$method"
# Get updated data
local intel=$(get_ip_intelligence "$ip")
IFS='|' read -r score hits bot_type attacks ban_count rep_score <<< "$intel"
# Determine if this is a threat
local level=$(get_threat_level "$score")
# Only log medium+ threats or attacks
if [ "$score" -ge "$THREAT_THRESHOLD_MEDIUM" ] || [ -n "$attacks" ]; then
local color=$(get_threat_color "$level")
local time_str=$(date +"%H:%M:%S")
# Build log line
local log_line="${color}[${time_str}] $ip"
log_line+=" | Score:$score [$level]"
if [ -n "$attacks" ]; then
local first_attack=$(echo "$attacks" | cut -d',' -f1)
local icon=$(get_attack_icon "$first_attack")
log_line+=" | $icon$first_attack"
fi
log_line+=" | $url${NC}"
echo -e "$log_line" >> "$TEMP_DIR/recent_events"
fi
fi
done &
}
################################################################################
# Main Loop
################################################################################
# Start log monitoring
monitor_apache_logs
# Periodic snapshot saving in background
(
while true; do
sleep 300 # Save every 5 minutes
save_snapshot
done
) &
# Main dashboard loop
LOOP_COUNT=0
while true; do
draw_header
draw_intelligence_panel
draw_attack_breakdown
draw_live_feed
draw_quick_actions
# Periodic cleanup (every 50 loops = ~100 seconds)
((LOOP_COUNT++))
if [ $((LOOP_COUNT % 50)) -eq 0 ]; then
# Trim event log to last 1000 lines
if [ -f "$TEMP_DIR/recent_events" ]; then
tail -1000 "$TEMP_DIR/recent_events" > "$TEMP_DIR/recent_events.tmp" 2>/dev/null
mv "$TEMP_DIR/recent_events.tmp" "$TEMP_DIR/recent_events" 2>/dev/null
fi
fi
# Non-blocking input with timeout
read -t $REFRESH_INTERVAL -n 1 key
case "$key" in
b|B)
show_blocking_menu
;;
q|Q)
cleanup
;;
r|R)
# Force refresh
continue
;;
s|S)
# Show stats
clear
show_ip_reputation_stats
read -p "Press Enter to continue..."
;;
h|H|\?)
# Show help
clear
print_banner "Keyboard Controls"
echo ""
echo "Available Commands:"
echo " ${BOLD}b${NC} - Open IP blocking menu (batch or individual)"
echo " ${BOLD}s${NC} - Show IP reputation database statistics"
echo " ${BOLD}r${NC} - Force refresh display"
echo " ${BOLD}h${NC} - Show this help screen"
echo " ${BOLD}q${NC} - Quit and save snapshot"
echo ""
echo "Features:"
echo " • Real-time bot classification (legit/AI/monitor/suspicious)"
echo " • Attack vector detection (SQL, XSS, RCE, etc.)"
echo " • Threat scoring (0-100 scale)"
echo " • IP reputation DB integration"
echo " • CSF/iptables temporary bans (1 hour default)"
echo " • Memory protection (max ${MAX_TRACKED_IPS} IPs tracked)"
echo " • Auto-save every 5 minutes + on exit"
echo ""
read -p "Press Enter to continue..."
;;
esac
done