Could we help you? Please click the banners. We are young and desperately need the money
We wanted to receive notifications from Postfix about bounced e-mails send by our customers. The reason is that larger amounts of e-mail bounces may be an indicator of a hacked account or a user abusing the system. We did not find a tool or script which can handle this simple task out of the box. Thus we wrote our own neat little program to do that.
Simple:
In case you don't see any subject most probably Postfix does not write the Subject into the mail.log file. This is how you can enable the Subject function in Postfix:
Copy paste it from below or download it here.
#!/bin/bash export LC_ALL=en_US.utf8 MAILLOG=/var/log/mail.log LOGMAILFROM="Your sender name<websupport@yourdomain.tld>" ## Set the FROM for the e-mail. You can write "Bounce Mail Report<youremail@address.tld>" LOGMAILTO="your@yourdomain.tld" ## Set the TO for the e-mail (single e-mail address). # Set the amount of hours you want the system to check your logs for bounce messages. # Example: If it's 18:13 hours now and you enter 6 in the variable below, the system will check for all bounce messages which occured between 12:00-13:00, 13:00-14:00, 14:00-15:00, 15:00-16:00, 16:00-17:00 and 17:00-18:00 AMOUNTOFHOURSTOCHECK=6 TIME_START=$(date +"%s") # Initialize the regex pattern which is used to fetch all bounce messages from the past X hours BOUNCEMESSAGERGXPATTERN="" # Loop through the last 6 hours and build the regex pattern for (( i = ${AMOUNTOFHOURSTOCHECK}; i > 0; i-- )); do hour=$(date -d "-${i} hour" '+%b %e %H') # Append to the pattern, separated by the OR operator | BOUNCEMESSAGERGXPATTERN+="(${hour}.*postfix/smtp.*status=bounced)|" done # Remove the trailing '|' BOUNCEMESSAGERGXPATTERN=${BOUNCEMESSAGERGXPATTERN%|} # Fetch all bounce messages from the mail log and store the result in a variable for later use ALLBOUNCES=`cat ${MAILLOG} | egrep "$BOUNCEMESSAGERGXPATTERN"` COUNTBOUNCES=$( [ -n "$ALLBOUNCES" ] && echo "$ALLBOUNCES" | wc -l || echo 0 ) if [ ${COUNTBOUNCES} -gt 0 ]; then MAILINFO='<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><title></title>' MAILINFO+='<style>' MAILINFO+='.mainTable { border-collapse: collapse; } .mainTable th, .mainTable td { border:1px dotted #cccccc; text-align:left; vertical-align:top; padding:5px 10px; } .mainTable th { border-bottom: 2px solid #cccccc; }' MAILINFO+='.additionalTable { border-collapse: collapse; } .additionalTable td { border: 0px; text-align: left; vertical-align: top; padding: 5px 10px; }' MAILINFO+='</style>' MAILINFO+='</head><body><table class="mainTable">' MAILINFO+="<tr><th>DATE & TIME</th><th>MAIL ID</th><th>CLIENT PTR</th><th>CLIENT IP</th><th>USERNAME</th><th>MAIL FROM</th><th>MAIL TO</th><th>HOST</th><th>HOST IP</th><th>REASON</th><th>SUBJECT</th></tr>" while IFS= read -r BOUNCE do ## Clean the bounce variable (remove all line-breaks) BOUNCE="${BOUNCE//$'\n'/ }" ## Get the mail ID from the current log line MAILID=$(perl -pe "s/.*?postfix\/smtp\[\w+\]:\s(\w+).*/\1/g" <<< ${BOUNCE}) MAILTO=$(perl -pe "s/.*to=<(.*?)>.*/\1/g" <<< ${BOUNCE}) DATETIME=$(perl -pe "s/^(\w+\s+\w+\s+\w+:\w+:\w+)\s.*/\1/g" <<< ${BOUNCE}) ## Check if there's information about the host (there is non when the delivery has been done locally) NEEDLE=".*?\(host\s.*?\[.*" if [[ "${BOUNCE}" =~ ${NEEDLE} ]]; then HOST=$(perl -pe "s/.*?\(host\s(.*?)\[.*/\1/g" <<< ${BOUNCE}) HOSTIP=$(perl -pe "s/.*?\(host\s.*?\[(.*?)\]\s.*/\1/g" <<< ${BOUNCE}) else HOST="<span style='color:#888888;'><i>No host found</i></span>" HOSTIP="<span style='color:#888888;'><i>No IP found</i></span>" fi ## Try to fetch the bounce reason NEEDLE=".*\ssaid:\s.*" if [[ "${BOUNCE}" =~ ${NEEDLE} ]]; then REASON=$(perl -pe "s/.*\ssaid:\s(.*)/\1/g" <<< ${BOUNCE}) else ## Check if perhaps the domain could not be resolved (Host not found message). In that case there would be no said NEEDLE=".*\(Host or domain name not found.*" BOUNCE_MSG_PRESENT=".*?status=bounced\s\(.*" if [[ "${BOUNCE}" =~ ${NEEDLE} ]]; then REASON=$(perl -pe "s/.*?\((Host or domain name not found.*)/\1/g" <<< ${BOUNCE}) elif [[ "${BOUNCE}" =~ ${BOUNCE_MSG_PRESENT} ]]; then REASON=$(perl -pe "s/.*?status=bounced\s\((.*?)\)/\1/g" <<< ${BOUNCE}) else REASON="<span style='color:#888888;'><i>Reject reason not found.</i></span>" fi fi ## Fetch additional information about the bounced message based on the MAILID MESSAGEDATA=$(cat ${MAILLOG} |grep ${MAILID}) MESSAGEDATA="${MESSAGEDATA//$'\n'/ }" ## Check if there's a username NEEDLE=".*sasl_username.*" if [[ "${MESSAGEDATA}" =~ ${NEEDLE} ]]; then USERNAME=$(perl -pe "s/.*?sasl_username=(.*?)\s.*/\1/gm" <<< ${MESSAGEDATA}) SASL_CLIENT_PTR=$(perl -pe "s/.*?client=(.*?)\[.*/\1/gm" <<< ${MESSAGEDATA}) SASL_CLIENT_IP=$(perl -pe "s/.*?client=.*?\[(.*?)\].*/\1/gm" <<< ${MESSAGEDATA}) else USERNAME="<span style='color:#888888;'><i>local delivery (non-delivery notification)</i></span>" SASL_CLIENT_PTR="<span style='color:#888888;'><i>No PTR found</i></span>" SASL_CLIENT_IP="<span style='color:#888888;'><i>No IP found</i></span>" fi ## Check if there's a subject line NEEDLE=".*?header\sSubject:\s.*?\sfrom\s.*" if [[ "${MESSAGEDATA}" =~ ${NEEDLE} ]]; then SUBJECT=$(perl -pe "s/.*?header\sSubject:\s(.*?)\sfrom\s.*/\1/gm" <<< ${MESSAGEDATA}) else NEEDLE=".*sender non-delivery notification.*" if [[ "${MESSAGEDATA}" =~ ${NEEDLE} ]]; then SUBJECT="<span style='color:#888888;'><i>No subject (delivery notification)</i></span>" else SUBJECT="<span style='color:#888888;'><i>No subject (reason unknown)</i></span>" fi fi MAILFROM=$(perl -pe "s/.*?from=<(.*?)>.*/\1/gm" <<< ${MESSAGEDATA}) MAILINFO+="<tr><td>${DATETIME}</td><td>${MAILID}</td><td>${SASL_CLIENT_PTR}</td><td>${SASL_CLIENT_IP}</td><td>${USERNAME}</td><td>${MAILFROM}</td><td>${MAILTO}</td><td>${HOST}</td><td>${HOSTIP}</td><td>${REASON}</td><td>${SUBJECT}</td></tr>" done <<< "$ALLBOUNCES" MAILINFO+="</table></body></html>" MAILINFO+="<br/><br/><h3>Additional information</h3>" MAILINFO+="<table class='additionalTable'>" TIME_DIFF=$(($(date +"%s")-${TIME_START})) MAILINFO+="<tr><td><strong>Script runtime:</strong></td><td>$((${TIME_DIFF} / 60)) Minutes</td><td>$((${TIME_DIFF} % 60)) Seconds</td><td></td></tr>" MAILINFO+="</table></body></html>" if [ ${COUNTBOUNCES} -gt 6 ]; then BOUNCEWARNING="WARNING | "; else BOUNCEWARNING=""; fi echo ${MAILINFO} | mail -a "From: ${LOGMAILFROM}" -a "MIME-Version: 1.0" -a "Content-Type: text/html; charset=utf-8" -s "${BOUNCEWARNING}${COUNTBOUNCES} Mail Bounce(s) Registered" ${LOGMAILTO} fi