Files
Linux-Server-Management-Too…/modules/email/email-diagnostics.sh
T
cschantz 05396b6984 Enhance email diagnostics with comprehensive tracking
Bug fixes:
- Fix integer expression errors by sanitizing grep output
- Separate IMAP/POP3 authentication from email delivery events
- Prevent login failures from being counted as email bounces

New tracking features:
- Spam rejections (SpamAssassin)
- Greylisting events
- Emails received count
- Authentication activity (successful/failed logins)
- Failed login IPs extraction
- Top 5 senders and recipients
- Email account existence check
- Mailbox size and message count
- Quota information
- Email forwarder detection

Enhanced recommendations:
- Spam rejection troubleshooting
- Greylisting explanation
- Account not found guidance
- Failed login attempt handling
2025-12-31 18:49:24 -05:00

551 lines
19 KiB
Bash
Executable File

#!/bin/bash
################################################################################
# Email Account/Domain Diagnostics
################################################################################
# Purpose: Verify email is working for specific address or domain
# Shows proof of delivery or identifies why emails aren't working
################################################################################
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/email-functions.sh"
show_banner "Email Diagnostics - Verify Email Delivery"
# Get mail log path
MAIL_LOG=$(get_mail_log_path)
if [ ! -f "$MAIL_LOG" ]; then
print_error "Mail log not found: $MAIL_LOG"
exit 1
fi
print_success "Using mail log: $MAIL_LOG"
echo ""
# Ask what to check
echo -e "${BOLD}What would you like to check?${NC}"
echo ""
echo " 1) Specific email address (e.g., user@example.com)"
echo " 2) Entire domain (e.g., example.com)"
echo ""
read -p "Enter choice [1]: " check_type
check_type=${check_type:-1}
# Get email/domain to check
echo ""
if [ "$check_type" = "2" ]; then
read -p "Enter domain to check (e.g., example.com): " target
search_pattern="@${target}"
check_label="domain $target"
else
read -p "Enter email address to check: " target
search_pattern="$target"
check_label="email $target"
fi
if [ -z "$target" ]; then
print_error "No email/domain provided"
exit 1
fi
# Time period to check
echo ""
echo "Check logs from:"
echo " 1) Last 1 hour"
echo " 2) Last 6 hours"
echo " 3) Last 24 hours (recommended)"
echo " 4) Last 48 hours"
echo " 5) Last week"
echo ""
read -p "Enter choice [3]: " time_choice
time_choice=${time_choice:-3}
case "$time_choice" in
1) hours=1 ;;
2) hours=6 ;;
3) hours=24 ;;
4) hours=48 ;;
5) hours=168 ;;
*) hours=24 ;;
esac
echo ""
print_info "Analyzing $check_label for last $hours hours..."
echo ""
# Calculate time cutoff
if date --version 2>&1 | grep -q "GNU"; then
# GNU date
cutoff_time=$(date -d "$hours hours ago" +"%Y-%m-%d %H:%M:%S")
else
# BSD date (macOS)
cutoff_time=$(date -v-${hours}H +"%Y-%m-%d %H:%M:%S")
fi
################################################################################
# Analysis
################################################################################
TEMP_MATCHES="/tmp/email_diag_$$.txt"
TEMP_AUTH="/tmp/email_auth_$$.txt"
# Get ALL matches for this email/domain
grep -i "$search_pattern" "$MAIL_LOG" > "$TEMP_MATCHES" 2>/dev/null
# Separate authentication events (IMAP/POP3 logins)
grep -E "imap-login|pop3-login|dovecot.*Login|Logged in|Disconnected" "$TEMP_MATCHES" > "$TEMP_AUTH" 2>/dev/null || true
# Get only email delivery events (exclude auth logs)
grep -v "imap-login\|pop3-login\|dovecot.*Login\|Logged in\|Disconnected" "$TEMP_MATCHES" > "$TEMP_MATCHES.delivery" 2>/dev/null || true
mv "$TEMP_MATCHES.delivery" "$TEMP_MATCHES"
if [ ! -s "$TEMP_MATCHES" ]; then
print_error "❌ NO EMAIL ACTIVITY FOUND for $check_label"
echo ""
echo "This means:"
echo " • No emails sent TO this $check_label"
echo " • No emails sent FROM this $check_label"
echo " • No delivery attempts logged"
echo ""
print_warning "Possible reasons:"
echo " 1. Email address/domain doesn't exist on this server"
echo " 2. No email activity in the last $hours hours"
echo " 3. Emails are going to a different mail server"
echo ""
# Check if domain exists
if [ "$check_type" = "2" ]; then
if grep -q "$target" /etc/localdomains 2>/dev/null || grep -q "$target" /etc/userdomains 2>/dev/null; then
print_info "✓ Domain $target IS configured on this server"
else
print_warning "⚠ Domain $target NOT found in local domains"
fi
fi
rm -f "$TEMP_MATCHES"
exit 0
fi
total_lines=$(wc -l < "$TEMP_MATCHES")
print_success "✓ Found $total_lines log entries for $check_label"
echo ""
################################################################################
# Categorize activity
################################################################################
print_header "📊 Activity Summary"
echo ""
# Count different types (sanitize to remove newlines)
delivered=$(grep -ci "=> .*$search_pattern\|delivered.*$search_pattern" "$TEMP_MATCHES" 2>/dev/null || echo 0)
delivered=$(echo "$delivered" | head -1 | tr -d '\n\r')
sent=$(grep -ci "<=.*$search_pattern" "$TEMP_MATCHES" 2>/dev/null || echo 0)
sent=$(echo "$sent" | head -1 | tr -d '\n\r')
bounced=$(grep -ci "bounce.*$search_pattern\|failed.*$search_pattern\|550.*$search_pattern" "$TEMP_MATCHES" 2>/dev/null || echo 0)
bounced=$(echo "$bounced" | head -1 | tr -d '\n\r')
deferred=$(grep -ci "deferred.*$search_pattern\|retry.*$search_pattern\|temporarily rejected" "$TEMP_MATCHES" 2>/dev/null || echo 0)
deferred=$(echo "$deferred" | head -1 | tr -d '\n\r')
rejected=$(grep -ci "rejected.*$search_pattern\|denied.*$search_pattern\|blocked.*$search_pattern" "$TEMP_MATCHES" 2>/dev/null || echo 0)
rejected=$(echo "$rejected" | head -1 | tr -d '\n\r')
spf_fail=$(grep -ci "SPF.*fail.*$search_pattern" "$TEMP_MATCHES" 2>/dev/null || echo 0)
spf_fail=$(echo "$spf_fail" | head -1 | tr -d '\n\r')
dkim_fail=$(grep -ci "DKIM.*fail.*$search_pattern" "$TEMP_MATCHES" 2>/dev/null || echo 0)
dkim_fail=$(echo "$dkim_fail" | head -1 | tr -d '\n\r')
spam_rejected=$(grep -ci "spam.*$search_pattern\|SpamAssassin.*rejected.*$search_pattern" "$TEMP_MATCHES" 2>/dev/null || echo 0)
spam_rejected=$(echo "$spam_rejected" | head -1 | tr -d '\n\r')
greylist=$(grep -ci "greylist.*$search_pattern\|greylisted.*$search_pattern" "$TEMP_MATCHES" 2>/dev/null || echo 0)
greylist=$(echo "$greylist" | head -1 | tr -d '\n\r')
received=$(grep -ci "=> .*$search_pattern" "$TEMP_MATCHES" 2>/dev/null || echo 0)
received=$(echo "$received" | head -1 | tr -d '\n\r')
# Count authentication events
auth_failed=$(grep -ci "auth failed\|Login aborted\|authentication failed" "$TEMP_AUTH" 2>/dev/null || echo 0)
auth_failed=$(echo "$auth_failed" | head -1 | tr -d '\n\r')
auth_success=$(grep -ci "Logged in\|Login:.*user=.*$search_pattern" "$TEMP_AUTH" 2>/dev/null || echo 0)
auth_success=$(echo "$auth_success" | head -1 | tr -d '\n\r')
if [ "$delivered" -gt 0 ]; then
print_success "✅ Delivered: $delivered emails successfully delivered"
fi
if [ "$sent" -gt 0 ]; then
print_info "📤 Sent: $sent emails sent FROM this $check_label"
fi
if [ "$bounced" -gt 0 ]; then
print_error "❌ Bounced: $bounced emails bounced/failed"
fi
if [ "$deferred" -gt 0 ]; then
print_warning "⏸ Deferred: $deferred emails temporarily delayed"
fi
if [ "$rejected" -gt 0 ]; then
print_error "🚫 Rejected: $rejected emails blocked/rejected"
fi
if [ "$spf_fail" -gt 0 ]; then
print_warning "⚠ SPF Failures: $spf_fail authentication failures"
fi
if [ "$dkim_fail" -gt 0 ]; then
print_warning "⚠ DKIM Failures: $dkim_fail signature failures"
fi
if [ "$spam_rejected" -gt 0 ]; then
print_error "🚫 Spam Rejected: $spam_rejected emails marked as spam"
fi
if [ "$greylist" -gt 0 ]; then
print_info "⏱ Greylisted: $greylist emails temporarily delayed (anti-spam measure)"
fi
if [ "$received" -gt 0 ]; then
print_info "📥 Received: $received emails received TO this $check_label"
fi
# Show email traffic patterns
if [ "$sent" -gt 0 ] || [ "$received" -gt 0 ]; then
echo ""
print_header "📊 Email Traffic Patterns"
echo ""
# Top recipients (who this email is sending to)
if [ "$sent" -gt 0 ]; then
print_info "Top 5 recipients (emails sent TO):"
grep -i "<= .*$search_pattern" "$TEMP_MATCHES" | grep -oE "=> [^@]+@[^ ]+" | sed 's/=> //' | sort | uniq -c | sort -rn | head -5 | while read count recipient; do
echo " $recipient - $count emails"
done
echo ""
fi
# Top senders (who is sending to this email)
if [ "$received" -gt 0 ]; then
print_info "Top 5 senders (emails received FROM):"
grep -i "=> .*$search_pattern" "$TEMP_MATCHES" | grep -oE "<= [^@]+@[^ ]+" | sed 's/<= //' | sort | uniq -c | sort -rn | head -5 | while read count sender; do
echo " $sender - $count emails"
done
echo ""
fi
fi
# Show authentication summary
if [ -s "$TEMP_AUTH" ]; then
echo ""
print_header "🔐 Authentication Activity (IMAP/POP3 Login Attempts)"
echo ""
if [ "$auth_success" -gt 0 ]; then
print_success "✅ Successful Logins: $auth_success (password is correct)"
fi
if [ "$auth_failed" -gt 0 ]; then
print_error "❌ Failed Logins: $auth_failed (WRONG PASSWORD or unauthorized access)"
# Extract IPs attempting failed logins
failed_ips=$(grep -i "auth failed\|Login aborted" "$TEMP_AUTH" | grep -oE "rip=[0-9.]+|from [0-9.]+" | sed 's/rip=//; s/from //' | sort | uniq -c | sort -rn)
if [ -n "$failed_ips" ]; then
echo ""
print_warning "IPs with failed login attempts:"
echo "$failed_ips" | while read count ip; do
echo " $ip - $count attempts"
done
fi
fi
fi
echo ""
################################################################################
# Account Status Check (for specific email addresses)
################################################################################
if [ "$check_type" != "2" ]; then
print_header "📧 Email Account Status"
echo ""
# Extract username and domain from email
local_part=$(echo "$target" | cut -d'@' -f1)
domain_part=$(echo "$target" | cut -d'@' -f2)
# Check if email account exists in various locations
account_found=0
# Check cPanel mail directory
if [ -d "/home/*/mail/$domain_part/$local_part" ]; then
maildir=$(find /home/*/mail/$domain_part/$local_part -maxdepth 0 -type d 2>/dev/null | head -1)
if [ -n "$maildir" ]; then
print_success "✅ Email account EXISTS on this server"
account_found=1
# Get disk usage
if [ -d "$maildir" ]; then
disk_usage=$(du -sh "$maildir" 2>/dev/null | awk '{print $1}')
print_info "📊 Mailbox size: $disk_usage"
# Count messages
msg_count=$(find "$maildir" -type f -name "*:2,*" 2>/dev/null | wc -l)
print_info "📬 Messages stored: $msg_count"
fi
# Check for quota file
quota_file=$(find /home/*/mail/$domain_part/$local_part -name "maildirsize" 2>/dev/null | head -1)
if [ -f "$quota_file" ]; then
quota_info=$(head -1 "$quota_file" 2>/dev/null)
print_info "💾 Quota: $quota_info"
fi
fi
fi
# Check Plesk mail directory
if [ $account_found -eq 0 ] && [ -d "/var/qmail/mailnames/$domain_part/$local_part" ]; then
print_success "✅ Email account EXISTS on this server (Plesk)"
account_found=1
maildir="/var/qmail/mailnames/$domain_part/$local_part"
disk_usage=$(du -sh "$maildir" 2>/dev/null | awk '{print $1}')
print_info "📊 Mailbox size: $disk_usage"
fi
# Check generic /var/mail
if [ $account_found -eq 0 ] && [ -f "/var/mail/$local_part" ]; then
print_success "✅ Email account EXISTS in /var/mail"
account_found=1
disk_usage=$(du -sh "/var/mail/$local_part" 2>/dev/null | awk '{print $1}')
print_info "📊 Mailbox size: $disk_usage"
fi
if [ $account_found -eq 0 ]; then
print_warning "⚠ Email account NOT FOUND on this server"
echo ""
echo "This could mean:"
echo " • Account doesn't exist (typo in email address?)"
echo " • Account is on a different mail server"
echo " • Mail is handled by external service (Google, Office365, etc.)"
fi
# Check for forwarders
if [ -f "/etc/valiases/$domain_part" ]; then
forwarder=$(grep "^$local_part:" "/etc/valiases/$domain_part" 2>/dev/null)
if [ -n "$forwarder" ]; then
echo ""
print_info "📧 Forwarder configured:"
echo " $forwarder"
fi
fi
echo ""
fi
################################################################################
# Show verdict
################################################################################
print_header "🔍 Diagnostic Result"
echo ""
if [ "$delivered" -gt 0 ] && [ "$bounced" -eq 0 ] && [ "$rejected" -eq 0 ]; then
print_success "✅ EMAIL IS WORKING PROPERLY"
echo ""
echo "Evidence: $delivered successful deliveries in the last $hours hours"
echo "No bounces or rejections detected"
echo "Proof shown below with timestamps"
echo ""
elif [ "$delivered" -gt 0 ] && [ "$bounced" -gt 0 ]; then
print_warning "⚠ EMAIL PARTIALLY WORKING"
echo ""
echo "Some emails delivered ($delivered) but some failed ($bounced)"
echo "Check bounce reasons below for details"
echo ""
elif [ "$bounced" -gt 0 ] || [ "$rejected" -gt 0 ]; then
print_error "❌ EMAIL HAS PROBLEMS"
echo ""
echo "Emails are being rejected or bouncing"
echo "See details below for why"
echo ""
elif [ "$deferred" -gt 0 ]; then
print_warning "⏸ EMAIL DELAYED"
echo ""
echo "Emails are being deferred (temporary failures)"
echo "This usually resolves itself"
echo ""
else
print_info "📬 EMAILS SENT, AWAITING CONFIRMATION"
echo ""
echo "Emails were sent but no delivery confirmation yet"
echo "Check again in a few minutes"
echo ""
fi
################################################################################
# Show recent activity samples
################################################################################
if [ "$delivered" -gt 0 ]; then
print_header "✅ Recent Successful Deliveries (last 5 from past $hours hours)"
echo ""
print_info "PROOF - These emails were delivered recently:"
echo ""
grep -i "=> .*$search_pattern\|delivered.*$search_pattern" "$TEMP_MATCHES" | tail -5 | while read line; do
# Extract timestamp if present
timestamp=$(echo "$line" | grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}|[A-Z][a-z]{2} [0-9]+ [0-9]{2}:[0-9]{2}:[0-9]{2}' | head -1)
if [ -n "$timestamp" ]; then
echo -e " ${GREEN}[$timestamp]${NC} $line"
else
echo " $line"
fi
done
echo ""
fi
if [ "$bounced" -gt 0 ]; then
print_header "❌ Recent Bounces/Failures (last 5 from past $hours hours)"
echo ""
print_warning "PROOF - These emails failed recently:"
echo ""
grep -i "bounce.*$search_pattern\|failed.*$search_pattern\|550.*$search_pattern" "$TEMP_MATCHES" | tail -5 | while read line; do
timestamp=$(echo "$line" | grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}|[A-Z][a-z]{2} [0-9]+ [0-9]{2}:[0-9]{2}:[0-9]{2}' | head -1)
if [ -n "$timestamp" ]; then
echo -e " ${RED}[$timestamp]${NC} $line"
else
echo " $line"
fi
done
echo ""
# Extract bounce reasons
print_header "Common Bounce Reasons"
echo ""
grep -i "bounce\|550\|fail" "$TEMP_MATCHES" | grep -oE "550 .*|User unknown|Mailbox.*full|Relay.*denied|blocked" | sort | uniq -c | sort -rn | head -5
echo ""
fi
if [ "$rejected" -gt 0 ]; then
print_header "🚫 Recent Rejections (last 5)"
echo ""
grep -i "rejected.*$search_pattern\|denied.*$search_pattern\|blocked.*$search_pattern" "$TEMP_MATCHES" | tail -5 | while read line; do
echo " $line"
done
echo ""
fi
if [ "$spam_rejected" -gt 0 ]; then
print_header "🚫 Recent Spam Rejections (last 5)"
echo ""
grep -i "spam.*$search_pattern\|SpamAssassin.*rejected" "$TEMP_MATCHES" | tail -5 | while read line; do
timestamp=$(echo "$line" | grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}|[A-Z][a-z]{2} [0-9]+ [0-9]{2}:[0-9]{2}:[0-9]{2}' | head -1)
if [ -n "$timestamp" ]; then
echo -e " ${RED}[$timestamp]${NC} $line"
else
echo " $line"
fi
done
echo ""
fi
if [ "$greylist" -gt 0 ]; then
print_header "⏱ Recent Greylisting Events (last 5)"
echo ""
print_info "Note: Greylisting is temporary - emails usually deliver after retry"
echo ""
grep -i "greylist" "$TEMP_MATCHES" | tail -5 | while read line; do
echo " $line"
done
echo ""
fi
if [ "$spf_fail" -gt 0 ] || [ "$dkim_fail" -gt 0 ]; then
print_header "⚠ Authentication Issues"
echo ""
if [ "$spf_fail" -gt 0 ]; then
echo "SPF failures detected:"
grep -i "SPF.*fail" "$TEMP_MATCHES" | head -3
echo ""
fi
if [ "$dkim_fail" -gt 0 ]; then
echo "DKIM failures detected:"
grep -i "DKIM.*fail" "$TEMP_MATCHES" | head -3
echo ""
fi
fi
################################################################################
# Recommendations
################################################################################
print_header "💡 Recommendations"
echo ""
if [ "$bounced" -gt 0 ]; then
echo "To fix bounces:"
echo " 1. Verify the recipient email address is correct"
echo " 2. Check if recipient mailbox is full"
echo " 3. Ensure domain DNS is configured properly"
echo ""
fi
if [ "$rejected" -gt 0 ]; then
echo "To fix rejections:"
echo " 1. Check if server IP is blacklisted (use Blacklist Check tool)"
echo " 2. Verify SPF/DKIM/DMARC records are correct"
echo " 3. Check if recipient is blocking your domain"
echo ""
fi
if [ "$spf_fail" -gt 0 ]; then
echo "To fix SPF failures:"
echo " 1. Add this server's IP to domain's SPF record"
echo " 2. Use SPF/DKIM/DMARC Check tool for details"
echo ""
fi
if [ "$spam_rejected" -gt 0 ]; then
echo "To fix spam rejections:"
echo " 1. Check SpamAssassin score and adjust settings"
echo " 2. Verify email content isn't triggering spam filters"
echo " 3. Ensure proper SPF/DKIM authentication"
echo " 4. Check if sending domain has good reputation"
echo ""
fi
if [ "$greylist" -gt 0 ] && [ "$delivered" -eq 0 ]; then
echo "Greylisting delays:"
echo " • This is normal anti-spam behavior"
echo " • Emails should deliver after sender retries (5-15 minutes)"
echo " • No action needed unless emails never arrive"
echo ""
fi
if [ "$check_type" != "2" ] && [ $account_found -eq 0 ]; then
echo "Email account not found:"
echo " 1. Verify the email address is spelled correctly"
echo " 2. Check if domain DNS points to this server"
echo " 3. Create the email account if it should exist here"
echo " 4. Check if mail is handled by external service"
echo ""
fi
if [ "$auth_failed" -gt 0 ]; then
echo "Failed login attempts detected:"
echo " 1. Customer may be using wrong password"
echo " 2. Could be brute-force attack attempt"
echo " 3. Check IPs - block suspicious ones with firewall"
echo " 4. Advise customer to change password if compromised"
echo ""
fi
################################################################################
# Save full report
################################################################################
REPORT_FILE="/tmp/email_diag_${target//[@.]/_}_$(date +%Y%m%d_%H%M%S).txt"
cp "$TEMP_MATCHES" "$REPORT_FILE"
print_info "Full log saved to: $REPORT_FILE"
echo ""
# Cleanup
rm -f "$TEMP_MATCHES" "$TEMP_AUTH"