digressions from meandering


quick and trivial greylisting setup with exim and sqlite 27-Sep-2011 10:38 PM

This is the old config snippet I am about to copy from one config to another and, while I'm at it, I thought I'd put it here for future reference.

The good thing about this approach is that almost the whole thing is completely done in the config file and requires almost no setup elsewhere. The bad thing is that I don't think it will perform well on high load. On my MX with approximately 10K incoming connections a day it performs alright.

So, this is how it's done. Exim needs to be compiled with sqlite support, of course. First step is to initialize the database with this table:

CREATE TABLE greylist (
 gl_mailid text NOT NULL,
 gl_hostid text NOT NULL,
 gl_ctime integer NOT NULL DEFAULT ( strftime('%s','now') ),
 gl_atime integer NOT NULL DEFAULT ( strftime('%s','now') ),
 gl_btime integer NOT NULL DEFAULT ( strftime('%s','now') ),
 gl_ptime integer,
 gl_blocked integer DEFAULT 1,
 gl_passed integer DEFAULT 0,
 PRIMARY KEY (gl_mailid,gl_hostid)
);

Then put this (or similar)

GREYDB=/var/lib/exim4/greylist.db

line into main section of exim configuration.

This acl does the whole thing:

acl_greylist:
        warn
         set acl_c_gl_mailid = $sender_address+$local_part@$domain
         set acl_c_gl_hostid = $sender_host_address+$sender_helo_name
         set acl_c_gl = ${lookup sqlite {GREYDB select *, \
          strftime('%s','now')-gl_atime as sincea, strftime('%s','now')-gl_ctime as sincec from greylist \
          where gl_mailid='${quote_sqlite:$acl_c_gl_mailid}' and gl_hostid='${quote_sqlite:$acl_c_gl_hostid}'; \
          }}

        # defer strangers
        defer
         condition = ${if eq {$acl_c_gl}{} {true}{false}}
         set acl_c_nothing = ${lookup sqlite {GREYDB insert into greylist (gl_mailid,gl_hostid) VALUES \
          ('${quote_sqlite:$acl_c_gl_mailid}','${quote_sqlite:$acl_c_gl_hostid}'); }}
         set acl_c_gl_message = Come back in some 10 minutes, stranger

        # accept passers
        accept
         condition = ${if >{${extract{gl_passed}{$acl_c_gl}}}{0} {true}{false}}
         set acl_c_nothing = ${lookup sqlite {GREYDB update greylist SET gl_passed=gl_passed+1, gl_atime=strftime('%s','now') \
          where gl_mailid='${quote_sqlite:$acl_c_gl_mailid}' and gl_hostid='${quote_sqlite:$acl_c_gl_hostid}'; \
          }}
        # accept after 12 mins of silence or 3 hours of knocking
        accept
         condition = ${if or{\
          {>{${extract{sincea}{$acl_c_gl}}}{720}}\
          {>{${extract{sincec}{$acl_c_gl}}}{10800}}\
          }{true}{false}}
         set acl_c_nothing = ${lookup sqlite {GREYDB update greylist SET gl_atime=strftime('%s','now'), gl_passed=gl_passed+1, \
          gl_ptime=coalesce(gl_ptime,strftime('%s','now')) \
          where gl_mailid='${quote_sqlite:$acl_c_gl_mailid}' and gl_hostid='${quote_sqlite:$acl_c_gl_hostid}'; \
          }}
        defer
         set acl_c_nothing = ${lookup sqlite {GREYDB update greylist SET gl_atime=strftime('%s','now'), gl_blocked=gl_blocked+1, \
          gl_btime=strftime('%s','now') \
          where gl_mailid='${quote_sqlite:$acl_c_gl_mailid}' and gl_hostid='${quote_sqlite:$acl_c_gl_hostid}'; \
          }}
         set acl_c_gl_message = Come back in some 10 minutes, buddy

Finally, somewhere in acl_check_rcpt it would make sense to call the thing. Something like this:

 warn
  acl = acl_greylist

So the only thing left is a daily cleanup:

DELETE FROM greylist WHERE 3600*24*gl_passed < (strftime('%s','now')-gl_atime);

And, unless I am mistaken (which is not completely unlikely) the greylisting setup is complete.

This page is a work of fiction and is intended to be viewed for depression purpose only. All characters and incidents mentioned or not are fictional, any resemblance to the actual companies, incidents, or persons living or dead is unintended and purely coincidental. Note: This disclaimer is also fictitious.
Michael Krelin
Munich, Germany
born on the 28th of February
 
 subscribe