50d9067134
- Changed from 'score >= 40' to 'score > 0 OR has attacks OR suspicious bot' - Now shows ALL interesting traffic, not just high-scoring threats - Added bot type display for suspicious/AI bots - Users will see much more activity in the feed This fixes the issue where legitimate attacks weren't showing because they hadn't accumulated enough score yet.
707 lines
24 KiB
Bash
Executable File
707 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")
|
|
|
|
# Log all traffic with attacks, or score > 0, or suspicious bots
|
|
# This ensures we see everything interesting, not just high scores
|
|
if [ "$score" -gt 0 ] || [ -n "$attacks" ] || [ "$bot_type" = "suspicious" ]; 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]"
|
|
|
|
# Show bot type if interesting
|
|
if [ "$bot_type" = "suspicious" ] || [ "$bot_type" = "ai" ]; then
|
|
log_line+=" | Bot:$bot_type"
|
|
fi
|
|
|
|
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
|