Sysop:GreylistingExampleSetUp
Im folgenden sind die Configs und Setups für ein Greylisting via MySQL und Exim:
greylisting ([1] [2]) is a bit more problematic than blacklists but is as effective as blacklists for the mail that passed them.
there are serveral solutions out there. we are using a plain mysql solution from: http://slett.net/spam-filtering-for-mx/exim-greylisting.html
table setup
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) );
exim config
In the main section of your Exim configuration file, declare the following macros:
# if you don't have another database defined, then define it here hide mysql_servers = localhost/exim4/user/password # 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
Now, in the ACL section (after begin acl), declare a new ACL named "greylist_acl":
.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
Incorporate this ACL into your acl_rcpt_to to greylist triplets where the sender address is non-empty. This is to allow for sender callout verifications:
.ifdef GREYLIST_ENABLED defer condition = ${if or {{eq {$interface_port}{10025}} \ {eq {$received_protocol}{spam-scanned}} \ }{0}{1}} !senders = : postmaster@* acl = greylist_acl message = greylisted - try again later .endif
Also incorporate it into your acl_data 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.
.ifdef GREYLIST_ENABLED defer condition = ${if or {{eq {$interface_port}{10025}} \ {eq {$received_protocol}{spam-scanned}} \ }{0}{1}} senders = : postmaster@* acl = greylist_acl message = greylisted - try again later .endif
usage
table desciption
email_greylist:
- relay_ip: from which server is the mail?
- sender: from which address
- recipient: for who is the mail?
- block_expires:
- record_expires: when drops the record?
- create_time
- type: AUTO=by exim MANUAL=by hand inserted
- passcount: how many times a mail got through this rule
- blockcount: how many times a mail got temporarly blocked.
enable for a domain/host/sender
DELETE FROM email_greylist where type='MANUAL' and recipient like '@$domain'
this ensures that all entries are dropped, which we added by hand.
disable
DELETE FROM email_greylist where recipient like '%@$domain'; INSERT INTO email_greylist (recipient) VALUES ('@$domain');
it is important that before you insert the recepient/sender/host etc. that you delete all entries otherwise it won't work.