The Perfect SpamSnake - Ubuntu 8.04 LTS - Page 3

2 DNS Server


apt-get install bind9

For security reasons we want to run BIND chrooted so we have to do the following steps:

/etc/init.d/bind9 stop

Edit the file /etc/default/bind9 so that the daemon will run as the unprivileged user bind, chrooted to /var/lib/named. Modify the line: OPTIONS="-u bind" so that it reads OPTIONS="-u bind -t /var/lib/named":

vi /etc/default/bind9

OPTIONS="-u bind -t /var/lib/named"

Create the necessary directories under /var/lib:

mkdir -p /var/lib/named/etc
mkdir /var/lib/named/dev
mkdir -p /var/lib/named/var/cache/bind
mkdir -p /var/lib/named/var/run/bind/run

Then move the config directory from /etc to /var/lib/named/etc:

mv /etc/bind /var/lib/named/etc

Create a symlink to the new config directory from the old location (to avoid problems when bind gets updated in the future):

ln -s /var/lib/named/etc/bind /etc/bind

Make null and random devices, and fix permissions of the directories:

mknod /var/lib/named/dev/null c 1 3
mknod /var/lib/named/dev/random c 1 8
chmod 666 /var/lib/named/dev/null /var/lib/named/dev/random
chown -R bind:bind /var/lib/named/var/*
chown -R bind:bind /var/lib/named/etc/bind

We need to modify /etc/default/syslogd so that we can still get important messages logged to the system logs. Modify the line: SYSLOGD="" so that it reads SYSLOGD="-a /var/lib/named/dev/log":

vi /etc/default/syslogd

SYSLOGD="-a /var/lib/named/dev/log"

Restart the logging daemon:

/etc/init.d/sysklogd restart

Start up BIND, and check /var/log/syslog for errors:

/etc/init.d/bind9 start



In order to install MySQL, we run

apt-get install mysql-server mysql-client libmysqlclient15-dev

You will be asked to provide a password for the MySQL root user - this password is valid for the user root@localhost as well as, so we don't have to specify a MySQL root password manually later on (as was the case with previous Ubuntu versions):

New password for the MySQL "root" user: <-- yourrootsqlpassword

We want MySQL to listen on all interfaces, not just localhost, therefore we edit /etc/mysql/my.cnf and comment out the line bind-address =

vi /etc/mysql/my.cnf

#bind-address =

Then we restart MySQL:

/etc/init.d/mysql restart

Now check that networking is enabled. Run

netstat -tap | grep mysql

The output should look like this:

tcp 0 0 *:mysql *.* LISTEN 5286/mysqld


4 Apache with PHP5 and Ruby

Now we install Apache:

apt-get install apache2 apache2-doc apache2-mpm-prefork apache2-utils libexpat1 ssl-cert

Next we install PHP5 and Ruby (both as Apache modules):

apt-get install libapache2-mod-php5 libapache2-mod-ruby php5 php5-common php5-curl php5-dev php5-gd php5-idn php-pear php5-imagick php5-imap php5-mcrypt php5-memcache php5-mhash php5-ming php5-mysql php5-pspell php5-recode php5-snmp php5-sqlite php5-tidy php5-xmlrpc php5-xsl php5-sqlite php5-tidy php5-xmlrpc php5-xsl

You will be asked the following question:

Continue installing libc-client without Maildir support? <-- Yes

Next we edit /etc/apache2/mods-available/dir.conf and change the following:

vi /etc/apache2/mods-available/dir.conf

DirectoryIndex index.html index.htm index.shtml index.cgi index.php index.php3 index.xhtml

Now we have to enable some Apache modules (SSL, rewrite, suexec, and include):

a2enmod ssl
a2enmod rewrite
a2enmod suexec
a2enmod include

Reload the Apache configuration:

/etc/init.d/apache2 force-reload


4.1 Fix for Imagick

Because of a bug that causes the following error, the below must be done as a workaround:

PHP Warning: PHP Startup: Unable to load dynamic library '/usr/lib/php5/20060613/' - cannot open shared object file: No such file or directory in Unknown on line 0

apt-get remove php5-imagick

apt-get install libmagick9-dev

pecl install imagick

Edit /etc/php5/apache2/php.ini and add the following:

vi /etc/php5/apache2/php.ini

/etc/init.d/apache2 restart


5 Synchronize the System Clock

It is a good idea to synchronize the system clock with an NTP (network time protocol) server over the internet. Simply run

apt-get install ntp ntpdate

and your system time will always be in sync.


6 Setting up Postfix

apt-get install postfix postfix-pcre postfix-mysql postfix-ldap cabextract lha unrar razor pyzor spamassassin

You will be asked two questions. Answer as follows:

General type of mail configuration: <-- Internet Site
System mail name: <--

Stop Postfix:

postfix stop


6.1 Edit

BTW watch for the two Postfix configuration files, both located in the /etc/postfix folder. More than one admin has gotten confused between and!

First back up the current

cp /etc/postfix/ /etc/postfix/


vi /etc/postfix/

We need to add two items below the pickup service type. The pickup service "picks up" local mail (local meaning "on this machine") and delivers it. This is a way to bypass content filtering for mail generated by this machine.

Add this just below the 'pickup' service type:

         -o content_filter=
         -o receive_override_options=no_header_body_checks

It should look like this when you are done:

pickup fifo n - - 60 1 pickup
   -o content_filter=
   -o receive_override_options=no_header_body_checks


6.2 Edit

First we need to backup the file.

cp /etc/postfix/ /etc/postfix/


6.2.1 alias_maps

We simply need to make a correction to the default setting here:

postconf -e "alias_maps = hash:/etc/aliases"

Create the aliases file:


Since our system will be configured not to store any local mails, this will be ignored.


6.2.2 myorigin

The domain name that mail created on this machine appears to come from. For example, if cron sends mail to "" it will appear to come from "".

postconf -e "myorigin ="

Obviously, in the above, and all the following commands, replace my example parameters, like "", with your own specific values.


6.2.3 myhostname

The fully-qualified domain name (FQDN) of the machine running the Postfix system.

postconf -e "myhostname ="


6.2.4 mynetworks

These are the machines I trust, and will relay mail for, to any destination. If you will be dealing with multiple internal mail servers, and/or want to allow several machines and/or subnets to relay through this server (careful!), just add them to this parameter in CIDR format and seperate the networks like this:

postconf -e "mynetworks =,"

The is there to allow the local server to send, you need to at least put this one in. outbound trusted relay IP

If you'd like your SpamSnake to handle outgoing emails as well, be sure to add your local network to the list e.g. If your mailserver is and you only want to trust only that IP, add You just have to setup your mailserver to relay (smarthost) to your SpamSnake.


6.2.5 message_size_limit

Maximum size email that Postfix will let in the "front door".

postconf -e "message_size_limit = 10485760"

The above allows email up to 10MB, the value is in bytes (10*1024*1024). Mail larger than this may possibly get bypassed by the anti-virus scanner (ClamAV). You could increase this if you also configure ClamAV to scan files larger than 10MB. If you allow messages larger than 10MB, keep an eye on RAM.


6.2.6 local_transport

Return an error message for local delivery attempts.

postconf -e "local_transport = error:No local mail delivery"


6.2.7 mydestination

An empty mydestination tells Postfix this machine is not the final destination.

postconf -e "mydestination = "


6.2.8 local_recipient_maps

An empty local_recipient_maps tells Postfix there are no local mailboxes.

postconf -e "local_recipient_maps = "


6.2.9 virtual_alias_maps

Our spamfilter must be able to receive mail for postmaster@yourIP. Reportedly, some things actually expect this ability to exist. We will also allow mail to abuse@yourIP. Since we do not allow local mail delivery, mail addressed to our spamfilter's IP address will get rejected with an error message. Setting up virtual_alias_maps allows email to these two accounts to be forwarded to an inside address. Make sure your Exchange server is set up to receive messages addressed to "root", "postmaster" and "abuse".

Set up a reference to the virtual file:

postconf -e "virtual_alias_maps = hash:/etc/postfix/virtual"

Then edit the virtual file:

vi /etc/postfix/virtual

Add these lines to the top of the virtual file:


Save and exit the file, then create the binary file that Postfix will use:

postmap /etc/postfix/virtual


6.2.10 relay_recipient_maps

We are going to build a table of every single user in every single domain that we accept mail for.

Set up a reference to a file we will create to store the data:

postconf -e "relay_recipient_maps = hash:/etc/postfix/relay_recipients"

Then edit relay_recipients:

vi /etc/postfix/relay_recipients

For the moment, we are going to accept mail for all users in our domain(s) so enter each domain you accept mail for in the following format: OK OK

Then create the binary file that Postfix will use:

postmap /etc/postfix/relay_recipients

The entries above are temporary. They are wildcards that allow mail to your domains. You MUST remove the entries above at some point in the near future and replace them with every single one of your valid recipients' email addresses. When you are ready to enter each user individually in the relay_recipients file, you would first remove (or comment out) the data above that allows mail to all users in the domain, and then list each user individually in the form: OK OK


6.2.11 transport_maps

Tells Postfix where to look for a transport file. We use the transport file to tell Postfix where to forward valid mail for our domain(s). Setting up transport is similar to setting up relay_recipients.

Create a reference to it in

postconf -e "transport_maps = hash:/etc/postfix/transport"

Then edit transport:

vi /etc/postfix/transport

Add 1 new line for each domain for which you will be handling mail, similar to the example below. The IP address is that of whatever server is the final destination of messages addressed to our domain(s) (our Exchange server). It does not matter where you place these items in the file, but I like to put them at the top. smtp:[192.168.0.x] smtp:[192.168.0.x]

Include the brackets on these lines!. You can also use FQDN hostname instead of an IP address (i.e. smtp:[]).

Now to create the binary file Postfix will use:

postmap /etc/postfix/transport


6.2.12 relay_domains

What destination domains (and subdomains thereof) this system will relay mail for.

postconf -e "relay_domains = hash:/etc/postfix/relay_domains"

Edit relay_domains:

vi /etc/postfix/relay_domains

Add 1 new line for each domain for which you will be handling mail, similar to the example below: OK OK

This file currently has a very similar format to relay_recipients do not mistake the two. This file cannot have '@' in front of the domain name. Just thought I'd mention it, some very smart people have been known to have done this...

Then create the binary file Postfix will use:

postmap /etc/postfix/relay_domains

Share this page:

42 Comment(s)

Add comment


From: at: 2008-09-17 15:07:59

Can I implement this solution on a machine running as a firewall with 2 network interfaces? One for the Internet and one for the internal network?


From: Anonymous at: 2008-10-06 18:12:18

In theory I don't see why not, but it probably isn't a good idea.  Doing that way makes the spamsnake a bridge between the two networks that isn't protected by the firewall.  The only way to make it secure is to have the firewall accept mail on the external interface, with the appropriate firewall blocking, pass it to the spamsnake on the internal interface for processing and then forward it to a mail server for distribution.  If the spamsnake accepts mail on the external interface directly, it will bypass the firewall.

The more secure option is to have the spamsnake be a separate external machine, accept and process all mail there and only pass the legitimate mail to the internal network via the external interface of the firewall.  The other benefit of this method is it reduces the load on the firewall since all the spam, and the associated connections, has been dumped before it reaches the firewall.

From: at: 2008-05-10 13:27:38

I've been using file based Greylisting for more than an year and I would say it is faster than the DB based ones. I am using tumgreyspf.

From: at: 2008-05-13 12:35:22

When it comes to the addons, it's really your choice which one you want to use.  I've been quite lucky with the db setup so that's why I use it. 

Thanks for your recommendation though.  If I have any problems with my current setup, I would be more than happy to give your recommendation a shot.

From: at: 2008-08-05 11:00:51

Hello ! Any specific reason for using MailScanner and not AMaViS ? Just out of curiosity. Regards, Sebastian M Juergse

From: at: 2008-10-14 16:04:23

I tried this setup with Amavis but thought MailScanner was a bit faster.

From: ctrl at: 2010-01-09 09:30:03

Large a thank you for your tutorial! I have used SpamSnake for 6 months and I am magic.

This morning, SpamSnake informs me of a very high number of message containing the Virus (Exploit.PDF-9669). Information taken, it acts of a bug in Clamav which I decide to update by a “apt-get install clamav clamav-demon clamav-fresclam” then I launch “freshclam” to recover the update of the database.

However, and in a more total way, I wishes knowledge if I could, without risk for the configuration of SpamSnake, throw a “apt-get upgrade”.

Better greetings,

From: Rob at: 2010-01-21 11:42:03

I installed this. Tested it and now it is running for all our domains and it works perfect! thanks :)

From: Haas at: 2008-11-16 16:01:24

Unarj: it seems, that unarj was removed from the Debian/Ubuntu archives

From: Anonymous at: 2008-11-25 18:57:52

Any news on how to install unarj now that it is no longer in the debian or ubuntu repos? I am trying to configure a spam snake following the tutorial but unarj is no longer available. Plese help.


From: Anonymous at: 2008-11-27 19:00:40

I've googled for 'linux arj options' and found that the options are the same as for 'arj'.

So I think that 'arj' already does the job of 'unarj'. If not, you can test if (as root or sudo) 'ln -s /usr/bin/arj /usr/bin/unarj' works...


From: Anonymous at: 2009-01-05 12:16:05

I downloaded the file from another mirror, saved it in my web folder and ointed linux to it ]

worked great

but can not get web admin working


From: Philip Jones at: 2009-02-23 15:09:11


 I found that if you change unarg... for arg_3.10.22-2_i386.deb at the end of the wget command it works fine, to check which files are available open the link (without the wget or filename) in your browser.

Great How-To


From: Gagandeep at: 2009-03-21 20:20:56

I downloaded it from here

From: Hurup at: 2008-11-11 18:04:26


After changed the Bind folder directories i get following error:

Nov 11 18:56:32 localservername named[20763]: starting BIND 9.4.2-P2 -u bind -t /var/lib/named
Nov 11 18:56:32 localservername named[20763]: found 1 CPU, using 1 worker thread
Nov 11 18:56:32 localservername named[20763]: loading configuration from '/etc/bind/named.conf'
Nov 11 18:56:32 localservername named[20763]: none:0: open: /etc/bind/named.conf: permission denied
Nov 11 18:56:32 localservername named[20763]: loading configuration: permission denied
Nov 11 18:56:32 localservername named[20763]: exiting (due to fatal error)
Nov 11 18:56:32 localservername kernel: [178236.619792] audit(1226426192.264:7): type=1503 operation="inode_permission" requested_mask="r::" denied_mask="r::" name="/var/lib/named/etc/bind/named.conf" pid=20764 profile="/usr/sbin/named" namespace="default"

The permissions are :

 # ls -la /etc/bind/
total 52
drwxr-sr-x 2 bind bind 4096 2008-11-11 18:38 .
drwxr-xr-x 3 root root 4096 2008-11-11 18:41 ..
-rw-r--r-- 1 bind bind  237 2008-10-10 18:53 db.0
-rw-r--r-- 1 bind bind  271 2008-10-10 18:53 db.127
-rw-r--r-- 1 bind bind  237 2008-10-10 18:53 db.255
-rw-r--r-- 1 bind bind  353 2008-10-10 18:53 db.empty
-rw-r--r-- 1 bind bind  270 2008-10-10 18:53 db.local
-rw-r--r-- 1 bind bind 2878 2008-10-10 18:53 db.root
-rw-r--r-- 1 bind bind  907 2008-10-10 18:53 named.conf
-rw-r--r-- 1 bind bind  165 2008-10-10 18:53 named.conf.local
-rw-r--r-- 1 bind bind  695 2008-10-10 18:53 named.conf.options
-rw-r----- 1 bind bind   77 2008-11-11 18:38 rndc.key
-rw-r--r-- 1 bind bind 1317 2008-10-10 18:53 zones.rfc1918


From: at: 2008-12-02 13:25:01


Please make sure apparmor is disabled.


From: Steve at: 2008-12-30 22:57:21

As an FYI, it would appear that installing bind9 re-enables apparmour

From: at: 2009-06-03 14:44:16



You have to redo the Remove Apparmour steps from Page 2 again following the bind9 install

From: José Manuel Avalos García at: 2009-12-02 07:17:31

 one to keep active Apparmour, add to /etc/apparmor.d/usr.sbin.named the next lines

  #CHROOT /var/lib/named/
  /var/lib/named/dev/random r,
  /var/lib/named/etc/bind/** r,
  /var/lib/named/var/cache/bind/** rw,
  /var/lib/named/var/cache/bind/ rw,
  /var/lib/named/var/run/bind/run/ w,
  /var/lib/named/var/run/bind/named.options r,
( before the last "}" ) 
and run
 /etc/init.d/apparmor restart
 /etc/init.d/bind9 start

From: Jamie Strandboge at: 2009-12-28 16:09:04

There is no reason to chroot bind9 if using AppArmor. Chrooting bind is the traditional way to limit file access for bind9, and it works fine, but does not confine bind9 as much as an AppArmor profile can. AppArmor also limits file access, networking and capabilities for bind9, and the Ubuntu developers have created a default bind9 installation that does not require any additional configuration for securing bind9. This way all users of bind9 can benefit from it.

Additionally, this tutorial recommends to disable all of AppArmor. Unless you have a very specific need to do so, this is not recommended. If you opt to chroot bind9 instead of use AppArmor, then please disable the profile, and leave the other profiles that are not causing problems to do their jobs. See my blog entry at for details.

From: Jake at: 2009-10-22 09:50:19

I would like to know the issues if any to leave bind9 alone and let apparmor deal with the security? I see that the author would like you to chroot bind9 but I wonder if it is out of habit or necessity.

From: Mastech Miami at: 2011-09-02 14:09:49

It is a problem installing "pecl install imagick". it returns

Cannot find config.m4.
Make sure that you run '/Applications/MAMP/bin/php5/bin/phpize' in the top level source directory of the module

to work around:

cd /usr


 php go-pear.phar

 pecl install imagick



From: at: 2008-05-17 02:23:27

For those in the 64-bit world:

dpkg -i dcc-common_1.3.42-5_amd64.deb
dpkg -i dcc-server_1.3.42-5_amd64.deb

From: Patrick at: 2009-03-26 10:30:37

Use mailscanner_4.74.16-1_all.deb in stead.

From: Klaus Hochlehnert at: 2008-12-28 22:23:36

Hi, couldn't get mailscanner from the mentioned location.
But I found it in the Intrepid archive:


Regards, Klaus

From: PieterJ at: 2008-09-25 14:04:57

You have to change this line in db_clean also:





From: Eric at: 2008-12-18 02:06:23

9.24.2 Filename and Filetype Release:
allow   .*      -       -

allow   .*      -       -

Remember to separate fields with tab characters

From: Mircsicz at: 2008-10-27 14:02:12

9.11 Fix to allow MailWatch to work with Postfix Inbound/Outbound Queue

the URL changed to:

From: Eric at: 2008-12-18 02:08:14

9.24.2 Filename and Filetype Release: /etc/MailScanner


allow .* - -


allow .* - -

Remember to separate fields with tab characters

From: Anonymous at: 2009-06-11 12:59:08 is to be found within


hope that I'm not the only one that dident find this file :P

From: Steve Baker at: 2009-10-14 07:24:59


Newer versions of Ubuntu enable an apparmor profile on /usr/sbin/clamd, this prohibits clam from seeing the Mailscanner spool folders and thus it cannot scan for viruses.  This issue is shown in the clamav logs as a 'permission denied' or 'access denied' error or similar, even if the permissions/groups on those folders is set correctly.

You need to edit the file /etc/apparmor.d/usr.sbin.clamd and add the following line:

 /var/spool/MailScanner/incoming/** r,


From: Walmiro Muzzi at: 2009-08-27 12:04:51

I'm having this warning when run spamassassin -D -p /etc/MailScanner/spam.assassin.prefs.conf --lint:

[6154] warn: lint: 2 issues detected, please rerun with debug enabled for more information

Please, how I fix it?


Thanks in advance.

From: Patrick at: 2009-03-29 00:20:31

Could you be a bit more specific on which howto this would be?

From: linch_y at: 2009-02-13 21:13:42

If someone finds it useful:

Instead of importing the users you may use live lookups against the AD. There is a howto in the howtoforge forums.

Good luck. 

From: hattmardy at: 2009-01-31 20:15:30

nevermind, i just realized i had simply forgot to uncomment out

open VALID, ">$VALID" or die "CANNOT OPEN $VALID $!";

and this was printing to this file ... doh!

From: hattmardy at: 2009-01-31 20:10:21

I had to modify the print VALID $mail line from:

print VALID $mail." OK\n";

 print "VALID = ", $mail ," OK!\n";

for some reason, the perl print string wasn't formed properly by default.

I was getting lots of  error messages:

print() on unopened filehandle VALID at /usr/bin/ line 86, <DATA> line 656

From: Anonymous at: 2009-08-21 16:44:13

I get this error email from cronjob : need help to resolve this issue.

 /opt/MailScanner/bin/MailScanner .... 

Starting MailScanner...Can't locate Filesys/ in @INC (@INC contains: /opt/MailScanner/lib /etc/perl /usr/local/share/perl/5.8.8 /usr/lib/perl5 /usr/share/perl5 /usr/share/perl/5.8 /usr/local/lib/site_perl /opt/MailScanner/lib /usr/local/lib/perl/5.8.8 /usr/lib/perl/5.8) at /opt/MailScanner/bin/MailScanner line 91.
BEGIN failed--compilation aborted at /opt/MailScanner/bin/MailScanner line 91.

 Please help ...

From: Patrick at: 2009-03-30 08:36:35

So, 13.1 should be apt-get install curl rsync

From: Patrick at: 2009-03-30 08:18:31
From: Richard at: 2009-05-26 20:17:34

Fix not necessary with Jaunty.

From: Tom at: 2009-07-01 18:24:16

The cron job set up to clean up the greylist table every night is too squeaky clean for me. It has the effect of wiping out the entire table every night. the greylist table uses column 'n' as a counter of how many times the entry has been hit. On the initial attempt this value is set to 1, when the remote MTA resends a valid email this value is set to 2 and incremented from there. This means that the minimum 'n' value for a valid entry is 2.

 I prefer to have my valid entries kept indefinitely, that way there isn't an ongoing delay for communication between valid business contacts. I set my crontab entry like so:

 55 23 * * * /usr/bin/mysql -ugld_user -pgld_pass -e 'USE gld_db; DELETE FROM greylist WHERE n < 2;' &> /dev/null

From: at: 2009-11-13 16:41:44

I will be sure to update the guide reflecting your correction. T