schlittermann

Why just another greylist for exim?

There are a lot greylist implementations around. I just needed a really simple one. A fast one. (Writing this document took me more time than setting up the grey list itself ;-))

How does it work?

This greylist works on the sender ip address (as available in $sender_host_address). Each sender ip address gets transformed: 192.168.10.12 becomes $BASEDIR/192/168/10/12. If the file $BASEDIR/192/168/10/12 exists and is old enough, the IP is accepted. Since we do not need more than 255 entries per directory the file system should be fast enough to be used as data base.

Pitfalls

If you expect to get mails from hosts with changing IP this greylist may not be for you, as every IP based greylist.

If you expect to get mails from providers with a large amount of outgoing mail servers it may take a while until your greylist learned about all of them.

Efficiency

Here some statistic (not updated any more because now I'm using a perl based solution (grey)).
 

ACL entry

It's up to you where your greylist rule gets applied. The HELO test may be a good choice. But if you've trusted hosts (authenticated), then you might give them a chance to authenticate first. For this reason I use this rule in the MAIL test:

acl_check_mail:

    accept  authenticated = *

    defer   condition = ${run{/etc/exim/bin/unseen $sender_host_address 600}}
	    log_message = grey listed ($sender_host_address)

    ...

The unseen script itself

It's quite simple, straight forward and hopefully without any pitfalls.

W. B. Hacker sent me some hints, so please read this note: Beware, it's only tested unter Debian GNU/Linux. Please test the script first, esp. check if the stat command on your Un*x system uses the same options. And check the #! /bin/bash line.

#! /bin/bash
# © 2006 Heiko Schlittermann
# You may use this script on you own risk.  It is published here under
# the terms of the GNU Copyright.

# Example:
# defer	condition = ${run{.../unseen $sender_host_address 300}{$value}}
#	log_message = gray unseen ($sender_host_address)

test -t 0 && trap "echo" EXIT

NAME="$1"; shift
DELAY="${1:-600}"; shift
BASE="${1:-/var/run/exim/grey.d}"; shift

NAME=${NAME//.//}
DIR=$BASE/${NAME%/*}
FILE=$DIR/${NAME##*/}


test -d "$DIR" || mkdir -p "$DIR"

test -f "$FILE" || {
	> "$FILE"
	echo -n yes
	exit 0 
}

# AGE = (current time) - (time of last modification of "$FILE")
# please check your systems 'stat' command!
AGE=$(($(date +%s) - $(stat -c '%Y' "$FILE")))

test $AGE -lt $DELAY && {
	echo -n yes
	exit 0
}

read <"$FILE"
echo -n no
exit 0

Maintainance

From time to time the directory containing the "tag" files should be cleaned: Remove all files that are not used for several days:
find $BASEDIR -type f -atime +7 | xargs --no-run-if-empty rm