<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML ><HEAD ><TITLE >Adding Greylisting Support</TITLE ><META NAME="GENERATOR" CONTENT="Modular DocBook HTML Stylesheet Version 1.7"><LINK REL="HOME" TITLE="Spam Filtering for Mail Exchangers" HREF="index.html"><LINK REL="UP" TITLE="Exim Implementation" HREF="exim.html"><LINK REL="PREVIOUS" TITLE="Adding SMTP transaction delays" HREF="exim-smtpdelays.html"><LINK REL="NEXT" TITLE="Adding SPF Checks" HREF="exim-spf.html"></HEAD ><BODY CLASS="section" BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF" VLINK="#840084" ALINK="#0000FF" ><DIV CLASS="NAVHEADER" ><TABLE SUMMARY="Header navigation table" WIDTH="100%" BORDER="0" CELLPADDING="0" CELLSPACING="0" ><TR ><TH COLSPAN="3" ALIGN="center" >Spam Filtering for Mail Exchangers: </TH ></TR ><TR ><TD WIDTH="10%" ALIGN="left" VALIGN="bottom" ><A HREF="exim-smtpdelays.html" ACCESSKEY="P" >Prev</A ></TD ><TD WIDTH="80%" ALIGN="center" VALIGN="bottom" >Appendix A. Exim Implementation</TD ><TD WIDTH="10%" ALIGN="right" VALIGN="bottom" ><A HREF="exim-spf.html" ACCESSKEY="N" >Next</A ></TD ></TR ></TABLE ><HR ALIGN="LEFT" WIDTH="100%"></DIV ><DIV CLASS="section" ><H1 CLASS="section" ><A NAME="exim-greylisting" ></A >A.6. Adding Greylisting Support</H1 ><P > There are several alternate greylisting implementations available for Exim. Here we will cover a couple of these. </P ><DIV CLASS="section" ><H2 CLASS="section" ><A NAME="exim-greylistd" ></A >A.6.1. greylistd</H2 ><P > This is a Python implementation developed by <EM >yours truly</EM >. (So naturally, this is the implementation I will include in the <A HREF="exim-final.html" >Final ACLs</A > to follow). It operates as a stand-alone daemon, and thus does not depend on any external database. Greylist data is stored as simple 32-bit hashes for efficiency. </P ><P > You can find it at <A HREF="http://packages.debian.org/unstable/mail/greylistd" TARGET="_top" >http://packages.debian.org/unstable/mail/greylistd</A >. Debian users can get it via APT: <TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><FONT COLOR="#000000" ><PRE CLASS="screen" ># apt-get install greylistd</PRE ></FONT ></TD ></TR ></TABLE > </P ><P > To consult <TT CLASS="option" >greylistd</TT >, we insert two statements in <A HREF="exim-final.html#acl_rcpt_to_final" >acl_rcpt_to</A > ACL that we previously declared, right before the final <TT CLASS="option" >accept</TT > statement: </P ><P > <TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><FONT COLOR="#000000" ><PRE CLASS="screen" > # Consult "greylistd" to obtain greylisting status for this particular # peer/sender/recipient triplet. # # We do not greylist messages with a NULL sender, because sender # callout verification would break (and we might not be able to # send mail to a host that performs callouts). # defer message = $sender_host_address is not yet authorized to deliver mail \ from <$sender_address> to <$local_part@$domain>. \ Please try later. log_message = greylisted. domains = +local_domains : +relay_to_domains !senders = : postmaster@* set acl_m9 = $sender_host_address $sender_address $local_part@$domain set acl_m9 = ${readsocket{/var/run/greylistd/socket}{$acl_m9}{5s}{}{}} condition = ${if eq {$acl_m9}{grey}{true}{false}} </PRE ></FONT ></TD ></TR ></TABLE > </P ><P > Unless you incorporate <A HREF="exim-sign.html" >envelope sender signatures</A > to block bogus <A HREF="gloss.html#dsn" ><I CLASS="glossterm" >Delivery Status Notification</I ></A >s, you may want to add a similar statement in your <A HREF="exim-final.html#acl_data_final" >acl_data</A > to also greylist messages with a NULL sender. </P ><P > The data we use for greylisting purposes here will be a little different than above. In addition to <TT CLASS="option" >$sender_address</TT > being emtpy, neither <TT CLASS="option" >$local_part</TT > nor <TT CLASS="option" >$domain</TT > is defined at this point. Instead, the variable <TT CLASS="option" >$recipients</TT > contains a comma-separated list of all recipient addresses. For a legitimate DSN, there should be only one address. <TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><FONT COLOR="#000000" ><PRE CLASS="screen" > # Perform greylisting on messages with no envelope sender here. # We did not subject these to greylisting after RCPT TO: because # that would interfere with remote hosts doing sender callouts. # defer message = $sender_host_address is not yet authorized to send \ delivery status reports to <$recipients>. \ Please try later. log_message = greylisted. senders = : postmaster@* set acl_m9 = $sender_host_address $recipients set acl_m9 = ${readsocket{/var/run/greylistd/socket}{$acl_m9}{5s}{}{}} condition = ${if eq {$acl_m9}{grey}{true}{false}} </PRE ></FONT ></TD ></TR ></TABLE > </P ></DIV ><DIV CLASS="section" ><H2 CLASS="section" ><A NAME="exim-greylist-mysql" ></A >A.6.2. MySQL implementation</H2 ><P > The following inline implementation was contributed by Johannes Berg <TT CLASS="email" ><<A HREF="mailto:johannes (at) sipsolutions.net" >johannes (at) sipsolutions.net</A >></TT >, based in part on: </P ><P ></P ><UL ><LI ><P > work by Rick Stewart <TT CLASS="email" ><<A HREF="mailto:rick.stewart (at) theinternetco.net" >rick.stewart (at) theinternetco.net</A >></TT >, published at <A HREF="http://theinternetco.net/projects/exim/greylist" TARGET="_top" >http://theinternetco.net/projects/exim/greylist</A >, in turn based on </P ></LI ><LI ><P > a Postgres implementation created by Tollef Fog Heen <TT CLASS="email" ><<A HREF="mailto:tfheen (at) raw.no" >tfheen (at) raw.no</A >></TT >, available at <A HREF="http://raw.no/personal/blog/tech/Debian/2004-03-14-15-55_greylisting" TARGET="_top" >http://raw.no/personal/blog/tech/Debian/2004-03-14-15-55_greylisting</A > </P ></LI ></UL ><P > It requires no external programs - the entire implementation is based on these configuration snippets along with a MySQL database. </P ><P > An archive containing up-to-date configuration snippets as well as a <TT CLASS="option" >README</TT > file is available at: <A HREF="http://johannes.sipsolutions.net/wiki/Projects/exim-greylist" TARGET="_top" >http://johannes.sipsolutions.net/wiki/Projects/exim-greylist</A >. </P ><P > MySQL needs to be installed on your system. At a MySQL prompt, create an <TT CLASS="option" >exim4</TT > database with two tables named <TT CLASS="option" >exim_greylist</TT > and <TT CLASS="option" >exim_greylist_log</TT >, as follows: <TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><FONT COLOR="#000000" ><PRE CLASS="screen" > CREATE DATABASE exim4; use exim4; CREATE TABLE exim_greylist ( id bigint(20) NOT NULL auto_increment, relay_ip varchar(80) default NULL, sender varchar(255) default NULL, recipient varchar(255) default NULL, block_expires datetime NOT NULL default '0000-00-00 00:00:00', record_expires datetime NOT NULL default '9999-12-31 23:59:59', create_time datetime NOT NULL default '0000-00-00 00:00:00', type enum('AUTO','MANUAL') NOT NULL default 'MANUAL', passcount bigint(20) NOT NULL default '0', blockcount bigint(20) NOT NULL default '0', PRIMARY KEY (id) ); CREATE TABLE exim_greylist_log ( id bigint(20) NOT NULL auto_increment, listid bigint(20) NOT NULL, timestamp datetime NOT NULL default '0000-00-00 00:00:00', kind enum('deferred', 'accepted') NOT NULL, PRIMARY KEY (id) ); </PRE ></FONT ></TD ></TR ></TABLE > </P ><P > In the <EM >main</EM > section of your Exim configuration file, declare the following macros: <TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><FONT COLOR="#000000" ><PRE CLASS="screen" > # if you don't have another database defined, then define it here hide mysql_servers = localhost/exim4/<TT CLASS="parameter" ><I >user</I ></TT >/<TT CLASS="parameter" ><I >password</I ></TT > # options # these need to be valid as xxx in mysql's DATE_ADD(..,INTERVAL xxx) # not valid, for example, are plurals: "2 HOUR" instead of "2 HOURS" GREYLIST_INITIAL_DELAY = 1 HOUR GREYLIST_INITIAL_LIFETIME = 4 HOUR GREYLIST_WHITE_LIFETIME = 36 DAY GREYLIST_BOUNCE_LIFETIME = 0 HOUR # you can change the table names GREYLIST_TABLE=exim_greylist GREYLIST_LOG_TABLE=exim_greylist_log # comment out to the following line to disable greylisting (temporarily) GREYLIST_ENABLED= # uncomment the following to enable logging #GREYLIST_LOG_ENABLED= # below here, nothing should normally be edited .ifdef GREYLIST_ENABLED # database macros GREYLIST_TEST = SELECT CASE \ WHEN now() > block_expires THEN "accepted" \ ELSE "deferred" \ END AS result, id \ FROM GREYLIST_TABLE \ WHERE (now() < record_expires) \ AND (sender = '${quote_mysql:$sender_address}' \ OR (type='MANUAL' \ AND ( sender IS NULL \ OR sender = '${quote_mysql:@$sender_address_domain}' \ ) \ ) \ ) \ AND (recipient = '${quote_mysql:$local_part@$domain}' \ OR (type = 'MANUAL' \ AND ( recipient IS NULL \ OR recipient = '${quote_mysql:$local_part@}' \ OR recipient = '${quote_mysql:@$domain}' \ ) \ ) \ ) \ AND (relay_ip = '${quote_mysql:$sender_host_address}' \ OR (type='MANUAL' \ AND ( relay_ip IS NULL \ OR relay_ip = substring('${quote_mysql:$sender_host_address}',1,length(relay_ip)) \ ) \ ) \ ) \ ORDER BY result DESC LIMIT 1 GREYLIST_ADD = INSERT INTO GREYLIST_TABLE \ (relay_ip, sender, recipient, block_expires, \ record_expires, create_time, type) \ VALUES ( '${quote_mysql:$sender_host_address}', \ '${quote_mysql:$sender_address}', \ '${quote_mysql:$local_part@$domain}', \ DATE_ADD(now(), INTERVAL GREYLIST_INITIAL_DELAY), \ DATE_ADD(now(), INTERVAL GREYLIST_INITIAL_LIFETIME), \ now(), \ 'AUTO' \ ) GREYLIST_DEFER_HIT = UPDATE GREYLIST_TABLE \ SET blockcount=blockcount+1 \ WHERE id = $acl_m9 GREYLIST_OK_COUNT = UPDATE GREYLIST_TABLE \ SET passcount=passcount+1 \ WHERE id = $acl_m9 GREYLIST_OK_NEWTIME = UPDATE GREYLIST_TABLE \ SET record_expires = DATE_ADD(now(), INTERVAL GREYLIST_WHITE_LIFETIME) \ WHERE id = $acl_m9 AND type='AUTO' GREYLIST_OK_BOUNCE = UPDATE GREYLIST_TABLE \ SET record_expires = DATE_ADD(now(), INTERVAL GREYLIST_BOUNCE_LIFETIME) \ WHERE id = $acl_m9 AND type='AUTO' GREYLIST_LOG = INSERT INTO GREYLIST_LOG_TABLE \ (listid, timestamp, kind) \ VALUES ($acl_m9, now(), '$acl_m8') .endif </PRE ></FONT ></TD ></TR ></TABLE > </P ><P > Now, in the ACL section (after <TT CLASS="option" >begin acl</TT >), declare a new ACL named <SPAN CLASS="QUOTE" >"greylist_acl"</SPAN >: <TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><FONT COLOR="#000000" ><PRE CLASS="screen" > .ifdef GREYLIST_ENABLED # this acl returns either deny or accept # since we use it inside a defer with acl = greylist_acl, # accepting here makes the condition TRUE thus deferring, # denying here makes the condition FALSE thus not deferring greylist_acl: # For regular deliveries, check greylist. # check greylist tuple, returning "accepted", "deferred" or "unknown" # in acl_m8, and the record id in acl_m9 warn set acl_m8 = ${lookup mysql{GREYLIST_TEST}{$value}{result=unknown}} # here acl_m8 = "result=x id=y" set acl_m9 = ${extract{id}{$acl_m8}{$value}{-1}} # now acl_m9 contains the record id (or -1) set acl_m8 = ${extract{result}{$acl_m8}{$value}{unknown}} # now acl_m8 contains unknown/deferred/accepted # check if we know a certain triple, add and defer message if not accept # if above check returned unknown (no record yet) condition = ${if eq{$acl_m8}{unknown}{1}} # then also add a record condition = ${lookup mysql{GREYLIST_ADD}{yes}{no}} # now log, no matter what the result was # if the triple was unknown, we don't need a log entry # (and don't get one) because that is implicit through # the creation time above. .ifdef GREYLIST_LOG_ENABLED warn condition = ${lookup mysql{GREYLIST_LOG}} .endif # check if the triple is still blocked accept # if above check returned deferred then defer condition = ${if eq{$acl_m8}{deferred}{1}} # and note it down condition = ${lookup mysql{GREYLIST_DEFER_HIT}{yes}{yes}} # use a warn verb to count records that were hit warn condition = ${lookup mysql{GREYLIST_OK_COUNT}} # use a warn verb to set a new expire time on automatic records, # but only if the mail was not a bounce, otherwise set to now(). warn !senders = : postmaster@* condition = ${lookup mysql{GREYLIST_OK_NEWTIME}} warn senders = : postmaster@* condition = ${lookup mysql{GREYLIST_OK_BOUNCE}} deny .endif </PRE ></FONT ></TD ></TR ></TABLE > </P ><P > Incorporate this ACL into your <A HREF="exim-final.html#acl_rcpt_to_final" >acl_rcpt_to</A > to greylist triplets where the sender address is non-empty. This is to allow for sender callout verifications: <TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><FONT COLOR="#000000" ><PRE CLASS="screen" > .ifdef GREYLIST_ENABLED defer !senders = : postmaster@* acl = greylist_acl message = greylisted - try again later .endif </PRE ></FONT ></TD ></TR ></TABLE > </P ><P > Also incorporate it into your <A HREF="exim-firstpass.html#acl_data_1" >acl_data</A > block, but this time only if the sender address is empty. This is to prevent spammers from getting around greylisting by setting the sender address to NULL. <TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><FONT COLOR="#000000" ><PRE CLASS="screen" > .ifdef GREYLIST_ENABLED defer senders = : postmaster@* acl = greylist_acl message = greylisted - try again later .endif </PRE ></FONT ></TD ></TR ></TABLE > </P ></DIV ></DIV ><DIV CLASS="NAVFOOTER" ><HR ALIGN="LEFT" WIDTH="100%"><TABLE SUMMARY="Footer navigation table" WIDTH="100%" BORDER="0" CELLPADDING="0" CELLSPACING="0" ><TR ><TD WIDTH="33%" ALIGN="left" VALIGN="top" ><A HREF="exim-smtpdelays.html" ACCESSKEY="P" >Prev</A ></TD ><TD WIDTH="34%" ALIGN="center" VALIGN="top" ><A HREF="index.html" ACCESSKEY="H" >Home</A ></TD ><TD WIDTH="33%" ALIGN="right" VALIGN="top" ><A HREF="exim-spf.html" ACCESSKEY="N" >Next</A ></TD ></TR ><TR ><TD WIDTH="33%" ALIGN="left" VALIGN="top" >Adding SMTP transaction delays</TD ><TD WIDTH="34%" ALIGN="center" VALIGN="top" ><A HREF="exim.html" ACCESSKEY="U" >Up</A ></TD ><TD WIDTH="33%" ALIGN="right" VALIGN="top" >Adding SPF Checks</TD ></TR ></TABLE ></DIV ></BODY ></HTML >