The Perfect SpamSnake - Ubuntu Jeos 9.10 - Page 4

8. 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.007.tar.gz
cd postfix-policyd-spf-perl-2.007
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 = reject_unauth_destination,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.


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


Create MySQL Database


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

Setup the password:

mysql -u root -p
mysql> GRANT ALL ON FuzzyOcr.* TO fuzzyocr@localhost IDENTIFIED BY 'password';


MailWatch Fix

Do the following to prevent an error in MailWatch:

vi /etc/mail/spamassassin/


'use POSIX;'


'use POSIX qw(SIGTERM);'


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)."


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.


10. Filtering PDF, XLS and Phishing Spam with ClamAV (Sanesecurity Signatures)

There is currently a lot of spam where the spam "information" is attached as .pdf or .xls files, sometimes also hidden inside a .zip file. While these spam mails are not easy to catch with e.g. SpamAssassin or a Bayes filter, the ClamAV virus scanner can catch them easily when it is fed with the correct signatures as ClamAV is built to scan mail attachments.

Create a folder for sanesecurity and download and give the script the proper permission.

apt-get install curl rsync
mkdir /usr/src/sanesecurity && cd /usr/src/sanesecurity
tar -zxf clamav-unofficial-sigs.tar.gz && cd clamav-unofficial-sigs-3.7
mv /usr/bin
mv clamav-unofficial-sigs.conf /etc/
chmod +x /usr/bin/

Edit clamav-unofficial-sigs.conf and change the following variables to match your installation:


The variable clamav_dbs contains the path to the directory where your ClamAV signatures are stored.

Path to


Reload after update:

reload_opt="kill -USR2 `cat $clamd_pid`" #Signals PID to reload dbs

Work Directory:


And once you're done with the configuration, set the following to yes:


Now we run the update script to check if the download works:

Now we a add the script to the root crontab to be run once a day:

crontab -e

Add the following line at the end of the root crontab:

00 04 * * * /usr/bin/ –c /etc/clamav-unofficial-sigs.conf &> /dev/null


11. Greylisting with Sqlgrey

Install sqlgrey:

apt-get install sqlgrey
touch clients_fqdn_whitelist.local && touch clients_ip_whitelist.local

Edit sqlgrey.conf to look like:

## SQLgrey config file ##
# Notes:
# - Unless specified otherwise commented settings are SQLgrey's defaults
# - SQLgrey uses a specific config file when called with -f <conf_file>
## Configuration files
conf_dir = /etc/sqlgrey
## Log level
# Uncomment to change the log level (default is normal: 2)
# nothing: O, errors only: 0, warnings: 1, normal: 2, verbose: 3, debug: 4
# loglevel = 2
## log categories can be fine-tuned,
# here are the log messages sorted by types and levels,
# (anything over the loglevel is discarded):
# grey     : (0) internal errors,
#            (2) initial connections, early reconnections,
#                awl matches, successful reconnections, AWL additions,
#            (3) smart decision process debug,
# whitelist: (2) whitelisted connections,
#            (3) actual whitelist hit,
#            (4) whitelists reloads,
# optin:     (3) optin/optout global result
#            (4) optin/optout SQL query results
# spam     : (2) attempts never retried,
# mail     : (1) error sending mails,
#            (4) rate-limiter debug,
# dbaccess : (0) DB errors,
#            (1) DB upgrade,
#            (2) DB upgrade details,
# martians : (2) invalid e-mail addresses,
# perf     : (2) cleanup time,
# system   : (0) error forking,
#            (3) forked children PIDs, children exits,
# conf     : (0) errors in config files, missing required file,
#            (1) warnings in config files,
#                missing optional configuration files,
#            (2) reloading configuration files,
# other    : (4) Startup cleanup
# you can set a level to O (capital o) to disable logs completely,
# but be aware that then SQLgrey can come back to haunt you...
# Provide a coma-separated "logtype:loglevel" string
# For example if you set the loglevel to 3 (verbose) but want SQLgrey to be:
# . quiet for whitelists
# . normal for greylisting
# uncomment the following line.
# log_override = whitelist:1,grey:2
# By default, log_override is empty
## Log identification
# by default this is the process name. If you define the following variable
# SQLgrey will use whatever you set it to
#log_ident = sqlgrey
## username and groupname the daemon runs as
user = sqlgrey
group = sqlgrey
## Socket
# On which socket do SQLgrey wait for queries
# use the following if you need to bind on a public IP address
# inet =
# default :
# inet = 2501    # bind to localhost:2501
## PID
# where to store the process PID
# pidfile = /var/run/
## Config directory
# where to look for other configuration files (whitelists)
confdir = /etc/sqlgrey
## Greylisting delays
# If you want to be really strict (RFC-wise) use these
# This is *not* recommended, you'll have false positives
# reconnect_delay = 15    # don't allow a reconnection before 15 minutes
# max_connect_age = 2     # don't allow a reconnection after 2 hours
# default: (based on real-life experience)
# reconnect_delay = 5
# max_connect_age = 24
connect_src_throttle = 5
## Auto whitelists settings
# default is tailored for small sites
awl_age = 60
group_domain_level = 2
# For bigger sites you may want
# a smaller awl_age and a bigger group_domain_level
# awl_age = 32            # AWL must be renewed at least once a month
                          # 32 > 31 (max delay between monthly newsletters)
