Add user/domain filtering and ModSecurity support to error analyzer

- Added per-user analysis (select cPanel user from list)
- Added per-domain analysis (enter specific domain)
- ModSecurity audit log integration
- Detects SQL injection, XSS, command injection blocks by ModSec
- Filters logs by user or domain when specified
- Shows ModSecurity blocks in error report
- Provides specific recommendations for ModSec false positives
- Matches bot analyzer's filtering capabilities
This commit is contained in:
cschantz
2025-11-03 19:08:53 -05:00
parent e396df5b1a
commit 5e517b9858
+150 -9
View File
@@ -11,11 +11,15 @@
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
source "$SCRIPT_DIR/lib/common-functions.sh" source "$SCRIPT_DIR/lib/common-functions.sh"
source "$SCRIPT_DIR/lib/system-detect.sh" source "$SCRIPT_DIR/lib/system-detect.sh"
source "$SCRIPT_DIR/lib/user-manager.sh"
# Configuration # Configuration
APACHE_ERROR_LOG="/var/log/apache2/error_log" APACHE_ERROR_LOG="/var/log/apache2/error_log"
DOMLOGS_DIR="/var/log/apache2/domlogs" DOMLOGS_DIR="/var/log/apache2/domlogs"
MODSEC_AUDIT_LOG="/usr/local/apache/logs/modsec_audit.log"
HOURS_TO_ANALYZE=24 HOURS_TO_ANALYZE=24
FILTER_USER=""
FILTER_DOMAIN=""
print_banner "Intelligent Website Error Analyzer" print_banner "Intelligent Website Error Analyzer"
@@ -37,6 +41,39 @@ echo " ✓ Permission issues blocking access"
echo " ✓ Memory exhaustion affecting performance" echo " ✓ Memory exhaustion affecting performance"
echo "" echo ""
# Ask for filtering scope
echo -e "${CYAN}Analysis Scope:${NC}"
echo " 1) All users/domains (default)"
echo " 2) Specific cPanel user"
echo " 3) Specific domain"
echo ""
read -p "Select option [1]: " scope_choice
scope_choice=${scope_choice:-1}
case $scope_choice in
2)
# Select specific user
select_user_interactive "Select cPanel user to analyze"
if [ -n "$SELECTED_USER" ]; then
FILTER_USER="$SELECTED_USER"
echo "→ Filtering for user: $FILTER_USER"
fi
;;
3)
# Enter specific domain
echo ""
read -p "Enter domain name (e.g., example.com): " FILTER_DOMAIN
if [ -n "$FILTER_DOMAIN" ]; then
echo "→ Filtering for domain: $FILTER_DOMAIN"
fi
;;
*)
echo "→ Analyzing all users/domains"
;;
esac
echo ""
# Ask for time range # Ask for time range
echo -e "${CYAN}How far back should we analyze?${NC}" echo -e "${CYAN}How far back should we analyze?${NC}"
echo " 1) Last 1 hour" echo " 1) Last 1 hour"
@@ -74,26 +111,77 @@ LOG_FILES_LIST="$TEMP_DIR/log_files.txt"
echo "→ Discovering log file locations..." echo "→ Discovering log file locations..."
> "$LOG_FILES_LIST" > "$LOG_FILES_LIST"
# Apache main error log # Apache main error log (only if not filtering by user)
if [ -z "$FILTER_USER" ] && [ -z "$FILTER_DOMAIN" ]; then
for log in /var/log/apache2/error_log /usr/local/apache/logs/error_log /var/log/httpd/error_log; do for log in /var/log/apache2/error_log /usr/local/apache/logs/error_log /var/log/httpd/error_log; do
[ -f "$log" ] && echo "$log|apache_main" >> "$LOG_FILES_LIST" [ -f "$log" ] && echo "$log|apache_main" >> "$LOG_FILES_LIST"
done done
fi
# Per-domain PHP error logs in public_html # Per-domain PHP error logs in public_html
find /home/*/public_html -name "error_log" -type f 2>/dev/null | while read -r log; do if [ -n "$FILTER_USER" ]; then
domain=$(echo "$log" | grep -oE '/home/[^/]+' | sed 's|/home/||') # Specific user
echo "$log|php_$domain" >> "$LOG_FILES_LIST" find "/home/$FILTER_USER" -name "error_log" -type f 2>/dev/null | while read -r log; do
echo "$log|php_$FILTER_USER" >> "$LOG_FILES_LIST"
done done
elif [ -n "$FILTER_DOMAIN" ]; then
# Try to find domain's user and error logs
user=$(grep -l "DNS.*$FILTER_DOMAIN" /var/cpanel/users/* 2>/dev/null | head -1 | xargs basename 2>/dev/null)
if [ -n "$user" ]; then
find "/home/$user" -name "error_log" -type f 2>/dev/null | while read -r log; do
echo "$log|php_$FILTER_DOMAIN" >> "$LOG_FILES_LIST"
done
fi
else
# All users
find /home/*/public_html -name "error_log" -type f 2>/dev/null | while read -r log; do
username=$(echo "$log" | grep -oE '/home/[^/]+' | sed 's|/home/||')
echo "$log|php_$username" >> "$LOG_FILES_LIST"
done
fi
# Per-domain Apache logs in domlogs # Per-domain Apache logs in domlogs
if [ -d "$DOMLOGS_DIR" ]; then if [ -d "$DOMLOGS_DIR" ]; then
for log in "$DOMLOGS_DIR"/*; do if [ -n "$FILTER_DOMAIN" ]; then
[ -f "$log" ] && echo "$log|domlog_$(basename "$log")" >> "$LOG_FILES_LIST" # Specific domain
for log in "$DOMLOGS_DIR/$FILTER_DOMAIN" "$DOMLOGS_DIR/$FILTER_DOMAIN-"*; do
[ -f "$log" ] && echo "$log|domlog_$FILTER_DOMAIN" >> "$LOG_FILES_LIST"
done done
elif [ -n "$FILTER_USER" ]; then
# Specific user - find their domains
if [ -f "/var/cpanel/users/$FILTER_USER" ]; then
grep "^DNS" "/var/cpanel/users/$FILTER_USER" | awk '{print $2}' | while read -r domain; do
for log in "$DOMLOGS_DIR/$domain" "$DOMLOGS_DIR/$domain-"*; do
[ -f "$log" ] && echo "$log|domlog_$domain" >> "$LOG_FILES_LIST"
done
done
fi
else
# All domains
for log in "$DOMLOGS_DIR"/*; do
[ -f "$log" ] && ! [[ "$log" =~ (bytes_log|offset|error_log)$ ]] && \
echo "$log|domlog_$(basename "$log")" >> "$LOG_FILES_LIST"
done
fi
fi
# ModSecurity audit log (only if not filtering or matches filter)
if [ -f "$MODSEC_AUDIT_LOG" ]; then
if [ -z "$FILTER_USER" ] && [ -z "$FILTER_DOMAIN" ]; then
echo "$MODSEC_AUDIT_LOG|modsec_audit" >> "$LOG_FILES_LIST"
elif [ -n "$FILTER_DOMAIN" ]; then
# Will filter ModSec entries during processing
echo "$MODSEC_AUDIT_LOG|modsec_audit" >> "$LOG_FILES_LIST"
fi
fi fi
log_count=$(wc -l < "$LOG_FILES_LIST" 2>/dev/null || echo "0") log_count=$(wc -l < "$LOG_FILES_LIST" 2>/dev/null || echo "0")
echo " Found $log_count log files to analyze" echo " Found $log_count log files to analyze"
if [ -n "$FILTER_USER" ]; then
echo " Scope: User '$FILTER_USER'"
elif [ -n "$FILTER_DOMAIN" ]; then
echo " Scope: Domain '$FILTER_DOMAIN'"
fi
echo "" echo ""
################################################################################ ################################################################################
@@ -230,11 +318,38 @@ critical_found=0
while IFS='|' read -r log_path log_type; do while IFS='|' read -r log_path log_type; do
echo " Analyzing: $(basename "$log_path")..." echo " Analyzing: $(basename "$log_path")..."
# Determine if it's an access log (contains status codes) or error log # Determine log type
is_access_log=false is_access_log=false
is_modsec_log=false
[[ "$log_type" == domlog_* ]] && ! [[ "$log_path" =~ error ]] && is_access_log=true [[ "$log_type" == domlog_* ]] && ! [[ "$log_path" =~ error ]] && is_access_log=true
[[ "$log_type" == modsec_* ]] && is_modsec_log=true
if $is_access_log; then if $is_modsec_log; then
# ModSecurity audit log - parse for blocks/denials
while IFS= read -r line; do
((total_lines++))
# Skip if not a security event
echo "$line" | grep -qiE "ModSecurity.*Action.*Intercepted|status: (403|406|500)" || continue
# Extract domain/host if filtering
if [ -n "$FILTER_DOMAIN" ]; then
echo "$line" | grep -q "$FILTER_DOMAIN" || continue
fi
# Extract useful info
if echo "$line" | grep -qiE "SQL Injection|XSS|Command Injection|Path Traversal"; then
((critical_found++))
attack_type=$(echo "$line" | grep -oiE "SQL Injection|XSS|Command Injection|Path Traversal" | head -1)
uri=$(echo "$line" | grep -oE "uri: [^ ]+" | sed 's/uri: //' || echo "")
domain=$(echo "$line" | grep -oE "hostname: [^ ]+" | sed 's/hostname: //' || echo "unknown")
echo "$domain||ModSecurity blocked: $attack_type $uri" >> "$CRITICAL_ERRORS"
fi
done < <(tail -n 5000 "$log_path" 2>/dev/null)
elif $is_access_log; then
# Access log - look for 5xx status codes # Access log - look for 5xx status codes
while IFS= read -r line; do while IFS= read -r line; do
((total_lines++)) ((total_lines++))
@@ -247,12 +362,17 @@ while IFS='|' read -r log_path log_type; do
# Extract status code and URL # Extract status code and URL
if echo "$line" | grep -qE '" 5[0-9]{2} '; then if echo "$line" | grep -qE '" 5[0-9]{2} '; then
((critical_found++))
status=$(echo "$line" | grep -oE '" 5[0-9]{2} ' | tr -d '" ') status=$(echo "$line" | grep -oE '" 5[0-9]{2} ' | tr -d '" ')
url=$(echo "$line" | awk '{print $7}' | cut -c1-80) url=$(echo "$line" | awk '{print $7}' | cut -c1-80)
ip=$(echo "$line" | awk '{print $1}') ip=$(echo "$line" | awk '{print $1}')
domain=$(basename "$log_path" | sed 's/-.*//') domain=$(basename "$log_path" | sed 's/-.*//')
# Apply domain filter if set
if [ -n "$FILTER_DOMAIN" ] && [ "$domain" != "$FILTER_DOMAIN" ]; then
continue
fi
((critical_found++))
echo "$domain||HTTP $status - $url (from $ip)" >> "$CRITICAL_ERRORS" echo "$domain||HTTP $status - $url (from $ip)" >> "$CRITICAL_ERRORS"
fi fi
@@ -268,6 +388,14 @@ while IFS='|' read -r log_path log_type; do
continue continue
fi fi
# Apply user/domain filter if set
if [ -n "$FILTER_USER" ]; then
echo "$line" | grep -q "/home/$FILTER_USER" || continue
fi
if [ -n "$FILTER_DOMAIN" ]; then
echo "$line" | grep -q "$FILTER_DOMAIN" || continue
fi
# Check if it's critical and user-facing # Check if it's critical and user-facing
if is_critical_user_facing "$line"; then if is_critical_user_facing "$line"; then
((critical_found++)) ((critical_found++))
@@ -412,6 +540,19 @@ if grep -qiE "memory.*exhausted|Out of memory" "$CRITICAL_ERRORS"; then
echo "" echo ""
fi fi
if grep -qiE "ModSecurity" "$CRITICAL_ERRORS"; then
echo -e "${YELLOW}● ModSecurity Blocks detected${NC}"
echo " Priority: MEDIUM - Security rules blocking requests"
echo ""
echo " Actions:"
echo " 1. Review ModSecurity audit log for false positives"
echo " 2. Check if legitimate functionality is being blocked"
echo " 3. Consider whitelisting specific rules if needed"
echo " 4. Review blocked attack types to understand threats"
echo " 5. ModSecurity log: $MODSEC_AUDIT_LOG"
echo ""
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "" echo ""
echo "Full analysis saved to: $CRITICAL_ERRORS" echo "Full analysis saved to: $CRITICAL_ERRORS"