Skip to main content

Ralsina.Me — Roberto Alsina's website

Fighting Spam with Qmail (part II)

Introduction

In part I of this se­ries, I ex­plained how to fil­ter spam af­ter qmail takes it, but be­fore the us­er sees it. While that achieves the im­por­tant goal of avoid­ing us­er an­noy­ance, it still leaves one is­sue open: Spam is us­ing re­sources in the qmail serv­er. How?

SMTP connections:
Spammers are opening connections to your SMTP server, thus increasing the number of SMTP processes you run.
Local deliveries:
If spam reaches the ifspamh filter we installed before, it already has been delievered locally and if it is spam, it will have to be delivered again, into one of the spam folders.
Bounces:
Qmail doesn't verify that local recipients are valid before accepting a message. This is a serious problem. For example, if your server handles the mydomain.com domain, and qmail receives a messages for a user joe@mydomain.com who doesn't exist, it is accepted, queued, attempted to deliver locally (which finally gives an error), then a bounce is created, and the bounce is attempted to deliver remotely. If the original message was spam, that bounce will probably bounce as well, causing a second delivery (maybe local, maybe remote, depending on your qmail's doublebounceto and doublebouncehost configuration files)

This is par­tic­u­lar­ly grue­some if a spam­mer de­cides to har­vest ad­dress­es by post­ing to all let­ter com­bi­na­tion­s. In that case, qmail may have to han­dle thou­sands (or tens of thou­sand­s) of ex­tra de­liv­er­ies!

In this ar­ti­cle, I will ex­plain how to solve, at least part­ly, these prob­lem­s, us­ing a re­place­ment for qmail's SMTP server, called qpsmt­pd. All com­mands giv­en are as I used in a Red Hat 8.0 sys­tem. I doubt it, but some may need to be changed in oth­er dis­tros, so check the docs of each com­mand if you don't un­der­stand what a com­mand does. Re­mem­ber, you don't know me, I may be evil or dum­b!

I al­so as­sume that you al­ready have a work­ing, op­er­a­tional qmail set­up. If your qmail is not work­ing, fol­low­ing this tu­to­ri­al will on­ly break it fur­ther, so stop right now and fix it!

Note: some of this stuff on­ly works if you use a qmail set­up with reg­u­lar us­er ac­counts, a ful­ly vir­tu­al set­up like vmailm­gr. is NOT go­ing to work right in some places.

Implementation

Installing qpsmtpd

First, get qpsmt­pd. I am us­ing ver­sion 0.26, the lat­est right now.

Check out the README. This is weird, but to read it right, you have to use the com­mand perl­doc README . The rest of this sec­tion is pret­ty much cov­ered in the READ­ME, so you should just do what it says, un­less you don't un­der­stand it, or if I ex­plain why it does­n't work, then you can do what this page says ;-)

Perl Modules

Qpsmt­pd re­quires a cou­ple of perl mod­ules that you may not have in­stalled, Net::DNS and Mail::Ad­dress .

If your perl is old­er than 5.8.0, you al­so need Data::­Dumper and File::Temp (mine was 5.8.0, so I al­ready had them).

The README says that the eas­i­est way to get them is the CPAN shell. That is true, but it has a se­ri­ous prob­lem: if your perl is in­stalled by the dis­tri­bu­tion (not self­-­com­piled) and you want to use an up­date tool, like ap­t-get or up­2­date, then these mod­ules will get out of sync the next time your perl is up­dat­ed, and qpsmt­pd will stop work­ing! So, if you pos­si­bly can, get the mod­ules in a pack­aged for­m. Up­dat­ing your sys­tem is dif­fi­cult enough al­ready ;-).

Net::DNS al­ready comes with the dis­tri­bu­tion I used (RH 8.0), so I just in­stalled the RPM from freshrpm­s.net.

Mail::Ad­dress I got from Dag Wieers repos­i­to­ry. Be care­ful to see that the mod­ule does work (it worked flaw­less­ly for me, though). If lat­er on you see any fun­ny stuff, just re­move the rpm and go the CPAN route.

That way, when I up­date perl in the fu­ture, I will ei­ther get a warn­ing that I am break­ing these mod­ules de­pen­den­cies, or up­date them au­to­mat­i­cal­ly as well.

