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
