. mombe.org
home of the mad cow
  Not A Blog
 

Friday, March 24, 2006

Exim, FreeBSD & Greylisting

I was trying to find neat greylisting solutions for exim and came to the conclusion that there weren't really any. The biggest stumbling block seemed to be my requirements: that it use something like the Berkeley DB (rather than a full fledged SQL database), and that it was in FreeBSD's ports system. There are plenty of SQL-based solutions, and no exim solutions in ports.

What I did find was postgrey, a Postfix policy server that supported greylisting. The question then became one of whether I could convince exim to speak Postfix ...

This initially looked quite easy. Postgrey listens on a unix domain socket, and exim has a readsocket expansion variable that reads from one. All I had to do was emulate enough of Postfix's protocol on the socket. What I came up with was:

  defer   log_message   = greylisted host $sender_host_address
          set acl_m0    = request=smtpd_access_policy\nprotocol_state=RCPT\n    \
                          protocol_name=${uc:$received_protocol}\n              \
                          helo_name=$sender_helo_name\n                         \
                          client_address=$sender_host_address\n                 \
                          client_name=$sender_host_name\n                       \
                          sender=$sender_address\n                              \
                          recipient=$local_part@$domain\n\n
          set acl_m0    = ${sg{${readsocket{/var/run/postgrey}{$acl_m0}         \
                          {5s}{}{action=DUNNO}}}{action=}{}}
          message       = ${sg{$acl_m0}{^\\w+\\s*}{}}
          condition     = ${if eq{${uc:${substr{0}{5}{$acl_m0}}}}{DEFER}{true}{false}}

(the lines with a \ right off to the right hand side aren't supposed to break — remove the whitespace and \ and put them on one line. You'll need to change /var/run/postgrey to whereever your policy engine's socket is.)

What this does is set acl_m0 to contain a Postfix-ish query for the policy server, and then pass that to postgrey. It then resets acl_m0 to postgrey's response, and interprets that response as an ACL condition. Note that it isn't a full implementation of Postfix's protocol. All it cares about is a "DEFER" response when it needs to defer mail. (note "DEFER" and "DEFER_IF_PERMIT" are handled the same.)

Note that the hardcoded action=DUNNO occurs when a socket read times out, and allows this to fail safe (i.e. mail will be delivered) in the event of a problem with postgrey.

Postfix's protocol allows for a policy server to prepend a header on delivered e-mail, and postgrey uses this to notify users of how long mail was delayed for. If you want to make use of this feature, you need the following ACL somewhere under your defer one.

  warn    message       = ${sg{$acl_m0}{^\\w+\\s*}{}}
          condition     = ${if eq{${uc:${substr{0}{7}{$acl_m0}}}}{PREPEND}{true}{false}}

This sets the headers to whatever comes after the "PREPEND" line.

There's a small problem with all of this, however. Postfix's protocol allows policy server re-use; exim expects a readsocket to send an EOF. postgrey implements the former and, as a result, exim never gets a response. Since postgrey's written in Perl, it's easy to change this behaviour. It's easily solved with a patch to postgrey. The patch adds a --no-reuse option ot postgrey which causes an EOF to be sent when a particular policy run is complete.

With this done, exim will happily greylist using postgrey. I suspect the same approach can be taken for many other Postfix policy servers ... YMMV and all that.

posted by guy at: 09:19 SAST | path: /systems | permanent link

Bloxsom Powered

© 2002-2005, webmaster@mombe.org
 
 
RSS Valid XHTML 1.0!

Creative Commons License