exim greylisting


I no longer maintain this because I no longer use greylisting on my server, it proved to be little efficient when having a lot of other SPAM checks and often deferred valid email for me and my users. I'm leaving this here for reference only, I'll gladly accept patches and publish them here (with permission) if you send them to me.

advanced greylisting including statistics and manual whitelisting capability with exim 4 and mysql.

Author: Johannes Berg


Based on the original whitelisting whitepaper at http://projects.puremagic.com/greylisting/.

Partially based on the database code from http://theinternetco.net/projects/exim/greylist which is in turn based on http://raw.no/personal/blog/tech/Debian/2004-03-14-15-55_greylisting


  • manual white- and blacklisting by IP and IP subnet
  • manual white- and blacklisting by $sender_address and $sender_address_domain
  • manual white- and blacklisting by $local_part@domain, $local_part and $domain
  • manual white- and blacklisting by any combination of the above
  • individual email logging (including the rule relevant rule)
  • IP class definitions for common-pool servers


First you need to include the greylist-acl and the config-mysql files into your configuration (use .include, or if you use debian's split-config symlink them into acl/ and main/ respectively giving them appropriate numbers)

Based on the original greylisting whitepaper, you should use the greylist_acl as follows:

  • in your RCPT acl put:

    defer !senders = : postmaster
          acl = greylist_acl
          message = greylisted - try again later
  • in your DATA acl put:

    defer senders = : postmaster
          acl = greylist_acl
          message = greylisted - try again later


This data acl part is wrong. Don't use it. I will rewrite this to also work properly for bounces and mails addressed to postmaster. The idea is to allow bounces to be only to a single recipient, save that from $recipients into an acl variable and use that variable in the macros. For now, just leave it out completely, this will disable greylisting to postmaster and for bounces, but that doesn't normally affect its efficiency much.

This works since the greylist_acl can only return accept and deny. In the case of accept, the message is deferred, and in the case of deny the defer condition is skipped. We check the sender as well to not break callbacks that use an empty sender, or just postmaster as the sender.



fill in the appropriate fields (relay_ip, sender, recipient), leaving the others set to NULL, set type=MANUAL.

Combinations are possible.

For recipient you can also use "local_part@" and "@domain" for recipient, in addition to "local_part@domain"; For sender you can use "sender@domain" as well as just "@domain". Set record_expires to some high value (e.g. 9999-12-31 23:59:59).

IP class matching

I have added a new table that contains IP classes (see below). If a regular expression given in an ip_regex field matches the IP of the sender ($sender_host_address), the name given to that class is used instead. For example, I used the following statement to group web.de into a single class:

INSERT INTO exim4.exim_greylist_classes (ip_regex, name) values ('217\\.72\\.19[2-6]\\.[0-9]*', 'web.de');

after which the greylisting implementation will use "web.de" in the main table as relay_ip for any IP addres that matches the given regex (any address in the range -

Often, you will want to use this functionality instead of partial IP matching.

For reference, here are my current classes:

id ip_regex name comment
1 217.72.19[2-6].[0-9]* web.de None
2 213.165.6[4-6].[0-9]* gmx None
3 216.239.(3[2-9]|[45][0-9]|6[0-3]).[0-9]* google/gmail None
4 65.5[2-5].[0-9]*.[0-9]* microsoft hotmail uses this netblock, and retries from different servers
5 207.171.1([6-8][0-9]|91)..* amazon None
6 mail.duncanthrax.net None
7 216.73.(8[0-9]|9[0-5]).[0-9]* DOUBLECLICK-NET None

partial IP matching (subnets)

Instead of setting a full IP address in the relay_ip field, insert a manual record that contains only a partial IP adress, for example "1.2.3.". Be sure to include the trailing dot ("."), otherwise it is possible to also match "" with an entry like "1.2.3" (because this is simply done with substring extraction).


Just like whitelisting, but also set block_expires very high.


greylist tables

relay_ip is that long to allow full IPv6 addresses and classnames from the class-table to fit.

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',
CREATE TABLE exim_greylist_classes (
  id bigint(20) NOT NULL auto_increment,
  ip_regex varchar(255) NOT NULL,
  name varchar(80) NOT NULL,
  comment varchar(255) default NULL,

logging table

if you want to enable logging, add this table:

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,


  • index the database!
  • figure out above for IPv6 Addresses (does exim give us the full address, with zeroes filled in? - no, it seems to give the shortest possible form (from glibc), therefore the current logic should suffice)