# group_domain_level = 10 # wait for 10 validated adresses to add a whole
                          # domain in AWL
## Database settings
# instead of Pg below use "mysql" for MySQL, "SQLite" for SQLite
# any DBD driver is allowed, but only the previous 3 have been tested
db_type = mysql
db_name = sqlgrey
# Note: the following are not used with SQLite
db_host = localhost
db_port = default
db_user = sqlgrey
db_pass = password
db_cleandelay = 1800 # in seconds, how much time between database cleanups
clean_method = sync # sync : cleanup is done in the main process,
                      #        delaying other operations
                      # async: cleanup is done in a forked process,
                      #        it won't delay mail processing
                      #        BEWARE: lockups have been reported
                      #        and are still investigated
## X-Greylist header added?
# This adds delay, whitelist and autowhitelist information in the headers
prepend = 1
## Greylisting method:
# - full   : greylist by IP address
# - classc : greylist by class C network. eg:
#   connection accepted if did connect earlier
# - smart  : greylist by class C network unless there is no reverse lookup
#            or it looks like a home-user address
# Default is smart
# greymethod = smart
## Optin/Optout (see README.OPTINOUT for details)
# - none   : everyone is greylisted (default)
# - optin  : one must optin to have its (incoming) messages being greylisted
# - optout : one must optout to not have its messages being greylisted
optmethod = out
## SQLgrey return value.
# SQLgrey can tell Postfix to:
# - immediately reject a message with a temporary reject code
# - only do so if following rules would allow the message to pass
# The first choice will prevent Postfix from spending time evaluating
# potentially expensive rules.
# In some cases you may want following rules to be aware of the connection
# this.
# We can specify a different rejection strategy for the first connection
# attempt, and for early reconnections. 'immed' chooses immediate rejection
# 'delay' choose delayed rejection
# By default we use delay on first attempt
reject_first_attempt = immed
# Default for early reconnection is the value affected to reject_first_attempt
reject_early_reconnect = immed
## Update server
# where to get updates for whitelists
whitelists_host =
## Postmaster address
# who gets urgent notifications (DB is down for example)
# default or empty: don't send mail notifications
# admin_mail =

Create the database:

mysql -u root -p
GRANT ALL ON sqlgrey.* TO sqlgrey@localhost IDENTIFIED BY 'password';

Edit /etc/sqlgrey/sqlgrey.conf and update the database settings sections accordingly

Add the following to smtpd_recipient_restrictions:

check_policy_service inet:, permit


vi /usr/sbin/sqlgrey

(You can download the file from

chmod +x /usr/sbin/sqlgrey

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!!