For bet­ter per­for­mance, (re­mem­ber we are re­plac­ing a tight C pro­gram with a Perl scrip­t!) it's rec­om­mend­ed you use PPerl.

I can't find a de­cent RPM of PPer­l, so just use CPAN for this one:

[root@localhost root]# perl -MCPAN -e shell
cpan shell -- CPAN exploration and modules installation (v1.61)
ReadLine support available (try 'install Bundle::CPAN')

cpan> install PPerl
:
: (time passes)
:
PPerl is up to date.

To make qpsmt­pd use PPerl in­stead of Per­l, ed­it ~smt­pd/qpsmt­pd/qpsmt­pd and make the first line say #!/us­r/bin/p­perl -Tw One mi­nor an­noy­ance of us­ing pperl is that when you bring down the ser­vice and then raise it again, the qpsmt­pd pro­cess­es may still be the same ones you had be­fore stop­ping. So, make sure you kill all qpsmt­pd in­stances af­ter stop­ping the ser­vice, or some con­fig­u­ra­tion changes may not be in ef­fec­t.

Installing qpsmtpd

Add a us­er called smt­pd, and ex­pand qpsmt­pd there:

adduser smtpd -s /sbin/nologin -m
cd ~smtpd
wget http://develooper.com/code/qpsmtpd/files/qpsmtpd-0.26.tar.gz
tar xzvf qpsmtpd-0.26.tar.gz
mv qpsmtpd-0.26 qpsmtpd
Running qpsmtpd

Put the IP ad­dress where qpsmt­pd will lis­ten for con­nec­tions in ~smt­pd/qpsmt­pd/­con­fig/IP (usu­al­ly just 0, and lis­ten in all ad­dress­es).

The smt­pd us­er needs write ac­cess to ~smt­pd/tm­p/ but no oth­er di­rec­to­ry, so do some­thing like this:

chown -R root.smtpd ~smtpd
find -t d ~smtpd -exec chmod -R 750 {} \;
mkdir ~smtpd/tmp
chown -R smtpd ~smtpd/tmp
chmod -R g+rx ~smtpd
chmod 700 ~smtpd/tmp

I use the su­per­vise tools to run my qmail, so qpsmt­pd can be man­aged by them like this:

svc-add ~smtpd/qpsmtpd/

I had to mod­i­fy qpsmt­pd's run script /ser­vice/qpsmt­pd/run be­cause it used /us­r/lo­cal/bin/tcpserv­er and in my sys­tem it's /us­r/bin/tcpserv­er in­stead. I al­so had to change it so that it would use the re­lay­ing rules for smt­pd (-x op­tion). The end re­sult is like this:

#!/bin/sh
QMAILDUID=`id -u smtpd` NOFILESGID=`id -g smtpd` exec /usr/local/bin/softlimit -m 25000000 \ /usr/bin/tcpserver -c 10 -v -R -p \ -u \$QMAILDUID -g \$NOFILESGID -x /etc/tcpcontrol/smtp.cdb `head -1 config/IP` smtp \ ./qpsmtpd 2>&1

If you run your qmail some oth­er way, just in­stall su­per­vise any­way (it's eas­i­er than fig­ur­ing how to run qpsmt­pd with­out it ;-)

By de­fault qpsmt­pd should work as a drop-in re­place­ment for qmail's smt­pd. so all you have to do now is stop smt­pd and start qpsmt­pd. Us­ing the su­per­vise script­s, it's some­thing like this:

[root@localhost root]# svc-stop smtpd
Stopping smtpd: smtpd svcsmtpd/log done.
[root@localhost root]# svc-start qpsmtpd
Starting qpsmtpd: qpsmtpd/log qpsmtpd done.

If qpsmt­pd start­ed right, you should see some­thing like this:

[root@localhost root]# telnet localhost 25
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 localhost.localdomain ESMTP qpsmtpd 0.26 ready; send us your mail, but not your spam.

If it did­n't work, qpsmt­pd's log is at /ser­vice/qpsmt­pd/log/­main/cur­rent , read it care­ful­ly!

Configuring qpsmtpd

Of course, a slow­er drop-in re­place­ment for smt­pd is not what we want­ed ;-). What makes qpsmt­pd worth in­stalling is its plug­ins. Here are some I found use­ful:

