My c-plugins for qmail-spp: rblchecks and greylist
Tuesday January 30, 2007In order to fight spam, methods such as using a dns-based realtime blacklist or greylisting have been proven effective.
I use qmail with the very useful qmail-spp patch-set, that adds plugin capabilities to qmail-smtpd. Instead of just adding more and more chained commands to the run; file of tcpserver, you just specify a small plugin that is executed by qmail-smtpd when a certain smtp command is issued. In fact, this method is way nicer than forking many programs in a row… My run-line for the smtpd grew constantly, due to addition of rblsmtpd, qgreylist, … and I started to really dislike the increasingly huge memory footprint of a single smtp session. So I started searching for a rbl and greylisting plugin for qmail-spp – preferably in c, because forking several perl-instances per smtp session is just overkill. What I found was:
- greylisting-spp by Peter Conrad. His website indicated, that he claims greylisting-spp to be alpha-quality, so I kept away from using it (After writing my own greylisting solution and putting him into cc, he said that it is actually quite stable – but the website did not indicate it *argh*).
- ra-plugins by Roberto Alsina. Great collection of plugins – including a rblchecks one. But unfortunately his implementation lacks whitelisting support.
Because of this I did some own implementation – rather small and in c. To be honest, both greylisting and rblchecks are based on existing ideas / implementations. I suggest the following usage in the [mail] section of qmail-spp:
[mail]
skip-if-relayclient
skip-if-smtpauthuser
rblchecks
greylist
Update: People keep asking me per mail, what kind of blacklists and so on I use: I use the dul.dnsbl.sorbs.net and ix.dnsbl.manitu.net as blacklist and whitelist common mailservers such as web.de, gmx.net etc.
rblchecks.c – Check TCPREMOTEIP against a list of white- / blacklists.
Based on rblchecks.c of ra-plugins written by Roberto Alsina. Features & behavior of this plugin as follows (are README, and Makefile will be added at a later point).
- If
SKIP_RBLis set, plugin exits without doing anything (so following plugins in the chain will be executed) - The
bstringlibrary is used. So please have its header files present. - If
TCPREMOTEIPis listed in one of the servers specified in theRWLSERVERSenvironment variable, plugin exits withAanswer code. Please usetcp.smtpto specify theRWLSERVERS. Multiple servers can be supplied using : as separator. - If
TCPREMOTEIPis listed inRBLSERVERS, plugin will exit with error541and qmail-smtpd will quit the smtp session. - Be sure to set a DNS timeout via
resolv.confIn a future version, there will be an timeout handling within this plugin. - Plugin will give diagnostic messages via
STDERR, so that qmail-spp will pass them to the logging system of qmail. - In case a IPv6 connection is found (
$PROTO=TCP6) the plugin exits with a note – because IPv6 is not (yet?) subject to realtime blacklists.
greylist.c – Perform greylisting on TCPREMOTEIP.
This plugin was inspired by qgreylist by Jon Atkins. This implementation does a IP based greylisting – Sender and Receiver addresses are not taken into account! Right now there is just the c-file. Documentation as in README will follow.
- If
SKIP_GREYis set, plugin exits without doing anything (so following plugins in the chain will be executed) - Greylist creates an empty file named by
TCPREMOTEHOSTin the specifiedBASEDIR(default is/var/qmail/greylist) when a host is first seen. - A host, that is seen the first time gets a temporary 451 error.
- After a minimum waiting time set via
GL_MIN_REJECT(default is 300s) a SMTP session is accepted. - If host comes back before
GL_MIN_REJECT, it gets an 451 again. - Once a session was successful, the entry has a lifetime of
GL_ACCEPT_GOOD(default is 32 days). - If a host does not come back after
GL_MAX_WAIT(default is one day), the entry is subject to cleanup. - On every subsequent SMTP session of
TCPREMOTEHOSTthe file-access-time of the corresponding file is updated to the current time. - The modification time of the file corresponds to the time, when the host was first seen, while the access time refers to the time when it has been seen for the last time.
- Be sure to have enough inodes in the specified
BASEDIR! For high-volume servers you might consider using a ramdisk. But even for a server with about 15000 mails per hour, there was no slowdown. Best results have been seen on ext3 with htree, xfs and reiserfs. (I personally use a ramdisk with reiserfs on it. Do NOT use ramfs/tmpfs! BASEDIRmust be read-/writetable for theqmail-smtpuser (usually qmaild)- Concept is also valid (and working!) for IPv6.
- A clean-up cronjob for the
BASEDIRis suggested. Just cleanup files, that have not been seen again withinGL_ACCEPT_GOOD(now > atime + accept good) or that exceedGL_MAX_WAIT(now > atime +maxwait and atime == utime). A simple perl-oneliner does that:
perl -e "my $time = time(); for my $file ( </var/qmail/greylist/*> ) { my ( $atime, $mtime ) = (stat $file)[8,9]; if ( ( ( $atime == $mtime ) and ( $atime < $time - 300 ) ) or ( $atime < $time - 2764800 ) ) { unlink $file or print "unlink $file failed"; } }"
skip-if-relayclient.c and skip-if-smtpauthuser.c – Simple plugins that stop further qmail-spp processing if RELAYCLIENT or SMTPAUTHUSER
The use of creating a kind of if-then-else handling in qmail-spp made me write these trivial little programs. So get them in front of your tool-chain in order to stop further plugins from being executed when you have a trusted sender!
- If
SMTPAUTHUSERorRELAYCLIENTare set, plugin exits using theAanswer code (no further plugins in the chain will be executed)
Sorbs.net can be easily defeated. All the sender has to do is change their SMTP port setting from 25 (the standard default) to 587.
This is a public service announcement from a non-spammer who is on Sorb’s black list ONLY because my ISP is AT&T/SBCGlobal.net.
Comment by Big Gay Al — Wednesday, January 31 2007 @ 22:01
bugs in rblchecks.c :
* a closing ‘)’ is missing
* strcmp returns 0 for equal strings, so we have to negate the PROTO = TCP6 check
— rblchecks.c.orig 2007-10-18 23:54:02.625000000 +0800
+++ rblchecks.c 2007-10-18 23:58:14.234375000 +0800
@@ -93,7 +93,7 @@
}
// be IPv6 safe and just exit if PROTO = TCP6
- if (strcmp(getenv (“PROTO”),”TCP6″)
+ if (!strcmp(getenv (“PROTO”),”TCP6″))
{
fprintf (stderr, “rblchecks: pid %d – IPv6 not subject to RBL! Accepted %s\n”, ppid, ip->data);
exit (0);
Comment by ax — Friday, October 19 2007 @ 00:10
you are completely right – the distributed version was an old cvs one
thanks anyway.
Comment by blackwing — Saturday, October 27 2007 @ 03:10