The Perfect SpamSnake - Ubuntu 8.04 LTS - Page 6

10 Install and Configure SPF

The postfix-policyd-spf-perl package depends on the Mail::SPF and the NetAddr::IP Perl modules.

We need to download postfix-policyd-spf-perl from to the /usr/src/ directory and install it to the /usr/lib/postfix/ directory like this:

cd /usr/src
tar xvfz postfix-policyd-spf-perl-2.005.tar.gz
cd postfix-policyd-spf-perl-2.005
cp postfix-policyd-spf-perl /usr/lib/postfix/policyd-spf-perl

Then we edit /etc/postfix/ and add the following stanza at the end:

vi /etc/postfix/

policy unix - n n - - spawn
   user=nobody argv=/usr/bin/perl /usr/lib/postfix/policyd-spf-perl

(The leading spaces before user=nobody are important so that Postfix knows that this line belongs to the previous one!)

Then open /etc/postfix/ and search for the smtpd_recipient_restrictions directive. You should have reject_unauth_destination in that directive, and right after reject_unauth_destination you add check_policy_service unix:private/policy like this:

vi /etc/postfix/

smtpd_recipient_restrictions = permit_sasl_authenticated,permit_mynetworks,reject_unauth_destination,check_policy_service unix:private/policy

or like this:

smtpd_recipient_restrictions =
   check_policy_service unix:private/policy

It is important that you specify check_policy_service AFTER reject_unauth_destination or else your system can become an open relay!

Then restart Postfix:

/etc/init.d/postfix restart

That's it already.


11 Install and Configure FuzzyOcr

apt-get install netpbm gifsicle libungif-bin gocr ocrad libstring-approx-perl libmldbm-sync-perl imagemagick tesseract-ocr

Download and install the latest FuzzyOCR devel version from

cd /usr/src/

Unpack FuzzyOCR and move all FuzzyOcr* files and the FuzzyOcr directory (they are all in the FuzzyOcr-3.5.1/ directory) to /etc/mail/spamassassin:

tar xvfz fuzzyocr-3.5.1-devel.tar.gz
cd FuzzyOcr-3.5.1/
mv FuzzyOcr* /etc/mail/spamassassin/
wget -O /etc/mail/spamassassin/FuzzyOcr.words

We will be storing the image hashes in a mysql database to improve on performance such that images that we have already scanned do not get scanned again as OCR is a resource intense activity.


11.1 Create MySQL Database

The sql script creates the database and tables and adds a user fuzzyocr with the password fuzzyocr:

mysql -p < /etc/mail/spamassassin/FuzzyOcr.mysql

Change the password:

mysqladmin -u fuzzyocr -p fuzzyocr newpassword


11.2 MailWatch Fix

Do the following to prevent an error in MailWatch:

vi /etc/mail/spamassassin/

Change 'use POSIX;' to 'use POSIX qw(SIGTERM);'


11.3 FuzzyOcr Configuration

FuzzyOCR's configuration file is /etc/mail/spamassassin/ In that file almost everything is commented out. We open that file now and make some modifications:

vi /etc/mail/spamassassin/

Put the following line into it to define the location of FuzzyOCR's spam words file:

focr_global_wordlist /etc/mail/spamassassin/FuzzyOcr.words

/etc/mail/spamassassin/FuzzyOcr.words is a predefined word list that comes with FuzzyOCR. You can adjust it to your needs.

Next change:

# Include additional scanner/preprocessor commands here:
focr_bin_helper pnmnorm, pnminvert, pamthreshold, ppmtopgm, pamtopnm
focr_bin_helper tesseract


# Include additional scanner/preprocessor commands here:
focr_bin_helper pnmnorm, pnminvert, convert, ppmtopgm, tesseract

Finally add/enable the following lines:

# Search path for locating helper applications
focr_path_bin /usr/local/netpbm/bin:/usr/local/bin:/usr/bin
focr_preprocessor_file /etc/mail/spamassassin/FuzzyOcr.preps
focr_scanset_file /etc/mail/spamassassin/FuzzyOcr.scansets
focr_digest_db /etc/mail/spamassassin/FuzzyOcr.hashdb
focr_db_hash /etc/mail/spamassassin/FuzzyOcr.db
focr_db_safe /etc/mail/spamassassin/
focr_minimal_scanset 1
focr_autosort_scanset 1
focr_enable_image_hashing 3
focr_logfile /var/log/FuzzyOcr.log
#Mysql Connection#
focr_mysql_db FuzzyOcr
focr_mysql_hash Hash
focr_mysql_safe Safe
focr_mysql_user fuzzyocr
focr_mysql_pass password
focr_mysql_host localhost
focr_mysql_port 3306
focr_mysql_socket /var/run/mysqld/mysqld.sock

This is what the FuzzyOCR developers say about image hashing:

"The Image hashing database feature allows the plugin to store a vector of image features to a database, so it knows this image when it arrives a second time (and therefore does not need to scan it again). The special thing about this function is that it also recognizes the image again if it was changed slightly (which is done by spammers). "


11.4 Test FuzzyOCR

cd /usr/src/FuzzyOcr-3.5.1/samples
spamassassin --debug FuzzyOcr < ocr-animated.eml > /dev/null

You see the following:

[14808] info: FuzzyOcr: Found Score <9.000> for Exact Image Hash
[14808] info: FuzzyOcr: Matched [1] time(s). Prev match: 16 sec. ago
[14808] info: FuzzyOcr: Message is SPAM. Words found:
[14808] info: FuzzyOcr: "price" in 1 lines
[14808] info: FuzzyOcr: "company" in 1 lines
[14808] info: FuzzyOcr: "alert" in 1 lines
[14808] info: FuzzyOcr: "news" in 1 lines
[14808] info: FuzzyOcr: (6 word occurrences found)
[14808] dbg: FuzzyOcr: Remove DIR: /tmp/.spamassassin14808JZSvHBtmp
[14808] dbg: FuzzyOcr: Processed in 0.104555 sec.


12 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.


12.1 Install Dependencies

Install the perl module Net::LDAP:

perl -MCPAN -e shell
install Net::LDAP


12.2 Create the Get Email Address Script

Create and edit the script:

vi /usr/bin/

Copy and paste the code below into this new file.

#!/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 " 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
# 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 []" &
# 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://,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"
# 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://,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\"
# Connecting to Active Directory domain controllers
$ldap = Net::LDAP->new($dc1) or
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)
                    (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
if ($cookie) {
  # We had an abnormal exit, so let the server know we do not want any more
  $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\ OK\n";
#print VALID "user\ 550 User unknown.\n";
#print VALID " 550 User does not exist.\n";
close VALID;

Next set the permissions on the file to allow it to be executed:

chmod 500 /usr/bin/

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.


12.3 Run the Script

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. At this point, you can update your /etc/postfix/ to relay_recipient_maps. You will also have to postmap the file to create the database.

At this point, you may want to edit /etc/postfix/relay_recipients and edit out any unwanted email addresses as this script imports everything.


12.4 Create the Table

postmap /etc/postfix/relay_recipients

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/ (Optional)

vi /usr/bin/

cd /etc/postfix
postmap relay_recipients

Don't forget to make sure the following is in your /etc/postfix/ file:

relay_recipient_maps = hash:/etc/postfix/relay_recipients

Make the script excutable:

chmod +x /usr/bin/

Run crontab to add this script to the scheduled jobs:

crontab -e

Now add the following lines to the bottom of the file. 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.

# syncronize relay_recipients with Active Directory addresses
30 2 * * * /usr/bin/
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