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:
@@ -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)
|
||||||
for log in /var/log/apache2/error_log /usr/local/apache/logs/error_log /var/log/httpd/error_log; do
|
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
|
||||||
[ -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
|
||||||
done
|
echo "$log|php_$FILTER_USER" >> "$LOG_FILES_LIST"
|
||||||
|
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"
|
||||||
|
|||||||
Reference in New Issue
Block a user