|
( 14.03.2006 ) |
|
( 29.04.2004 ) |
|
( 16.04.2004 ) |
|
( 29.07.2003 ) |

|
 |
Top level Technology
I have to rewrite my spam-filtering systems every six months. This is my latest attempt, using greylisting with the exim MTA in addition to HELO checking and tests on known spammers.
|
Other useful resources are :-
I started with solution, but it used the sender domain, and had no local address part in the lookup, and no good index.
Packages to install - using whitebox linux / Redhat Enterprise.
-
exim - with exiscan and mysql support compiled in
- mysql-server
Setting up the database - first give it a root password, for basic security. Ensure that it starts up on boot, and, for security purposes, use unix sockets and not TCP. We do the last by adding --skip-networking to the safe_mysqld command in /etc/rc.d/init.d/mysqld.
/sbin/chkconfig mysqld on
/etc/rc.d/init.d/mysqld start
mysqladmin -u root password 'SecRet'
|
Next, create a database for exim to use, and grant permissions, and then create the table. This is indexed by sender email address, sender IP address, and username of the recipient and holds a timestamp. sender and recipient are in CHAR fields for speed, and are truncated by MySQL when being added if they are over 25 chars long.
The block_expires field is used as follows :-
- Absent ? Start Greylisting process, 25 mins in future
- In the future ? Greylisted, 25 minute block still active
- In the recent (6 hours) past? No retry of delivery
- 6-8 hours past ? Purge from db
- Over 8 hours past ? Set by UPDATE - accept.
- Over 35 days past ? Purge from db
If this timestamp is in the future, we wait until then before accepting mail (greylisting). If it is in the past, we accept the mail. Cleanup routines flush entries more than 7 days old.
mysqladmin -u root -p create exim
mysql -u root -p
mysql> grant all on exim.* to exim@localhost identified by 'exim%worD';
mysql> flush privileges;
mysql> quit;
mysql -u exim -p exim
mysql> CREATE TABLE greylist (
relay_ip char(15) NOT NULL,
sender char(100) NOT NULL,
recipient char(25) NOT NULL,
block_expires datetime NOT NULL,
PRIMARY KEY (relay_ip, sender, recipient)
);
mysql> quit;
|
We now need to add some ACLs to exim for it to use these.
At the top of /etc/exim.conf, we set up the acl_ routines, and add some MySQL macros for use later on.
Please note that dollar signs should not have a space between them and the following curly brackets. A 'feature' of my CMS, sorry
acl_smtp_rcpt = check_recipient
# are they in the database already ?
GREYLIST_TEST = SELECT CASE \
WHEN now() - block_expires > 0 THEN 2 \
ELSE 1 \
END \
FROM greylist \
WHERE relay_ip = '$ {quote_mysql:$sender_host_address}' \
AND sender = '$ {quote_mysql:$sender_address}' \
AND recipient = '$ {quote_mysql:$local_part}'
# add them in, with 25 minute block
GREYLIST_ADD = INSERT INTO greylist (relay_ip, sender, recipient, block_expires) \
VALUES ( '$ {quote_mysql:$sender_host_address}', \
'$ {quote_mysql:$sender_address}', \
'$ {quote_mysql:$local_part}', \
DATE_ADD(now(), INTERVAL 25 MINUTE) \
)
# keep the entry fresh.
GREYLIST_UPDATE = UPDATE greylist SET \
block_expires = DATE_SUB(now(), INTERVAL 8 HOUR) \
WHERE relay_ip = '$ {quote_mysql:$sender_host_address}' \
AND sender = '$ {quote_mysql:$sender_address}' \
AND recipient = '$ {quote_mysql:$local_part}'
hide mysql_servers = localhost/exim/exim/exim%worD
|
Down in the acl section, we put the following. I also check for a properly-formatted HELO message from the sending SMTP server. Please change your hostname and IP address to match your own. I also do a check against known spammer IPs. Denying on the basis of EHLO checks is controversial, you may wish to omit these. However, denying based on the other party using your own name or IP I consider reasonable.
check_recipient:
# deny addresses with funny letters and shell escapes
deny local_parts = ^.*[@%!/|] : ^\.
# accept locally generated emails
accept hosts = :
# accept anyone who can authenticate
accept authenticated = *
# accept null senders - bounced bounces or address verification
accept senders = : postmaster
domains = +local_domains : +relay_domains
# No HELO/EHLO
deny condition = $ {if eq{$sender_helo_name}{}{yes}{no}}
message = Polite hosts say HELO first\n\
Please see RFC 2821 section 4.1.1.1
log_message = Bad HELO: Empty HELO
# drop messages without at least one dot in the HELO hostname
drop message = HELO/EHLO must contain a Fully Qualified Domain Name
condition = $ {if match{$sender_helo_name} \
{\N^[^.].*\.[^.]+$\N} \
{no}{yes} \
}
# surprising number of spammers spoof my own address
drop message = HELO/EHLO invalid
condition = $ {if match{$sender_helo_name} \
{your.hostname.org.za} \
{yes}{no} \
}
# and IP
deny condition = $ {if eq{$sender_helo_name}{$interface_address}{yes}{no}}
message = Forged HELO: you are not $sender_helo_name
log_message = Forged HELO: is our interface address
# listed as a spammer
deny message = $sender_host_address is listed at \
$dnslist_domain ($dnslist_value: $dnslist_text)
dnslists = sbl-xbl.spamhaus.org
# set a variable marking whether they were in the database
warn set acl_m2 = $ {lookup mysql{GREYLIST_TEST}{$value}{0}}
# new entry in database - defer, and add them
defer message = Greylisted - You have been greylisted. This is part of our standard anti-spam \
measures and your mail system should automatically try again later.
condition = $ {if eq{$acl_m2}{0}{1}}
condition = $ {lookup mysql{GREYLIST_ADD}{yes}{no}}
# they are listed, but it is still too early
defer message = Greylisted - try again in half an hour
condition = $ {if eq{$acl_m2}{1}{1}}
# freshen entry
warn condition = $ {lookup mysql{GREYLIST_UPDATE}{yes}{no}}
# could we deliver a bounce message if needed ?
require verify = sender
# we relay for our friends only
accept domains = +relay_domains
deny message = relay not permitted
|
Finally, we need to put in a cron job to remove stale entries from the database. It will run as the exim user, on an hourly basis. We therefore drop the following file into the /etc/cron.hourly/ directory, as exim-db.
#! /bin/sh
# purge items from database on two conditions :-
# between 6 and 8 hours old (Greylisted address did not retry)
# 28 days old (stale)
/usr/bin/mysql --user=exim --password='exim%worD' exim << END_SQL
DELETE FROM greylist
WHERE now() > DATE_ADD(block_expires, INTERVAL 6 HOUR) AND
now() < DATE_ADD(block_expires, INTERVAL 8 HOUR);
DELETE FROM greylist
WHERE now() > DATE_ADD(block_expires, INTERVAL 35 DAY);
END_SQL
|
|
| Printer-friendly page | |
|
|