The Perfect SpamSnake - Ubuntu Jeos 10.10 Maverick Meerkat - Page 4
On this page
- 14. KAM
- 15. ScamNailer
- 16. Firewalling the SpamSnake with Firehol
- Install Firehol
- Firehol Settings:
- 17. Apply Relay Recipients
- Install Dependencies
- Postmap the file to create the hash db
- 18. Install Webmin (Optional):
- 19. Automatically Add A Disclaimer To Outgoing Emails With alterMIME (Optional)
- 20. Screenshots
- Congratulations
14. KAM
vi /etc/cron.daily/kam.sh
Add the following content and
chmod +x /etc/cron.daily/kam.sh
#!/bin/bash # Original version modified by Andrew MacLachlan ([email protected]) # Added additional MailScanner restarts on inital restart failure # Made script run silently for normal (successful) operation # Increased UPDATEMAXDELAY to 900 from 600 # Insert a random delay up to this value, to spread virus updates round # the clock. 1800 seconds = 30 minutes. # Set this to 0 to disable it. UPDATEMAXDELAY=0 if [ -f /opt/MailScanner/var/MailScanner ] ; then . /opt/MailScanner/var/MailScanner fi export UPDATEMAXDELAY if [ "x$UPDATEMAXDELAY" = "x0" ]; then : else logger -p mail.info -t KAM.cf.sh Delaying cron job up to $UPDATEMAXDELAY seconds perl -e "sleep int(rand($UPDATEMAXDELAY));" fi # JKF Fetch KAM.cf #echo Fetching KAM.cf... cd /etc/mail/spamassassin rm -f KAM.cf wget -O KAM.cf http://www.peregrinehw.com/downloads/SpamAssassin/contrib/KAM.cf > /dev/null 2>&1 if [ "$?" = "0" ]; then #echo It completed and fetched something if ( tail -10 KAM.cf | grep -q '^#.*EOF' ); then # echo It succeeded so make a backup cp -f KAM.cf KAM.cf.backup else echo ERROR: Could not find EOF marker cp -f KAM.cf.backup KAM.cf fi else echo It failed to complete properly cp -f KAM.cf.backup KAM.cf fi #echo Reloading MailScanner and SpamAssassin configuration rules /etc/init.d/mailscanner reload > /dev/null 2>&1 if [ $? != 0 ] ; then echo "MailScanner reload failed - Retrying..." /etc/init.d/mailscanner force-reload if [ $? = 0 ] ; then echo "MailScanner reload succeeded." else echo "Stopping MailScanner..." /etc/init.d/mailscanner stop echo "Waiting for a minute..." perl -e "sleep 60;" echo "Attemping to start MailScanner..." /etc/init.d/mailscanner start fi fi
15. ScamNailer
vi /usr/sbin/update_scamnailer
and
chmod +x /usr/sbin/update_scamnailer
*Note: Content is in scamnailer.doc.)
Add it to cron:
@daily /usr/sbin/update_scamnailer &> /dev/null #Update Scamnailer
16. Firewalling the SpamSnake with Firehol
Introduction
Firehol is a stateful iptables packet filtering firewall configurator. It is abstracted, extensible, easy and powerful. It can handle any kind of firewall, but most importantly, it gives you the means to configure it, the same way you think of it.
Install Firehol
apt-get install firehol
Firehol Settings:
vi /etc/default/firehol
and change the following:
START_FIREHOL=YES
vi /etc/firehol/firehol.conf
and add the following:
version 5 # Accept all client traffic on any interface interface any internet protection strong server "icmp ping ICMP ssh http https telnet webmin dns dcc echo smtp" accept client all accept
This filters all incoming connections that are not related to the above services. If you want to be less polite, you can drop them by adding the following after 'protection strong': policy drop
vi /usr/sbin/get-iana
with the following and
chmod +x /usr/sbin/get-iana
#!/bin/bash # $Id: get-iana.sh,v 1.13 2010/09/12 13:55:00 jcb Exp $ # # $Log: get-iana.sh,v $ # Revision 1.13 2010/09/12 13:55:00 jcb # Updated for latest IANA reservations format. # # Revision 1.12 2008/03/17 22:08:43 ktsaou # Updated for latest IANA reservations format. # # Revision 1.11 2007/06/13 14:40:04 ktsaou # *** empty log message *** # # Revision 1.10 2007/05/05 23:38:31 ktsaou # Added support for external definitions of: # # RESERVED_IPS # PRIVATE_IPS # MULTICAST_IPS # UNROUTABLE_IPS # # in files under the same name in /etc/firehol/. # Only RESERVED_IPS is mandatory (firehol will complain if it is not there, # but it will still work without it), and is also the only file that firehol # checks how old is it. If it is 90+ days old, firehol will complain again. # # Changed the supplied get-iana.sh script to generate the RESERVED_IPS file. # FireHOL also instructs the user to use this script if the file is missing # or is too old. # # Revision 1.9 2007/04/29 19:34:11 ktsaou # *** empty log message *** # # Revision 1.8 2005/06/02 15:48:52 ktsaou # Allowed 127.0.0.1 to be in RESERVED_IPS # # Revision 1.7 2005/05/08 23:27:23 ktsaou # Updated RESERVED_IPS to current IANA reservations. # # Revision 1.6 2004/01/10 18:44:39 ktsaou # Further optimized and reduced PRIVATE_IPS using: # http://www.vergenet.net/linux/aggregate/ # # The supplied get-iana.sh uses .aggregate. if it finds it in the path. # (aggregate is the name of this program when installed on Gentoo) # # Revision 1.5 2003/08/23 23:26:50 ktsaou # Bug #793889: # Change #!/bin/sh to #!/bin/bash to allow FireHOL run on systems that # bash is not linked to /bin/sh. # # Revision 1.4 2002/10/27 12:44:42 ktsaou # CVS test # # # Program that downloads the IPv4 address space allocation by IANA # and creates a list with all reserved address spaces. # IPV4_ADDRESS_SPACE_URL="http://www.iana.org/assignments/ipv4-address-space/ipv4-address-space.txt" # The program will match all rows in the file which start with a number, have a slash, # followed by another number, for which the following pattern will also match on the # same rows IANA_RESERVED="(RESERVED|UNALLOCATED)" # which rows that are matched by the above, to ignore # (i.e. not include them in RESERVED_IPS)? #IANA_IGNORE="(Multicast|Private use|Loopback|Local Identification)" IANA_IGNORE="Multicast" tempfile="/tmp/iana.$$.$RANDOM" AGGREGATE="`which aggregate 2>/dev/null`" if [ -z "${AGGREGATE}" ] then AGGREGATE="`which aggregate 2>/dev/null`" fi if [ -z "${AGGREGATE}" ] then echo >&2 echo >&2 echo >&2 "WARNING" echo >&2 "Please install 'aggregate' to shrink the list of IPs." echo >&2 echo >&2 fi echo >&2 echo >&2 "Fetching IANA IPv4 Address Space, from:" echo >&2 "${IPV4_ADDRESS_SPACE_URL}" echo >&2 wget -O - -proxy=off "${IPV4_ADDRESS_SPACE_URL}" |\ egrep " *[0-9]+/[0-9]+.*${IANA_RESERVED}" |\ egrep -vi "${IANA_IGNORE}" |\ sed -e 's:^ *\([0-9]*/[0-9]*\).*:\1:' |\ ( while IFS="/" read range net do if [ ! $net -eq 8 ] then echo >&2 "Cannot handle network masks of $net bits ($range/$net)" continue fi first=`echo $range | cut -d '-' -f 1` first=`expr $first + 0` last=`echo $range | cut -d '-' -f 2` last=`expr $last + 0` x=$first while [ ! $x -gt $last ] do # test $x -ne 127 && echo "$x.0.0.0/$net" echo "$x.0.0.0/$net" x=$[x + 1] done done ) | \ ( if [ ! -z "${AGGREGATE}" -a -x "${AGGREGATE}" ] then "${AGGREGATE}" else cat fi ) >"${tempfile}" echo >&2 echo >&2 echo >&2 "FOUND THE FOLLOWING RESERVED IP RANGES:" printf "RESERVED_IPS=\"" i=0 for x in `cat ${tempfile}` do i=$[i + 1] printf "${x} " done printf "\"\n" if [ $i -eq 0 ] then echo >&2 echo >&2 echo >&2 "Failed to find reserved IPs." echo >&2 "Possibly the file format has been changed, or I cannot fetch the URL." echo >&2 rm -f ${tempfile} exit 1 fi echo >&2 echo >&2 echo >&2 "Differences between the fetched list and the list installed in" echo >&2 "/etc/firehol/RESERVED_IPS:" echo >&2 "# diff /etc/firehol/RESERVED_IPS ${tempfile}" diff /etc/firehol/RESERVED_IPS ${tempfile} if [ $? -eq 0 ] then echo >&2 echo >&2 "No differences found." echo >&2 rm -f ${tempfile} exit 0 fi echo >&2 echo >&2 echo >&2 "Would you like to save this list to /etc/firehol/RESERVED_IPS" echo >&2 "so that FireHOL will automatically use it from now on?" echo >&2 while [ 1 = 1 ] do printf >&2 "yes or no > " read x case "${x}" in yes) cp -f /etc/firehol/RESERVED_IPS /etc/firehol/RESERVED_IPS.old 2>/dev/null cat "${tempfile}" >/etc/firehol/RESERVED_IPS || exit 1 echo >&2 "New RESERVED_IPS written to '/etc/firehol/RESERVED_IPS'." echo "Firehol will now be restart" sleep 3 /etc/init.d/firehol restart break ;; no) echo >&2 "Saved nothing." break ;; *) echo >&2 "Cannot understand '${x}'." ;; esac done rm -f ${tempfile}
vi /usr/sbin/update-iana
with the following content and
chmod +x /usr/sbin/update-iana
#!/bin/sh /usr/sbin/get-iana < /etc/firehol/get-iana-answerfile
vi /etc/firehol/get-iana-answerfile
with the following content:
yes
Run the script to update RESERVED_IPS:
/usr/sbin/update-iana
Now your server is set up to only accept connections for the services you allowed.
Add it to cron:
@monthly /usr/sbin/update-iana &> /dev/null #Update firehol reserved ips
17. Apply Relay Recipients
The following directions are meant for people using Microsoft Exchange 2000 or Microsoft Exchange 2003.
This page describes how to configure your mail gateway to periodically get a list of valid recipient email addresses from your Exchange system. By doing this, you can configure your server to automatically reject any email addressed to invalid addresses. This will reduce the load on your exchange server, since it no longer has to process non-delivery reports, and it will reduce the load on your postfix server since it won't have to perform spam and virus scanning on the message.
Install Dependencies
Install the perl module Net::LDAP:
perl -MCPAN -e shell
install Net::LDAP
vi /usr/bin/getadsmtp.pl
with the following:
#!/usr/bin/perl -T -w # This script will pull all users' SMTP addresses from your Active Directory # (including primary and secondary email addresses) and list them in the # format "[email protected] OK" which Postfix uses with relay_recipient_maps. # Be sure to double-check the path to perl above. # This requires Net::LDAP to be installed. To install Net::LDAP, at a shell # type "perl -MCPAN -e shell" and then "install Net::LDAP" use Net::LDAP; use Net::LDAP::Control::Paged; use Net::LDAP::Constant ( "LDAP_CONTROL_PAGED" ); # Enter the path/file for the output $VALID = "/etc/postfix/relay_recipients"; open VALID, ">$VALID" or die "CANNOT OPEN $VALID $!"; # Enter the FQDN of your Active Directory domain controllers below $dc1="domaincontroller1.example.com"; $dc2="domaincontroller2.example.com"; # Enter the LDAP container for your userbase. # The syntax is CN=Users,dc=example,dc=com # This can be found by installing the Windows 2000 Support Tools # then running ADSI Edit. # In ADSI Edit, expand the "Domain NC [domaincontroller1.example.com]" & # you will see, for example, DC=example,DC=com (this is your base). # The Users Container will be specified in the right pane as # CN=Users depending on your schema (this is your container). # You can double-check this by clicking "Properties" of your user # folder in ADSI Edit and examining the "Path" value, such as: # LDAP://domaincontroller1.example.com/CN=Users,DC=example,DC=com # which would be $hqbase="cn=Users,dc=example,dc=com" # Note: You can also use just $hqbase="dc=example,dc=com" $hqbase="cn=Users,dc=example,dc=com"; # Enter the username & password for a valid user in your Active Directory # with username in the form cn=username,cn=Users,dc=example,dc=com # Make sure the user's password does not expire. Note that this user # does not require any special privileges. # You can double-check this by clicking "Properties" of your user in # ADSI Edit and examining the "Path" value, such as: # LDAP://domaincontroller1.example.com/CN=user,CN=Users,DC=example,DC=com # which would be $user="cn=user,cn=Users,dc=example,dc=com" # Note: You can also use the UPN login: "user\@example.com" $user="cn=user,cn=Users,dc=example,dc=com"; $passwd="password"; # Connecting to Active Directory domain controllers $noldapserver=0; $ldap = Net::LDAP->new($dc1) or $noldapserver=1; if ($noldapserver == 1) { $ldap = Net::LDAP->new($dc2) or die "Error connecting to specified domain controllers $@ \n"; } $mesg = $ldap->bind ( dn => $user, password =>$passwd); if ( $mesg->code()) { die ("error:", $mesg->error_text((),"\n")); } # How many LDAP query results to grab for each paged round # Set to under 1000 for Active Directory $page = Net::LDAP::Control::Paged->new( size => 990 ); @args = ( base => $hqbase, # Play around with this to grab objects such as Contacts, Public Folders, etc. # A minimal filter for just users with email would be: # filter => "(&(sAMAccountName=*)(mail=*))" filter => "(& (mailnickname=*) (| (&(objectCategory=person) (objectClass=user)(!(homeMDB=*))(!(msExchHomeServerName=*))) (&(objectCategory=person)(objectClass=user)(|(homeMDB=*) (msExchHomeServerName=*)))(&(objectCategory=person)(objectClass=contact)) (objectCategory=group)(objectCategory=publicFolder) ))", control => [ $page ], attrs => "proxyAddresses", ); my $cookie; while(1) { # Perform search my $mesg = $ldap->search( @args ); # Filtering results for proxyAddresses attributes foreach my $entry ( $mesg->entries ) { my $name = $entry->get_value( "cn" ); # LDAP Attributes are multi-valued, so we have to print each one. foreach my $mail ( $entry->get_value( "proxyAddresses" ) ) { # Test if the Line starts with one of the following lines: # proxyAddresses: [smtp|SMTP]: # and also discard this starting string, so that $mail is only the # address without any other characters... if ( $mail =~ s/^(smtp|SMTP)://gs ) { print VALID $mail." OK\n"; } } } # Only continue on LDAP_SUCCESS $mesg->code and last; # Get cookie from paged control my($resp) = $mesg->control( LDAP_CONTROL_PAGED ) or last; $cookie = $resp->cookie or last; # Set cookie in paged control $page->cookie($cookie); } if ($cookie) { # We had an abnormal exit, so let the server know we do not want any more $page->cookie($cookie); $page->size(0); $ldap->search( @args ); # Also would be a good idea to die unhappily and inform OP at this point die("LDAP query unsuccessful"); } # Add additional restrictions, users, etc. to the output file below. #print VALID "user\@domain1.com OK\n"; #print VALID "user\@domain2.com 550 User unknown.\n"; #print VALID "domain3.com 550 User does not exist.\n"; close VALID;
Next set the permissions on the file to allow it to be executed:
chmod +x /usr/bin/getadsmtp.pl
Edit the file to customize it for your specific domain. Since the file is read only, you will need to use :w! to save the file in vi.
1. Set $dc1 and $dc2 to the fully qualified domain names or IP addresses of 2 of your domain controllers.
2. Set $hqbase equal to the LDAP path to the container or organizational unit which holds the email accounts for which you wish to get the email addresses.
3. Set $user and $passwd to indicate which user account should be used to access this information. This account only needs to be a member of the domain, so it would be a good idea to setup an account specifically for this.
Try running the script. If it works correctly, it will create /etc/postfix/relay_recipients.
Note that if your postfix server is separated from your active directory controllers by a firewall, you will need to open TCP port 389 from the postfix server to the ADCs.
getadsmtp.pl
At this point, you may want to edit /etc/postfix/relay_recipients and edit out any unwanted email addresses as this script imports everything.
Postmap the file to create the hash db
postmap /etc/postfix/relay_recipients
postfix reload
Finally, you may want to set up a cron job to periodically update and build the /etc/postfix/relay_recipients.db file. You can set up a script called /usr/bin/update-relay-recipients.sh: (Optional)
vi /usr/bin/update-relay-recipients.sh
with the following and
chmod +x /usr/bin/update-relay_recipients.sh
#!/bin/sh /usr/bin/getadsmtp.pl postmap /etc/postfix/relay_recipients postfix reload
Don't forget to make sure the following is in your /etc/postfix/main.cf file:
relay_recipient_maps = hash:/etc/postfix/relay_recipients
Add it to cron:
30 2 * * * /usr/bin/update-relay-recipients.sh #syncronize relay_recipients with Active Directory addresses
:Note that this cron job will run every day at 2:30 AM to update the database file. You may want to run yours more frequently or not depending on how often you add new email users to your system.
18. Install Webmin (Optional):
apt-get install perl libnet-ssleay-perl libauthen-pam-perl libio-pty-perl apt-show-versions
cd /tmp && wget http://mirrors.kernel.org/ubuntu/pool/universe/libm/libmd5-perl/libmd5-perl_2.03-1_all.deb
dpkg -i libmd5-perl_2.03-1_all.deb
wget http://downloads.sourceforge.net/project/webadmin/webmin/1.520/webmin_1.520_all.deb
dpkg --install webmin_1.520_all.deb
19. Automatically Add A Disclaimer To Outgoing Emails With alterMIME (Optional)
This tutorial shows how to install and use alterMIME. alterMIME is a tool that can automatically add a disclaimer to emails. In this article I will explain how to install it as a Postfix filter on Ubuntu.
Installing alterMIME:
apt-get install altermime
Next we create the user filter with the home directory /var/spool/filter - alterMIME will be run as that user:
useradd -r -c "Postfix Filters" -d /var/spool/filter filter
mkdir /var/spool/filter
chown filter:filter /var/spool/filter
chmod 750 /var/spool/filter
Afterwards we create the script /etc/postfix/disclaimer which executes alterMIME. Ubuntu's alterMIME package comes with a sample script that we can simply copy to /etc/postfix/disclaimer:
cp /usr/share/doc/altermime/examples/postfix_filter.sh /etc/postfix/disclaimer
chgrp filter /etc/postfix/disclaimer
chmod 750 /etc/postfix/disclaimer
Now the problem with this script is that it doesn't distinguish between incoming and outgoing emails - it simply adds a disclaimer to all mails. Typically you want disclaimers only for outgoing emails, and even then not for all sender addresses. Therefore I've modified the /etc/postfix/disclaimer script a little bit - we'll come to that in a minute.
Right now, we create the file /etc/postfix/disclaimer_addresses which holds all sender email addresses (one per line) for which alterMIME should add a disclaimer:
vi /etc/postfix/disclaimer_addresses
[email protected] [email protected] [email protected]
Now we open /etc/postfix/disclaimer and modify it as follows (I have marked the parts that I've changed):
vi /etc/postfix/disclaimer
#!/bin/sh # Localize these. INSPECT_DIR=/var/spool/filter SENDMAIL=/usr/sbin/sendmail ####### Changed From Original Script ####### DISCLAIMER_ADDRESSES=/etc/postfix/disclaimer_addresses ####### Changed From Original Script END ####### # Exit codes from <sysexits.h> EX_TEMPFAIL=75 EX_UNAVAILABLE=69 # Clean up when done or when aborting. trap "rm -f in.$$" 0 1 2 3 15 # Start processing. cd $INSPECT_DIR || { echo $INSPECT_DIR does not exist; exit $EX_TEMPFAIL; } cat >in.$$ || { echo Cannot save mail to file; exit $EX_TEMPFAIL; } ####### Changed From Original Script ####### # obtain From address from_address=`grep -m 1 "From:" in.$$ | cut -d "<" -f 2 | cut -d ">" -f 1` if [ `grep -wi ^${from_address}$ ${DISCLAIMER_ADDRESSES}` ]; then /usr/bin/altermime --input=in.$$ \ --disclaimer=/etc/postfix/disclaimer.txt \ --disclaimer-html=/etc/postfix/disclaimer.txt \ --xheader="X-Copyrighted-Material: Please visit http://www.company.com/privacy.htm" || \ { echo Message content rejected; exit $EX_UNAVAILABLE; } fi ####### Changed From Original Script END ####### $SENDMAIL "$@" <in.$$ exit $?
Next we need the text file /etc/postfix/disclaimer.txt which holds our disclaimer text. Ubuntu's alterMIME package comes with a sample text that we can use for now (of course, you can modify it if you like):
cp /usr/share/doc/altermime/examples/disclaimer.txt /etc/postfix/disclaimer.txt
Finally we have to tell Postfix that it should use the /etc/postfix/disclaimer script to add disclaimers to outgoing emails. Open /etc/postfix/master.cf and add -o content_filter=dfilt: to the smtp line:
vi /etc/postfix/master.cf
# # Postfix master process configuration file. For details on the format # of the file, see the master(5) manual page (command: "man 5 master"). # # ========================================================================== # service type private unpriv chroot wakeup maxproc command + args # (yes) (yes) (yes) (never) (100) # ========================================================================== smtp inet n - - - - smtpd -o content_filter=dfilt: [...]
At the end of the same file, add the following two lines:
[...] dfilt unix - n n - - pipe flags=Rq user=filter argv=/etc/postfix/disclaimer -f ${sender} -- ${recipient}
Restart Postfix afterwards:
/etc/init.d/postfix restart
That's it! Now a disclaimer should be added to outgoing emails sent from the addresses listed in /etc/postfix/disclaimer_addresses.
20. Screenshots
Congratulations
You should now have a complete working SpamSnake with all the goodies :-)