check_badmailfrom
Support for qmail's badmailfrom file. Read the qmail-smtpd docs for explanation.
check_badrcptto
Like badmailfrom but for recipient addresses, the doc says.
check_earlytalker
Some spammers will open a connection, and without waiting for the greeting, will just dump the mails through the connection. They do it to work faster. Well, no sane, honest mailer does that, so, why not reject the mail if the sender tries to talk too early? That's what this plugin detects.
check_relay
Allow relaying if the RELAYCLIENT variable is set. This is the usual mechanism used in qmail to allow relaying, so you are very likely to need this! (unless you use check_rcptto)
count_unrecognized_commands
This one closes the connection if the sender makes mistakes. Why this is necessary is a bit long to explain, though.
dnsbl and rhsbl
These use public lists of hosts that send spam. I am not a fan of using that kind of thing, but if you are, they are here.
spamassassin
Enable spamassassin server-wide. I prefer to do it per-user, but hey, whatever floats your boat.
check_rcptto
This one is very important... and it's not included in qpsmtpd by default! You can get it here, I got it from somewhere in qpsmtpd's mailing list archives, posted by its author, Andrew Pam, just save it in ~smtpd/qpsmtpd/plugins .It should be used instead of check_relay.

Keep in mind that this plug­in on­ly works if you use a qmail set­up with reg­u­lar us­er ac­counts, a ful­ly vir­tu­al set­up like vmailm­gr.

The plug­ins are en­abled in the ~smt­pd/qpsmt­pd/­con­fig/­plu­g­ins file, here's mine:

#
#  Example configuration file for plugins
#

# enable this to get configuration via http; see perldoc
# plugins/http_config for details.
#   http_config http://localhost/~smtpd/config/  http://www.example.com/smtp.pl?config=

#quit_fortune

check_earlytalker
count_unrecognized_commands 4

#require_resolvable_fromhost

#rhsbl
#dnsbl
check_badmailfrom
check_badrcptto
check_spamhelo

# this plugin needs to run after all other "rcpt" plugins
#check_relay
#Using check_rcptto instead of check_relay
check_rcptto


# content filters
#klez_filter

# You can run the spamassassin plugin with options.  See perldoc
# plugins/spamassassin for details.
#
#spamassassin

# rejects mails with a SA score higher than 20 and munges the subject
# of the score is higher than 10.
#
#   spamassassin reject_threshold 20 munge_subject_threshold 10


# run the clamav virus checking plugin
# clamav

# queue the mail with qmail-queue
queue/qmail-queue

If all worked right, you should get an er­ror mes­sage when you send an email to an un­known lo­cal user, and it should work for those who ex­ist:

[root@localhost plugins]# telnet localhost 25
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 localhost.localdomain ESMTP qpsmtpd 0.26 ready; send us your mail, but not your spam.
helo pepe
250 localhost.localdomain Hi [127.0.0.1] [127.0.0.1]; I am so happy to meet you.
mail from: ralsina@whatever.org
250 ralsina@whatever.org, sender OK - how exciting to get mail from you!
rcpt to: ralsina@mydomain.com
250 ralsina@mydomain.com, recipient ok
rcpt to: noone@mydomain.com
550 Return to sender, address unknown

Final Words

While in­stalling and con­fig­ur­ing qpsmt­pd is not a triv­ial ef­fort, the end re­sult is a smtp serv­er that us­es less net­work re­sources, per­haps at the cost of a high­er CPU or mem­o­ry us­age. A client of mine han­dles each day many thou­sands of mes­sages that would be bounced more ef­fi­cient­ly by qpsmt­pd's check­_r­cpt­to.

For small server­s, this is maybe not worth the ef­fort. For larg­er ones, it's al­most manda­to­ry. I keep wish­ing some­one will patch the reg­u­lar qmail-smt­pd so it will do some­thing like check­_r­cpt­to, but haven't found it yet.

There's a re­place­ment called mag­ic-smt­pd that I haven't re­al­ly tried yet, and may be the sub­ject of a fu­ture ar­ti­cle.

I still haven't fig­ured out how to use some oth­er tech­niques like tarpit­ting and rate lim­it­ing with qpsmt­pd. As soon as I do, there will be a "Fight­ing Spam with Qmail (part II­I)"

Important Links

Roberto Alsina / 2006-04-04 16:24:

Comments for this story are here:

http://www.haloscan.com/com...


Contents © 2000-2020 Roberto Alsina