The Perfect SpamSnake - Ubuntu Jeos 9.10 - Page 5

12. KAM

Create /etc/cron.daily/ with the following content:

  # Original version modified by Andrew MacLachlan (
  # 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.
  if [ -f /opt/MailScanner/var/MailScanner ] ; then
  . /opt/MailScanner/var/MailScanner
  if [ "x$UPDATEMAXDELAY" = "x0" ]; then
  logger -p -t Delaying cron job up to $UPDATEMAXDELAY seconds
  perl -e "sleep int(rand($UPDATEMAXDELAY));"
  # JKF Fetch
  #echo Fetching
  cd /etc/mail/spamassassin
  rm -f
  wget -O > /dev/null 2>&1
  if [ "$?" = "0" ]; then
  #echo It completed and fetched something
  if ( tail -10 | grep -q '^#.*EOF' ); then
  # echo It succeeded so make a backup
  cp -f
  echo ERROR: Could not find EOF marker
  cp -f
  echo It failed to complete properly
  cp -f
  #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."
  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

Make it executable

chmod +x /etc/cron.daily/


13. ScamNailer

Create /opt/MailScanner/bin/update_scamnailer:

(You can download the script from

Make it executable:

chmod +x /opt/MailScanner/bin/update_scamnailer


14. Cron jobs:

# m h  dom mon dow   command
30 0 * * * /opt/MailScanner/bin/update_phishing_sites #Update Phishing Sites
35 0 * * * /opt/MailScanner/bin/update_bad_phishing_sites #Update bad phishing sites
40 0 * * * /opt/MailScanner/bin/update_virus_scanners #Update virus scanners
@hourly /opt/MailScanner/bin/check_mailscanner #Make sure MailScanner is running
* 1 * * * /opt/MailScanner/bin/update_spamassassin #Update spamassassin
* 0 * * * /opt/MailScanner/bin/db_clean.php --clean #Clean Mailwatch DB
10 0 * * * /opt/MailScanner/bin/quarantine_maint.php --clean #Clean Quarantine
* * * * 6 /usr/sbin/update_sqlgrey_config #Update Sqlgrey Config
50 0 * * * /opt/MailScanner/bin/update_scamnailer #Update Scamnailer
* 23 * * * /usr/bin/ #Update Sanesecurity


15. Firewalling the SpamSnake with Firehol


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

Edit /etc/default/firehol and change the following:


Edit /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

Be sure to comment out the default configuration before applying these settings. 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


Updating RESERVED_IPS list

cd /usr/src && wget
tar xzvf firehol.tar.gz && cd firehol
mv /usr/bin/
chmod +x /usr/bin/

Run the script to update RESERVED_IPS:

Make sure to select 'yes' when asked if you would like to save RESERVED_IPS to /etc/firehol/RESERVED_IPS.

Start Firehol:

/etc/init.d/firehol start

Now your server is set up to only accept connections for the services you allowed.


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

Create the Get Email Address 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.

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.


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/


17. Install Webmin (Optional):

apt-get install perl libnet-ssleay-perl libauthen-pam-perl libio-pty-perl libmd5-perl
cd /usr/src && wget dpkg --install webmin_1.500_all.deb


18. 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/ /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

Now we open /etc/postfix/disclaimer and modify it as follows (I have marked the parts that I've changed):

vi /etc/postfix/disclaimer

# Localize these.
####### Changed From Original Script #######
####### Changed From Original Script END #######
# Exit codes from <sysexits.h>
# 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
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" || \
                    { echo Message content rejected; exit $EX_UNAVAILABLE; }
####### 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/ and add -o content_filter=dfilt: to the smtp line:

vi /etc/postfix/

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



You should now have a complete working SpamSnake.

Share this page:

54 Comment(s)

Add comment


From: 011 at: 2010-04-23 01:51:00

Does this apply to Debian too or there are specific steps for it?

From: at: 2010-04-26 17:38:36

Debian should also work, but I don't know for sure.  I chose Ubuntu because it's a bit more updated than Debian with certain packages.

From: bluegrass at: 2010-08-04 07:04:07

Hi,  will this apply on an existing Virtual Users and Domains with Postfix, Courier, MySQL?  Or it should be installed on a separate hardware?

From: at: 2010-08-17 02:41:15

This is a standalone install, but can possibly be converted to a virtual setup.  I personally haven't tried it though.

From: iser0073 at: 2010-07-26 12:40:04

Thanx for the coolest anti spam solutions I have found!! It is really appreciated.

In the line:

postconf -e "mynetworks =,”

 the ” at the end of this line translates to a . when copying and pasting from this guide, would be cool if you could simply replace it with a "

 Kind Regards,

From: devnull1369 at: 2010-08-20 20:54:06

DCC has moved.


From: fredux at: 2010-04-24 14:49:34

Help me!!!

 Bayes error:

 [21180] dbg: bayes: using username: root
[21180] dbg: bayes: database connection established
[21180] dbg: bayes: found bayes db version 3
[21180] dbg: bayes: unable to initialize database for root user, aborting!




From: at: 2010-04-25 16:51:28

Has any spam email been processed yet? Maybe that is the reason.

From: fredux at: 2010-04-26 11:12:54

no, no message in queue. still in installation!!!


 Best regards,


From: Eddo Jansen at: 2010-07-02 06:54:36

First initialize the database by running the following command:

/usr/bin/sa-learn --force-expire --sync -p /opt/MailScanner/etc/spam.assassin.prefs.conf

From: 011 at: 2010-04-23 01:56:01

Mailwatch is at 1.0.5 right now. Is it OK to install that one?

Url for download of the patch is

Did not have time to check if this is patch for 1.05 as well.

From: at: 2010-04-29 02:38:25

I think the only difference is compatibility with php5, otherwise, everything is the same. It should work.  This is what I found on their support site:

## 1.0.5 - VERY BASIC alterations so that MailWatch also runs on PHP5.3 and MySql5.1

From: Anonymous at: 2010-06-18 12:31:33

I think I buggered up the GeoIP line , I changed the whole line to look like :


 What are the rest of the code behind it please :)

From: Joolee at: 2010-11-30 08:43:37

  dbquery("LOAD DATA LOCAL INFILE '".$base.'/'.$file2."' INTO TABLE geoip_country FIELDS TERMINATED BY ',' ENCLOSED BY '\"'");

 That's what it's supposed to look like after editing.

From: at: 2010-04-25 16:53:57

I believe the file to be edited for "Fix to allow wildcards in Whitelist/Blacklist" should be /opt/MailScanner/lib/MailScanner/CustomFunctions/

From: at: 2010-04-29 02:29:23

Correct, updated.

From: at: 2010-04-25 16:59:52

Directory /var/www/mailscanner/temp should also be apache writeable because MailWatch cannot extract the csv fle upon update.

Recommended fix:  replace procedure under title:

Install & Configure MailWatch - Make a temp directory:

mkdir temp
chmod g+w temp

chown root:www-data temp

From: at: 2010-04-29 02:27:45

Guide updated.

From: fredux at: 2010-04-26 20:52:52

Clamd Error Fix not work, I'm change user in clamd.conf to work using root and fix, do you have any other idea? sorry my english!

Best regards,


From: at: 2010-04-29 02:39:41
From: Anonymous at: 2010-07-23 13:49:16

This has solved for me.....

From: 011 at: 2010-05-10 16:44:15

I have a problem with "Fix to Allow Multiple Release of Messages in Message Operations" section.

Second step more specificaly.

Could you, please, point out what is the "following section" in "Then, find the following section and change it to look like this:" that needs ot be replaced with givcen code.



From: at: 2010-05-27 12:35:22

It's pretty clear, just find that section in the file, and change it to look like what's posted.

From: ihamouda at: 2010-07-03 18:19:11

This section doesn't exists in the file "do_message_ops.php" at least in version 1.0.5

Do you have any idea what file it was moved to?

From: at: 2010-07-13 17:55:42

Make a backup of your file and copy the content below and overwrite your original with it.  You should be good to go after that.

 MailWatch for MailScanner
 Copyright (C) 2003  Steve Freegard (

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

$refresh = html_start("Operation Results");

echo "<TABLE BORDER=0 WIDTH=\"100%\" CLASS=\"maildetail\">\n";
echo " <THEAD>\n";
echo "  <TH>Spam Learn Results</TH>\n";
echo " </THEAD>\n";
echo "  <TR>\n";
echo "  <TD CLASS=\"detail\">";

// Iterate through the POST variables
//Edited 1/5/09 Original = $id = $Regs[1];
if(is_array($_POST)) {
 foreach($_POST as $k=>$v) {
  if (preg_match('/^OPT-(.+)$/', $k, $Regs)) {
   $id = str_replace("_", ".",$Regs[1]);
  } else {
  switch ($v) {
   case 'S':
   case 'H':
   case 'F':
   case 'R':
  $items = quarantine_list_items($id,RPC_ONLY);
  echo "<TABLE WIDTH=100%>\n";
  if(count($items) > 0) {
   foreach($items as $num=>$item) {
    if ($item['file'] == 'message') {
$itemnum = array($num);
   if ($type == 'release'){
      if($quarantined = quarantine_list_items($id,RPC_ONLY)) {
         $to = $quarantined[0]['to'];
       echo "<tr><td><a href=\"detail.php?id=$id\">$id</a></td><td>$type</td><td>" . quarantine_release($quarantined, $itemnum, $to, RPC_ONLY) . "</td></tr>\n";
    } else {
     echo "<tr><td><a href=\"detail.php?id=$id\">$id</a></td><td>$type</td><td>" . quarantine_learn($items, $itemnum, $type, RPC_ONLY) . "</td></tr>\n";
  echo "</TABLE>\n";
echo "  </TD>\n";
echo " </TR>\n";
echo " </TABLE>\n";

echo "<p><a href=\"javascript:history.back(1)\">Back</a>";


From: Eddo Jansen at: 2010-07-07 06:05:15

Same here, that section does not exists, I have the following:

 case 'S':
   case 'H':
   case 'F':
  $items = quarantine_list_items($id,RPC_ONLY);
  echo "<TABLE WIDTH=100%>\n";
  if(count($items) > 0) {
   foreach($items as $num=>$item) {
    if ($item['file'] == 'message') {
     $itemnum = array($num);
      echo "<tr><td><a href=\"detail.php?id=$id\">$id</a></td><td>$type</td><td>" . quarantine_learn($items, $itemnum, $type, RPC_ONLY) . "</td></tr>\n";;
  } else {
   echo "<tr><td colspan=3>Message $id not found in quarantine</td></tr>\n";
  echo "</TABLE>\n";

Any advise??


From: PieterJ at: 2010-05-17 20:42:53

I added dcc_path /usr/bin/dccproc to the top of /opt/MailScanner/etc/spam.assassin.prefs.conf

The lint test gave this error:

[13033] info: config: dcc_path "/usr/local/bin/dccproc" isn't an executable
[13033] warn: config: SpamAssassin failed to parse line, "/usr/local/bin/dccproc" is not valid for "dcc_path", skipping: dcc_path /usr/local/bin/dccproc
[13033] info: config: dcc_path "/usr/local/bin/dccproc" isn't an executable
[13033] warn: config: SpamAssassin failed to parse line, "/usr/local/bin/dccproc" is not valid for "dcc_path", skipping: dcc_path /usr/local/bin/dccproc

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

I have comment these lines to solve it:

# ifplugin Mail::SpamAssassin::Plugin::DCC
#dcc_path /usr/local/bin/dccproc


From: at: 2010-05-27 12:29:52

Instead of adding the line to the top of the file and commenting out this section, just change it like:

ifplugin Mail::SpamAssassin::Plugin::DCC
dcc_path /usr/bin/dccproc

 That should work.

From: at: 2010-06-03 02:24:07

Double check the permissions, you missed a change somewhere.  It should be under the Mailwatch section.

From: Shoggy at: 2010-05-30 08:56:49

Thanks a lot for this How To. Everything works great except releasing quarantined message in Mailwatch. I have been battling this for over a week but no luck, i dont get the buttons and check box in the message details to release or learn spam. What am i missing, PLS HELP!!

From: iser0073 at: 2010-08-16 08:04:46

I have managed to install these after having some problems:

Install perl:

  aptitude install perl

for DBD::mysql you need mysql dev libraries so install:

  aptitude install libmysqlclient-dev libmysqlclient16-dev

now install DBD::mysql :

  cpan -i DBD::mysql

  cpan -i DBI

This worked for me.

From: Patrick at: 2012-06-22 10:18:47

For those running in to the problems I encountered while executing mysql -p < create.sql; below script runs properly.

 The issue I got were several errors like: ERROR 1064 (42000) at line 19

Alter the script to:


-- MySQL dump 8.23
-- Host: localhost    Database: mailscanner

-- Server version    3.23.58

-- Current Database: mailscanner

CREATE DATABASE /*!32312 IF NOT EXISTS*/ mailscanner;

USE mailscanner;

-- Table structure for table `audit_log`

CREATE TABLE audit_log (
  timestamp timestamp NOT NULL,
  user varchar(20) NOT NULL default '',
  ip_address varchar(15) NOT NULL default '',
  action text NOT NULL

-- Table structure for table `blacklist`

CREATE TABLE blacklist (
  id int(11) NOT NULL auto_increment,
  to_address text,
  to_domain text,
  from_address text,
  PRIMARY KEY  (id),
  UNIQUE KEY blacklist_uniq (to_address(100),from_address(100))

-- Table structure for table `geoip_country`

CREATE TABLE geoip_country (
  begin_ip varchar(15) default NULL,
  end_ip varchar(15) default NULL,
  begin_num bigint(20) default NULL,
  end_num bigint(20) default NULL,
  iso_country_code char(2) default NULL,
  country text,
  KEY geoip_country_begin (begin_num),
  KEY geoip_country_end (end_num)

-- Table structure for table `inq`

  id text,
  cdate date default NULL,
  ctime time default NULL,
  from_address text,
  to_address text,
  subject text,
  message text,
  size text,
  priority text,
  attempts text,
  lastattempt text,
  hostname text,
  KEY inq_hostname (hostname(50))

-- Table structure for table `maillog`

CREATE TABLE maillog (
  timestamp timestamp NOT NULL,
  id text,
  size bigint(20) default '0',
  from_address text,
  from_domain text,
  to_address text,
  to_domain text,
  subject text,
  clientip text,
  archive text,
  isspam tinyint(1) default '0',
  ishighspam tinyint(1) default '0',
  issaspam tinyint(1) default '0',
  isrblspam tinyint(1) default '0',
  isfp tinyint(1) default '0',
  isfn tinyint(1) default '0',
  spamwhitelisted tinyint(1) default '0',
  spamblacklisted tinyint(1) default '0',
  sascore decimal(7,2) default '0.00',
  spamreport text,
  virusinfected tinyint(1) default '0',
  nameinfected tinyint(1) default '0',
  otherinfected tinyint(1) default '0',
  report text,
  ismcp tinyint(1) default '0',
  ishighmcp tinyint(1) default '0',
  issamcp tinyint(1) default '0',
  mcpwhitelisted tinyint(1) default '0',
  mcpblacklisted tinyint(1) default '0',
  mcpsascore decimal(7,2) default '0.00',
  mcpreport text,
  hostname text,
  date date default NULL,
  time time default NULL,
  headers text,
  quarantined tinyint(1) default '0',
  KEY maillog_datetime_idx (date,time),
  KEY maillog_id_idx (id(20)),
  KEY maillog_clientip_idx (clientip(20)),
  KEY maillog_from_idx (from_address(200)),
  KEY maillog_to_idx (to_address(200)),
  KEY maillog_host (hostname(30)),
  KEY from_domain_idx (from_domain(50)),
  KEY to_domain_idx (to_domain(50)),
  KEY maillog_quarantined (quarantined)

-- Table structure for table `mcp_rules`

CREATE TABLE mcp_rules (
  rule char(100) NOT NULL default '',
  rule_desc char(200) NOT NULL default '',
  PRIMARY KEY  (rule)

-- Table structure for table `mtalog`

  timestamp datetime default NULL,
  host text,
  type text,
  msg_id varchar(20) default NULL,
  relay text,
  dsn text,
  status text,
  delay time default NULL,
  UNIQUE KEY mtalog_uniq (timestamp,host(10),type(10),msg_id,relay(20)),
  KEY mtalog_timestamp (timestamp),
  KEY mtalog_type (type(10))

-- Table structure for table `outq`

  id text,
  cdate date default NULL,
  ctime time default NULL,
  from_address text,
  to_address text,
  subject text,
  message text,
  size text,
  priority text,
  attempts text,
  lastattempt text,
  hostname text,
  KEY outq_hostname (hostname(50))

-- Table structure for table `sa_rules`

CREATE TABLE sa_rules (
  rule varchar(100) NOT NULL default '',
  rule_desc varchar(200) NOT NULL default '',
  PRIMARY KEY  (rule)

-- Table structure for table `saved_filters`

CREATE TABLE saved_filters (
  name text NOT NULL,
  col text NOT NULL,
  operator text NOT NULL,
  value text NOT NULL,
  username text NOT NULL,
  UNIQUE KEY unique_filters (name(20),col(20),operator(20),value(20),username(20))

-- Table structure for table `spamscores`

CREATE TABLE spamscores (
  user varchar(40) NOT NULL default '',
  lowspamscore decimal(10,0) NOT NULL default '0',
  highspamscore decimal(10,0) NOT NULL default '0',
  PRIMARY KEY  (user)

-- Table structure for table `user_filters`

CREATE TABLE user_filters (
  username varchar(60) NOT NULL default '',
  filter text,
  verify_key varchar(32) NOT NULL default '',
  active enum('N','Y') default 'N',
  KEY user_filters_username_idx (username)

-- Table structure for table `users`

  username varchar(60) NOT NULL default '',
  password varchar(32) default NULL,
  fullname varchar(50) NOT NULL default '',
  type enum('A','D','U','R','H') default NULL,
  quarantine_report tinyint(1) default '0',
  spamscore tinyint(4) default '0',
  highspamscore tinyint(4) default '0',
  noscan tinyint(1) default '0',
  quarantine_rcpt varchar(60) default NULL,
  PRIMARY KEY  (username)

-- Table structure for table `whitelist`

CREATE TABLE whitelist (
  id int(11) NOT NULL auto_increment,
  to_address text,
  to_domain text,
  from_address text,
  PRIMARY KEY  (id),
  UNIQUE KEY whitelist_uniq (to_address(100),from_address(100))



From: PieterJ at: 2010-05-19 07:04:27

When running you see this errors

/usr/bin/ line 798: rsync: command not found
/usr/bin/ line 803: rsync: command not found

Sanesecurity Database & GPG Signature File Updates

Sanesecurity mirror site used:
Connection to failed - Trying next mirror site.                                                                                                                               ..

Access to all Sanesecurity mirror sites failed - Check for connectivity issues
or signature database name(s) misspelled in the script's configuration file.

You can solve this by installing rsync (aptitude install rsync)


From: at: 2010-06-05 03:56:51

Both updated.  Thanks for the catch.

From: PieterJ at: 2010-05-19 18:43:44

After installing SQLGREY you see following errors in /var/log/mail.log:

May 19 16:55:56 smtp sqlgrey: dbaccess: can't connect to DB: Access denied for user 'sqlgrey'@'localhost' (using password: YES)
May 19 16:55:56 smtp sqlgrey: dbaccess: error: couldn't get now() from DB: Access denied for user 'sqlgrey'@'localhost' (using password: YES)
May 19 16:55:56 smtp sqlgrey: fatal: Can't call method "do" on an undefined value at /usr/sbin/sqlgrey line 308.

The SQLGREY db does not exist.

use mysql -p

CREATE USER 'sqlgrey'@'localhost' IDENTIFIED BY 'sqlgreypassword';
GRANT ALL PRIVILEGES ON `sqlgrey` . * TO 'sqlgrey'@'localhost';

edit /etc/sqlgrey/sqlgrey.conf to change db_pass accordenly.


From: Ruben Schepers at: 2010-06-02 16:00:13

In ubuntu mysq.sock is located at




(Regarding FuzzyOCR configuration)

From: at: 2010-06-05 03:55:02

That is what the guide has..

From: Francisco Maia at: 2010-06-24 10:01:02


 I keep getting these errors when testing FuzzyOCR:

Jun 24 10:50:11.147 [14957] dbg: FuzzyOcr: Exec : /usr/bin/giftext /tmp/.spamassassin149534oFg6Btmp/CIMG0980.gif
Jun 24 10:50:11.147 [14957] dbg: FuzzyOcr: Stdout: >/tmp/.spamassassin149534oFg6Btmp/
Jun 24 10:50:11.147 [14957] dbg: FuzzyOcr: Stderr: >>/tmp/.spamassassin149534oFg6Btmp/giftext.err
save_execute: Insecure dependency in open while running with -T switch at ../FuzzyOcr/ line 92.
save_execute: Insecure dependency in open while running with -T switch at ../FuzzyOcr/ line 92.
Jun 24 10:50:11.155 [14953] dbg: FuzzyOcr: Elapsed [14957]: 0.025447 sec. (/usr/bin/giftext: exit 8)
Jun 24 10:50:11.156 [14953] warn: readline() on closed filehandle INFILE at ../FuzzyOcr/ line 205.
Jun 24 10:50:11.156 [14953] info: FuzzyOcr: Image is single non-interlaced...
Jun 24 10:50:11.156 [14953] warn: rules: failed to run FUZZY_OCR test, skipping:
Jun 24 10:50:11.156 [14953] warn:  (Insecure dependency in printf while running with -T switch at /etc/spamassassin/ line 471.
Jun 24 10:50:11.156 [14953] warn: )

I am using the newest version of Ubuntu to install this, so I think this must be related with some incompatibility with a newer Perl version or something like this.

 I tried to install the latest version of FuzzyOCR (3.6.0) and the problem persists.

 Do you have any ideia how to overcome this?



From: Brandon at: 2010-07-03 14:53:17

facing the same issues.  anyone found a solution to this?

From: Joolee at: 2010-11-30 15:47:23

Install version 3.6 of FuzzyOcr ( and do not alter/uncomment/add the following line

focr_path_bin /usr/local/netpbm/bin:/usr/local/bin:/usr/bin

Works for me this way.

From: Anonymous at: 2011-09-20 21:03:08

Google fuzzyocr untaint

From: Eddo Jansen at: 2010-07-07 06:10:07

Got the same problem here, any help would be appreciated!


From: Error at: 2010-07-07 10:54:47

Yep, got them also on the latest release 10.04 Ubuntu server

save_execute: Insecure dependency in open while running with -T switch at /usr/share/perl5/FuzzyOcr/ line 92.
save_execute: Insecure dependency in open while running with -T switch at /usr/share/perl5/FuzzyOcr/ line 92.

Please help!

From: iser0073 at: 2010-08-16 10:55:14

I'm experiencing the same problems on new Ubuntu 10.04.1 LTS:

save_execute: Insecure dependency in open while running with -T switch at /etc/spamassassin/FuzzyOcr/ line 76.
save_execute: Insecure dependency in open while running with -T switch at /etc/spamassassin/FuzzyOcr/ line 76.
Aug 16 12:36:21.481 [4259] dbg: FuzzyOcr: Elapsed [4263]: 0.035510 sec. (/usr/bin/giftext: exit 8)
Aug 16 12:36:21.482 [4259] warn: readline() on closed filehandle INFILE at /etc/spamassassin/FuzzyOcr/ line 189.
Aug 16 12:36:21.482 [4259] info: FuzzyOcr: Image is single non-interlaced...
Aug 16 12:36:21.482 [4259] warn: rules: failed to run FUZZY_OCR test, skipping:
Aug 16 12:36:21.483 [4259] warn:  (Insecure dependency in printf while running with -T switch at /usr/share/perl5/ line 469.
Aug 16 12:36:21.483 [4259] warn: )

From: Nathan at: 2010-09-29 17:00:28

I too had this problem and was able to resolve it by performing a reinstall of fuzzyocr and netpbm:

sudo apt-get install fuzzyocr3

Although fuzzyocr was already installed, when I went to install this package it acted like both fuzzyocr and netpbm were not installed.

From: sergrok at: 2010-08-26 03:47:55

mistake in line


 it shoud be


From: sergrok at: 2010-08-26 03:42:24

in sqlgrey.conf you used a line
optmethod = out
but allowed values for optmethod is 'none', 'optin', 'optout'.  in your case value 'out' is equal to 'none'

From: sergrok at: 2010-08-26 03:58:53


 database name should be 'sqlgrey', not 'sglgrey'

From: Alvaro Soto at: 2010-11-13 05:16:05

Hello... I had the same problem... here is the solution...

Rocky.... great guide..!!!

From: at: 2010-12-04 00:20:12

Correct, comment out the focr_path line and everything should be fine:



From: Patrick at: 2011-08-17 19:07:53

In Step 11, shouldnt touch be:

 touch /etc/sqlgrey/clients_fqdn_whitelist.local && touch /etc/sqlgrey/clients_ip_whitelist.local

From: 011 at: 2010-04-23 02:13:00

I am not sure why only 2000 and 2003 exchange would be of interest here. 2000 version will take any email and then fiind out i fthere is a valid address so script might be of use. 2003 and later exchange will declined delivery of receipient is not valid.

Does this script retreive aliases too?

I am sure that postfix has capability to check if receipient is valid by asking exchenge 2003 and 2007 is email address is valid. I have a note that it is by adding, in, some check_receipient_address and adress_verify_map configurations

Server like 2000 exchange are rear. Why bother with script?

From: Peter at: 2010-04-26 13:13:44

The point of the Relay Recipients is to drop the connection at the SpamSnake if it is sent to an invalid recipient.  Why bother tying up resources on your mail server checking to see if the recipient is valid?  This is actually a great feature.  I would estimate 30-40% of the messages we receive are sent to invalid recipients.  Using a previous version of this SpamSnake has reduced the load on our mail server dramatically.

From: iser0073 at: 2010-08-16 21:00:23

 the script is giving me the following problems:

aggregate: no prefixes supplied


Failed to find reserved IPs.
Possibly the file format has been changed, or I cannot fetch the URL.

To fix this problem I had to edit /usr/bin/ file and make the following changes:

  1.  Replace the line :
    with this one :
  2. Replace the line :
    cut -d ' ' -f 1 |\
    with this one :
    awk '{print $1}' |\

now run the script again :

This fixed the problem for me.

I have also uploaded my working script, maybe someone can use my version and not have to go through all the trouble, you can grab it from here:

Remember to make the file executable after downloading :
chmod +x /usr/bin/

Hope this helps a little.

Thanx for a Brilliant tutorial!!