######################################################################## # # PROCMAIL SPAM BUSTER # ######################################################################## # # Original version written by nic@angel.net http://angel.net/~nic/spam-x.html # adapted and improved by Michael Buro # # USE THIS FILE AT YOUR OWN RISK! YOU GET WHAT YOU PAY FOR! # YOU CAN DO WHATEVER YOU LIKE WITH THIS FILE PROVIDED THE # REMARKS IN THIS PARAGRAPH REMAIN INTACT. # ######################################################################## # # CHANGE LOG: # # 01.Feb.2002 - mailing list rules fixed # 31.Jan.2002 - enhanced bounced mail filter # 30.Jan.2002 - cron script adds recipients to the accept-list # - initial release # 20.Sep.2005 - added comments # ######################################################################## # # What is it? # # This procmail recipe eliminates spam completely. # It manages an accept-list of trusted email addresses. If a sender with # address X is currently not on the list procmail appends X's message # to the files $PENDING_DIR/X and $PENDING_MSGS_DIR/$CURRENT_TIME. # It then replies to the message asking for a reply in turn. When X sends # a reply (usually meaning X is not a spammer), X gets on the accept-list # and all previous and future emails from X are delivered. # # Reply requests sent to spammers usually bounce. This recipe recognizes # those MAILER-DAEMON/postmaster messages and dumps them into a file. Other # bounce messages get through. # # Pending messages stored in $PENDING_MSGS_DIR can be viewed by a script # like the one below in reverse order. This feature is useful for catching # important auto notification messages from systems that do not answer the # reply request. # # # Installation (assumes your userid is FOOBAR): # # - grab the procmailrc file - well, you did that already ;-) # - include "export NFS=your-network-home-directory" in your shell startup # script (for bash). Syntax may be different for your shell. If you don't # live in a network it's "export NFS=/home/FOOBAR". # - move procmailrc to $NFS/.procmail # - adjust the settings to your environment # - mkdir $NFS/.PROCMAIL # mkdir $NFS/.PROCMAIL/pending # mkdir $NFS/.PROCMAIL/pending-msgs # - create a world-readable/executable file "exists": # # #/bin/bash # test -e $1 # # and adjust the path to it below (***). I couldn't figure out how # to test whether a file exists directly - # * ? test -f file didn't work on solaris. # - store the "pend" script into your search path and do chmod +x pend # - collect trusted email addresses and put them in $NFS/.PROCMAIL/accept-list # (one per line). All buddies you add here won't get nag emails. # - optional: install the cron files (otherwise you need to clean up # $NFS/.PROCMAIL/pending and $NFS/.PROCMAIL/pending-msgs manually when # they get too big) # - optional: log your outgoing emails in file $NFS/.PROCMAIL/cc (see below # for why and how) # - set $NFS/.forward to "| /usr/local/bin/procmail #FOOBAR" . # Make sure the procmail path is OK!!! Otherwise you might lose emails. # # # Default file list: # # $PMDIR/ # # accept-list list of trusted email addresses # backup all incoming messages are logged here # cc outgoing emails are logged here # deny-list list of email addresses to be rejected # bounced-spammer bounced messages caused by reply requests go here (SPAM!) # list-archive archive of mailing list messages matched by $LIST # (these also get through) # logfile procmail logfile (VERBOSE=on/off) # notifications archive of mailing list messages matched by $NOTIFICATIONS # (these won't get through) # pending/ messages waiting for reply (filename = email address) # pending-msgs/ messages waiting for reply (filename = time stamp) # ###################################################################### ###################################################################### # Script for checking the latest pending messages: # #----------------------- pend -------------------------- # #! /bin/bash # # # assumption: variable NFS contains your network home directory! # # cd $NFS/.PROCMAIL/pending-msgs # FILE=/tmp/pend.file # # rm -f $FILE # touch $FILE # # for i in $(ls -t) # do # cat $i >> $FILE # done # # mail -f $FILE # rm -f $FILE # # ###################################################################### ###################################################################### # Once a day I let cron call the following script to backup files # and to clean up old stuff: # #----- script stored in /etc/cron.daily ----------------- # # #! /bin/tcsh # su -l YOURID -c "~YOURID/scripts/procmail-backup.cron" >& /dev/null # #------------------ procmail-backup.cron ---------------- # # #! /bin/bash # # # assumption: variable NFS contains your network home directory! # # COPYDIR=~/etc/procmail # MAILHOME=$NFS # PROCMAILDIR=$MAILHOME/.PROCMAIL # # # store important files in a place that gets backed up # cd $PROCMAILDIR # rm -f $COPYDIR/pending/* $COPYDIR/pending-msgs/* # cp pending/* $COPYDIR/pending # cp pending-msgs/* $COPYDIR/pending-msgs # cp accept-list $COPYDIR # cp $MAILHOME/.procmailrc $COPYDIR/procmailrc # # # remove old pending messages # find pending/* -mtime +31 -print | xargs rm -f # find pending-msgs/* -mtime +31 -print | xargs rm -f # # # ###################################################################### ###################################################################### # It's kind of uncool when you send an email to someone and his reply # bounces. Here is a small cron script that scans outgoing email and # appends recipients' adresses to the accept-list. The script assumes # that outgoing email is logged in a file. To accomplish this # say: set record=NFS-DIR/.PROCMAIL/cc in ~/.mailrc and # (setq mail-archive-file-name "NFS-DIR/.PROCMAIL/cc") in ~/.emacs # #----- script stored in /etc/cron.min5 ----------------- # # #! /bin/tcsh # su -l YOURID -c "~YOURID/scripts/procmail-list-update.cron" >& /dev/null # #----- updated /etc/crontab (5 and 10 minute events) --- # # 03 * * * * root run-parts /etc/cron.min5 # 08 * * * * root run-parts /etc/cron.min5 # 13 * * * * root run-parts /etc/cron.min5 # 18 * * * * root run-parts /etc/cron.min5 # 23 * * * * root run-parts /etc/cron.min5 # 28 * * * * root run-parts /etc/cron.min5 # 33 * * * * root run-parts /etc/cron.min5 # 38 * * * * root run-parts /etc/cron.min5 # 43 * * * * root run-parts /etc/cron.min5 # 48 * * * * root run-parts /etc/cron.min5 # 53 * * * * root run-parts /etc/cron.min5 # 58 * * * * root run-parts /etc/cron.min5 # 02 * * * * root run-parts /etc/cron.min10 # 12 * * * * root run-parts /etc/cron.min10 # 22 * * * * root run-parts /etc/cron.min10 # 32 * * * * root run-parts /etc/cron.min10 # 42 * * * * root run-parts /etc/cron.min10 # 52 * * * * root run-parts /etc/cron.min10 # 01 * * * * root run-parts /etc/cron.hourly # 02 4 * * * root run-parts /etc/cron.daily # 22 4 * * 0 root run-parts /etc/cron.weekly # 42 4 1 * * root run-parts /etc/cron.monthly # #----------- procmail-list-update.cron ----------------- # # #! /bin/bash # # # assumes NFS set to network home directory # PROCMAILDIR=$NFS/.PROCMAIL # # # outgoing emails are logged here # CC=$PROCMAILDIR/cc # # # add new recipients (from outgoing email) # # IMPORTANT: the last grep line filters out recipients that are handled # # by GOOD_DOMAINS already, update it if necessary # # # cd $PROCMAILDIR # grep "To:" $CC |\ # grep -v In-Reply-To: |\ # grep @ |\ # gawk '{gsub(/[,<>"]/," ");for(n=1;;n++){if($n=="")break;if(index($n,"@")>0)print $n;}}'|\ # grep -v nj.nec.com \ # >> accept-list # # # sort accept-list and make entries unique # sort -u accept-list > a.tmp # mv -f a.tmp accept-list # # # ###################################################################### ############################## # CUSTOMIZATION STARTS HERE: ############################## # Your e-mail address MY_EMAIL=xxx@xxx.xxx # # This should be either blank, or a regex that matches any addresses from which # you get lots of mail that you want to archive but not read. These messages # are stored in file "notifications" and won't get through. NOTIFICATIONS=(root) # This should be either blank, or a regex that matches the To: headers of any # mailing lists you're on. Messages are stored in file "list_archive" and will # get through. LISTS=(opencxx|sourceforge|netflix|connectionists|theory-read) # This should be a regex that matches all domains from which you know you # won't get spammed: GOOD_DOMAINS=(gamasutra|Stellenmarkt|aaai.org|sourceforge|nj.nec.com|knowledgemicro.com|xig.com|uni-paderborn.de|dyndns|shizuoka.ac.jp|unimaas|umbrella|datek|dragon.ca|expedia.com|blizzard.com|ualberta|NECInternal.events|epcor.ca|royal|amazon|relic.com|bioware.com|igda.org|ncix.com|etrade.com|ETRADE@etrade.ca|gxk@Cs.Nott.AC.UK|banffcentre.ca) # This should be a regex that matches spam domains you know (can be empty); # messages are dumped SPAM_DOMAINS=(vmadmin.com|transcentives.net|art.com) # Here you name the domains you want to check for different From: and From domains # these mails will be saved in faked_from CHECK_FROM2=cs.ualberta.ca # Customize this to change the autoreply sent to messages from addresses # that are not in the accept list AUTOREPLY="Hello! Your message has been received, but it hasn't been delivered to me yet. Since I don't have any record of you sending me email from this address before, I need to verify that you're not a spammer. Please just hit 'Reply' and send this message back to me, and your previous message will be delivered, as well as all your future messages. Thanks, and sorry for the inconvenience. /MB --------------------------------------------------------------------- Hallo! Ihre Nachricht ist angekommen, aber sie ist noch nicht an mich weitergeleitet worden. Da Ihre Email-Adresse noch nicht in meiner No-Spam-Liste vorkommt, bitte ich Sie, kurz auf diese Email zu antworten (einfaches "Reply" genuegt). Danach wird Ihre urspruengliche Nachricht und alle nachfolgenden Mitteilungen automatisch an mich weitergereicht. Mit freundlichen Gruessen. /MB " # Customize these if they're not right on your system: TR=/usr/bin/tr CAT=/bin/cat FORMAIL=/usr/local/bin/formail GREP=/bin/grep AWK=/bin/awk HEAD=/usr/bin/head DATE=/bin/date SENDMAIL=/usr/lib/sendmail # this is where the "exists" script resides (***) EXISTS=$HOME/bin/exists # Directory for storing procmail files PMDIR=$HOME/.PROCMAIL MAILDIR=$HOME/.PROCMAIL # Customize these if you want to put addresses and pending messages # elsewhere - make sure PENDING_DIR and PENDING_MSGS_DIR exist! # good files are stored here GOOD=good DB=accept-list DENY_DB=deny-list PENDING_DIR=pending PENDING_MSGS_DIR=pending-msgs ############################################################################# # Don't change anything below this point unless you know what you're doing! # ############################################################################# SHELL=/bin/bash PATH COMSAT VERBOSE=on LOGFILE=logfile # first of all, back up everything! :0 c backup ###################### Vacation ######################### # uncomment before going on vacation # remove vacation.cache # update $HOME/vacation.reply #:0 Whc: vacation$LOCKEXT #* $^To:.*$LOGNAME #* !^FROM_DAEMON #* !^List- #* !^(Mailing-List|Approved-By|BestServHost|Resent-(Message-ID|Sender)): #* !^Sender: (.*-errors@|owner-) #* !^TOnev@bostic.com #* !^X-[^:]*-List: #* !^X-(Authentication-Warning|Loop|Sent-To|(Listprocessor|Mailman)-Version): #* !$^From +$LOGNAME(@| |$) #| $FORMAIL -rD 8192 vacation.cache # #:0 ehc #| ($FORMAIL -rI"Precedence: junk" \ # -A"X-Loop: $LOGNAME@$HOST" \ # ; \ # cat $HOME/vacation.reply \ # ) | $SENDMAIL $SENDMAILFLAGS -t ###################### Vacation ######################### # Get sender :0 hw FROM=|$FORMAIL -rzxTo: | $TR "/<>" " " | $AWK '{for(n=1;;n++){if($n=="")break;if(index($n,"@")>0)print $n;}}' # Get sender name (from:) :0 hw FROM2=|$FORMAIL -xFrom: | $TR "/<>" " " | $AWK '{for(n=1;;n++){if($n=="")break;if(index($n,"@")>0)print $n;}}' # Get addressee (To:) :0 hw FROM3=|$FORMAIL -xTo: | $TR "/<>" " " | $AWK '{for(n=1;;n++){if($n=="")break;if(index($n,"@")>0)print $n;}}' MSGID=`$DATE +%Y:%m:%d-%H:%M:%S` # dump mailer messages for reply requests (grep Body+Header) :0 BH * ^FROM_MAILER #* ^From.*(MAILER-DAEMON|postmaster) * ^Subject:.*Verify.*for.*$MY_EMAIL bounced-spammer # let other mailer messages through :0 BH * ^FROM_MAILER * ^error:.*nosuchuser: { :0 c $GOOD :0: $DEFAULT } # dump all other mailer messages :0 BH * ^FROM_MAILER bounced-spammer # If message is from a spam domain then dump it :0 * SPAM_DOMAINS ?? (.) * $ FROM ?? $SPAM_DOMAINS /dev/null :0 * SPAM_DOMAINS ?? (.) * $ FROM2 ?? $SPAM_DOMAINS /dev/null :0 * SPAM_DOMAINS ?? (.) * $ FROM3 ?? $SPAM_DOMAINS /dev/null # If message is from a known spammer then dump it :0 h * ? $GREP -i ^$FROM $DENY_DB /dev/null :0 h * ? $GREP -i ^$FROM2 $DENY_DB /dev/null :0 h * ? $GREP -i ^$FROM3 $DENY_DB /dev/null # archive notifications :0 * NOTIFICATIONS ?? (.) * $ FROM2 ?? $NOTIFICATIONS notifications # deliver messages from good "From:" domains :0 * GOOD_DOMAINS ?? (.) * $ FROM2 ?? $GOOD_DOMAINS { :0 c $GOOD :0: $DEFAULT } # deliver messages if From address is in accept-list :0 * ? $GREP -i ^$FROM $DB { :0 c $GOOD :0: $DEFAULT } # deliver messages if From: address is in accept-list :0 * ? $GREP -i ^$FROM2 $DB { :0 c $GOOD :0: $DEFAULT } # get rid of mails with "From" domain in CHECK_FROM2 and # "From:" domain not in CHECK_FROM2 :0 * CHECK_FROM2 ?? (.) * $ FROM ?? $CHECK_FROM2 * ! $ FROM2 ?? $CHECK_FROM2 faked_from # deliver messages from good "From" domains :0 * GOOD_DOMAINS ?? (.) * $ FROM ?? $GOOD_DOMAINS { :0 c $GOOD :0: $DEFAULT } # If message is to/from a list we're on then archive it and let it through :0 * LISTS ?? (.) #* $ ^(To|From).*$LISTS * $ (^TO_|^From).*$LISTS { :0 c list-archive :0 c $GOOD :0: $DEFAULT } # Else if message has chicken then register sender and deliver stored messages :0 Efw * $ ^Subject:.*Verify.*for.*$MY_EMAIL * ? echo $FROM >> $DB * ? $EXISTS $PENDING_DIR/$FROM |($CAT $PENDING_DIR/$FROM || true ) # Else if sender isn't in DB then send request for chicken and store message :0 E * $ ! ^X-Loop: $MY_EMAIL * ! ? $GREP -i ^$FROM $DB { :0 c * ! ? $EXISTS $PENDING_DIR/$FROM |($FORMAIL -rt -A"X-Loop: $MY_EMAIL" -I"Subject: Verify $FROM for $MY_EMAIL"; echo "$AUTOREPLY"; echo; ) | $SENDMAIL -t :0 c "$PENDING_MSGS_DIR/$MSGID" :0: $PENDING_DIR/$FROM } # Else deliver normally #