Skip to main content

Ralsina.Me — Roberto Alsina's website

Fighting Spam with Qmail (part III)

Yes, af­ter over two years, Fight­ing spam with Qmail is back.

You can see part 1 and part 2 of this se­ries too, if you wan­t.

This in­stall­ment will be about us­ing RBL ef­fec­tive­ly to de­crease your spam-re­lat­ed serv­er load, and the spam your users get.

The soft­ware de­scribed here is now part of my plug­ins pack­age

What is RBL?

RBL is a nice idea, us­ing DNS to tag IP­s.

For ex­am­ple, sup­pose you want to tag all the IPs that you know send spam:

10.0.0.14
172.16.2.3

You could have a DNS serv­er for the do­main "myr­bl.tld", and then re­solve for those IPs like this:

10.0.0.14.myrbl.tld -> 127.0.0.1
172.16.2.3.myrbl.tld -> 127.0.0.2

You can cod­i­fy your opin­ion of that IP in the an­swer. For in­stance, 127.0.0.1 may mean 10.0.0.14 is an open re­lay, and 127.0.0.2 may mean that 172.16.2.3 is a known spam­mer.

Or you can just say "host not found" is good, any­thing else is bad.

There are a num­ber of RBL servers out there, but I am most fa­mil­iar with spam­cop.net and njabl.org

So, when­ev­er you get a SMTP con­nec­tion, you can check the IP agains one data­base (or both­), and then you can de­cide to block (or not) the con­nec­tion based on the an­swer you get.

Neat, right?

The problems with RBL

There are many is­sues you need to keep in mind. Most of them are pol­i­cy re­lat­ed, a few are tech­ni­cal, but here are some I came up with:

  • What RBL should you use?

    RBLs are not all equal. Some are more zeal­ous than oth­­er­s, some would sim­­ply make your serv­er un­us­able. Some of them, once you get in, it's al­­most im­­pos­si­ble to get out, and they list rou­tine­­ly many le­git­i­­mate server­s. I am not vouch­ing even for the ones I use!

  • What is nore im­­por­­tan­t, spam or email?

    What­ev­er you use, there is bound to be some email that can not be sent to you eas­i­­ly. Some friend/­­clien­t/what­ev­er of yours is go­ing to have his SMTP serv­er list­ed and it's go­ing to be an­noy­ing.

    So, if you must ab­­so­­lute­­ly, pos­i­­tive­­ly get all your mail, RBLs are not a great idea.

  • Main­te­­nance.

    You will even­­tu­al­­ly have to deal with ex­­tra sup­­port ques­­tion­s, com­­plaints, etc. about why you are "block­­ing some­one's mail" and some­­such. If you are care­­ful, that should be kept to a min­i­­mum. But nev­er to ze­ro.

  • Poli­­cies.

    When do you per­­form the check? What do you do with the checks re­­sult­s? Here are some ex­am­­ples:

    1. Check on con­nec­­tion, and refuse con­nec­­tions from list­ed IP­s.
    2. Check on the MAIL com­­mand, and refuse to ac­­cept mails from list­ed IPs if the us­er is not au­then­ti­­cat­ed.
    3. Check on MAIL and add a head­­er to all mail com­ing from RBL-list­ed servers with nonau­then­ti­­cat­ed and let it pass, lat­er file it in a sep­a­rate fold­er.

You can be cre­ative here, peo­ple, as long as you read this thing all the way through :-)

Tools

Qmail comes with a RBL tool, called rblsmt­pd . Per­son­al­ly I don't like it much, and I am not go­ing to cov­er it fur­ther, but here's what I un­der­stand about how it's meant to be used:

You hook it to your qmail-smt­pd com­mand chain (be­fore qmail-smt­pd), and then if TCPRE­MOTEIP is RBL-list­ed, it hangs up the con­nec­tion.

My prob­lem with that is that if you want to use a dy­namip-ip ad­dress RBL, then al­most none of your own users will be able to use your own server!

A so­lu­tion to that is to al­so run a MSA serv­er on port 587, with­out RBL check­s, with manda­to­ry SMTP AU­TH, and tell your users to use that.

I do rec­om­mend run­ning such a MSA serv­er. How­ev­er, the sup­port load for the change to MSA is large (all com­mon MUAs come pre­con­fig­ured for port 25), and users will make mis­takes, then get a cryp­tic er­ror about how their con­nec­tion is not want­ed.

I pre­fer to use a plug­in along with Qmail-SPP.

Qmail-SPP is a qmail patch that lets you hook your own plug­ins (s­mall pro­gram­s) in­to qmail-smt­pd at ev­ery step of the pro­cess, make de­ci­sions about it, and mod­i­fy head­ers (and oth­er things, it's re­al­ly cool func­tion­al­i­ty!)

What I do is put a plug­in I wrote, called rblchecks at the be­gin­ning of the MAIL com­mand.

If the us­er is au­then­ti­cat­ed, it will do noth­ing, so it will ac­cept mail from your own user­s.

But if the us­er is not au­then­ti­cat­ed, and the source IP is RBL-list­ed, it will kick you out (ac­tu­al­ly, it on­ly gives you a mes­sage).

I wrote it us­ing the Bet­ter String Li­brary main­ly be­cause pro­gram­ming C for ar­bi­trary in­put I am a com­plete chick­en.

But first, a few con­ven­tions on how you write a qmail-spp plug­in.

  1. Ev­ery­thing you print on std­out goes to qmail-sp­p, and it's com­mand­s. Like "Give this er­ror" or "Add this head­er"
  2. You get your in­for­ma­tion about the mes­sage and the con­nec­tion from en­vi­ron­ment vari­ables. for ex­am­ple TCPRE­MOTEIP is the re­mote ip.
  3. What you print on stderr goes to the logs. So be care­ful, and log mean­ing­ful­ly! Specif­i­cal­ly, I al­ways print the plug­in name, and the par­ent process ID, which can be used to group all mes­sages from a sin­gle con­nec­tion, thus mak­ing the log use­ful.
  4. Be care­ful.
  5. Be quick. What­ev­er you de­lay, there may be a guy wait­ing, look­ing at out­look while you dither.

To make my plug­in more con­fig­urable, I de­cid­ed that you could pass a colon-sep­a­rat­ed list of RBL servers to be checked in the RBLSERVERS en­vi­ron­ment vari­able.

Usu­al­ly, you would set this ei­ther on qmail's start­up scrip­t, or, if you are us­ing tcpserv­er (y­ou prob­a­bly are if you are us­ing qmail), from its tcprules con­trol file.

For ex­am­ple, this is a per­fect­ly le­gal and nice rules file:

:allow,RBLSERVERS="bl.spamcop.net:combined.njabl.org",QMAILQUEUE="/usr/bin/simscan"

Yup. One line. No re­lays (use au­then­ti­ca­tion, guys!). Two RBL list­s. Sim­scan so you get no virus­es.

Then, you con­fig­ure the rblchecks plug­in in your smtp­plu­g­ins file on the mail sec­tion.

And that's it. You should see your spam go down, and you should see your serv­er load go down. In­stant­ly.

So, you want the plug­in code? Get it then!

As a bonus, you get a whole col­lec­tion of plug­ins for dif­fer­ent pur­pos­es, but please, please, please, don't use them un­less you re­al­ly know what you are do­ing. This is pret­ty much all new code :-)

Any com­ments, bug re­port­s, hap­py sto­ries... email me! Or leave com­ments. Or both!

And here is the code for this plug­in (there may be new­er code in the lat­est plug­in):

/*
* ­Copy­right (C) 2003-2004 Per­olo Si­lan­ti­co <per.sil@g­mx.it>
* ­Copy­right (C) 2006 Rober­to Alsi­na <ralsi­na@kde.org>
*
* ­For any ques­tion­s ­please ­con­tac­t Rober­to Alsi­na, be­cause
* this ver­sion is heav­i­ly ­mod­i­fied from the o­rig­i­nal.
*
* This pro­gram is free ­soft­ware; y­ou ­can re­dis­tribute it and/or
* ­mod­i­fy it un­der the terms of the GNU ­Gen­er­al Pub­lic Li­cense
* as pub­lished by the Free ­Soft­ware ­Foun­da­tion; ei­ther
* ver­sion 2 of the Li­cense, or (at y­our op­tion) any later
* ver­sion.
*
* This pro­gram is dis­tribut­ed in the hope that it will be use­ful,
* but WITH­OUT ANY WAR­RAN­TY; with­out even the im­plied war­ran­ty of
* MER­CHANTABIL­I­TY or ­FIT­NESS ­FOR A ­PAR­TIC­U­LAR PUR­POSE.  See the
* GNU ­Gen­er­al Pub­lic Li­cense ­for ­more de­tail­s.
*
* Y­ou should have re­ceived a ­copy­ of the GNU ­Gen­er­al Pub­lic Li­cense
* a­long with­ this pro­gram; if not, write ­to the Free ­Soft­ware ­Foun­da­tion,
* Inc., 59 Tem­ple ­Place - ­Suit­e 330, ­Boston, ­MA 02111-1307, USA.
*
*/


\#in­clude <arpa/inet.h>
\#in­clude <net­d­b.h>
\#in­clude <st­dio.h>
\#in­clude <st­dlib.h>
\#in­clude "b­str­lib.h"
\#in­clude <sys/­type­s.h>
\#in­clude <u­nist­d.h>

int
check­_r­bl   (bstring   lookup_ad­dr,   con­st   char   *rbl)
{
  
struct   ad­drin­fo   *ai   =   NULL;

  
bstring   lookup­name   =   bfor­mat   ("%s.%s",   lookup_ad­dr->da­ta,   rbl);
  
if   (getad­drin­fo   (lookup­name->da­ta,   NULL,   NULL,   &ai))
    
{
      
if   (ai)
        
freead­drin­fo   (ai);
      
re­turn   0;
    
}
  
freead­drin­fo   (ai);
  
re­turn   1;
}

bstring
en­vtostr   (char   *vname)
{
  
bstring   ret­val;
  
ret­val   =   bfrom­c­str   (getenv   (vname));
  
if   (!ret­val)
    
ret­val   =   bfrom­c­str   ("");

  
re­turn   ret­val;
}


int
main   (int   argc,   char   *argv[])
{
  
bstring   ip   =   bfrom­c­str   (getenv   ("TCPRE­MOTEIP"));
  
un­signed   long   ad­dress;
  
int   ppid   =   getp­pid   ();

  
if   (!ip)                        //­Some­how we are run­ning in a bad si­t­u­a­tion
    
{
      
printf   ("D\n");
      
fprintf   (stderr,   "r­blcheck­s: pid %d - no TCPRE­MOTEIP\n",   ppid);
      
ex­it   (0);
    
}



  
ad­dress   =   in­et_ad­dr   (ip->da­ta);
  
bstring   ad­dr   =   bfor­mat   ("%lu.%lu.%lu.%lu",
                          
(ad­dress   &   0xf­f000000)   >>   24,
                          
(ad­dress   &   0x00f­f0000)   >>   16,
                          
(ad­dress   &   0x0000f­f00)   >>   8,
                          
(ad­dress   &   0x000000ff)   >>   0);

  
//If au­then­ti­cat­ed, ­don't check­ at all
  
if   (getenv   ("SMT­PAU­THUSER"))
    
{
      
printf   ("A\n");
      
fprintf   (stderr,
               
"r­blcheck­s: pid %d - No check­s per­formed, be­cause user is au­then­ti­cat­ed\n",
               
ppid);
      
ex­it   (0);
    
}

  
bstring   rbl   =   bfrom­c­str   (getenv   ("R­BLSERVER­S"));
  
if   (!rbl)
    
rbl   =   bfrom­c­str   ("bl.s­pam­cop.net");

  
struct   bstrList   *list   =   bsplit   (rbl,   ':');

  
int   i   =   0;

  
for   (;   i   <   list->qty;   i++)
    
{
      
bstring   serv   =   list->en­try[i];
      
if   (serv->slen   ==   0)
        
con­tin­ue;
      
if   (check­_r­bl   (ad­dr,   serv->da­ta))
        
{
          
printf
            
("E541 Y­our IP (%s) is blocked, ­more in­for­ma­tion at  http://%s\n",
             
ip->da­ta,   serv->da­ta);
          
fprintf   (stderr,   "r­blcheck­s: pid %d - 541 Blocked by %s (%s)\n",
                   
ppid,   serv->da­ta,   ip->da­ta);
          
ex­it   (0);
        
}
    
}

  
// No R­BL is­sues
  
fprintf   (stderr,   "r­blcheck­s: pid %d - Ac­cept­ed %s\n",   ppid,   ip->da­ta);
  
ex­it   (0);
}
speel / 2006-04-04 15:43:

You can also use another smtp process, like qpsmtpd, which already can do what you want.
See Sme-Server (contribs.org) for an implementation.

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

Yes, I covered qpsmtpd in Fighting spam with QMail II.

I have grown not to like it so much since then, though.

No specific grudge, but I prefer this one on a purely gut-based decision process.

employment background check / 2011-12-27 23:21:


Hi very nice article


Contents © 2000-2023 Roberto Alsina