How do I setup spamtraps with Postfix so clients that send to trapped addresses are prevented from sending me more spam?



Bayesian filters and pattern matching and RBLs are all part of the fight against spam. This setup is intended to augment rather than replace those.

Note that I'm NOT greylisting. Greylisting is great in theory but in practice it's extremely annoying to your users. Some people have great success with it. I'm not one of those people.

What I've done is setup a number of email addresses (I have around 10,000 but you can make do with a couple hundred) to act as spamtraps. Lists of these addresses are discretely linked off various high traffic sites where bots will find them but people won't. These addresses are harvested, sold to spammers, and now I know where spam is going to come in.

Postfix is configured to redirect messages to any of these addresses to my spamtrap script, which adds them to a table in PF so that any further messages from that client end up in my tarpit.

The advantages:

  • Zero false-positive rate (or as close as you can get, at least)
  • Resource-cheap
  • Blocks spammers at the firewall layer after their first infraction
  • Self-healing (trapped addresses expire after 24 hours)

The disadvantages:

  • Not plug-and-play
  • Quite a few moving parts


Your mail server must be able to run PF and spamd. As far as I know, this means OpenBSD or FreeBSD. I'm sure these instructions can be adapted to flavors of Linux and their firewalls, but that's beyond the scope of my answer.

I've written this for FreeBSD. OpenBSD users should be able to adapt these steps mostly by changing paths.

Finally, this is all for Postfix 2.5+

table <spamd> persist
table <local-whitelist> persist file "/usr/local/etc/spamd/local-whitelist.txt"
  • Install spamd from ports
  • Create /usr/local/etc/spamd/local-whitelist.txt. PF will read this file to generate the <local-whitelist> table. I recommend referencing http://www.greylisting.org/whitelisting.shtml for addresses that should never be tarpitted. An example file:

# amazon


# apple

# ebay

# gmail

# postini

# skynet.be

# yahoo
  • Reload PF

  • Create /usr/local/scripts/get-spamtrapped:

/usr/local/sbin/spamdb | grep TRAPPED | cut -d '|' -f 2
  • Create /usr/local/etc/spamd/spamd.conf. I recommend using the nixspam and ualbert.ca lists as well, but at the least you need the spamtrapped and override lists. (NOTE: I know override is redundant with PF's rdr rules - I move things around enough that I want this double-protection):

# University of Alberta greytrap hits.
# Addresses stay in it for 24 hours from time they misbehave.
        :msg="Your address %A has sent mail to a ualberta.ca spamtrap\n\
        within the last 24 hours":\

# Nixspam recent sources list.
# Mirrored from http://www.heise.de/ix/nixspam
        :msg="Your address %A is in the nixspam list\n\
        See http://www.heise.de/ix/nixspam/dnsbl_en/ for details":\

# Trapped IPs - so we can block them without using greylisting
        :msg="Your address %A has sent mail to spamtrap on this server\n\
        within the last 24 hours":\

  • Set spamd to run on bootup. Note that you're not running in blacklist-only mode and you are throwing 5xx errors when someone tries to send to a blacklist. The former is so spamdb will handle storing/expiring trapped addresses. The latter is good manners. Add to /etc/rc.conf:
  • Start spamd: /usr/local/etc/rc.d/obspamd start

  • Cronjob to run spamd-setup in blacklist-only mode once an hour. Blacklist mode forces it to update the <spamd> pf table rather than spamd's internal tables. Since spamd is just tarpitting and storing data, everything else needs to be in PF. (replace XX with whatever minute of the hour you want it to run)

# spamd-setup
XX      *       *       *       *       root    /usr/local/sbin/spamd-setup -b
  • Create a spamtrap user on your machine. I give him a home directory for future extensions:
$ sudo pw useradd spamtrap -s /sbin/lologin -d /home/spamtrap -m -c "Spam Collector"
  • Create /usr/local/scripts/spamtrap:

# rudimentary checking - more complex checking will be done by 
# the pfctl and spamdb commands
if [[ ! ${#ADDRESS} = ${#1} ]]
        echo "Invalid characters in IP address"
        exit 1
if [ ! ${ADDRESS} ]
        echo "Usage: $0 <address>"
        exit 1

/usr/local/sbin/spamdb -t -a ${ADDRESS}
if [ "$?" -ne 0 ]
        echo "Failed to add ${ADDRESS} to spamdb"
        exit 1
/sbin/pfctl -qt spamd -T add ${ADDRESS}
if [ "$?" -ne 0 ]
        echo "Failed to add ${ADDRESS} to pf"
        exit 1
/usr/bin/logger -t spamtrap "Spamtrap caught ${ADDRESS}"
  • Configure a new transport service in Postfix's /usr/local/etc/postfix/master.cf. The X flag tells Postfix to consider this final delivery for the message so the spammer gets a success message. Note the user - it needs sudo access to the script.
# Spamtrap
spamtrapper  unix  -       n       n       -       -       pipe
  flags=X user=nobody argv=/usr/local/bin/sudo /usr/local/scripts/spamtrap ${client_address}
  • Add to sudoers:
nobody  ALL= NOPASSWD: /usr/local/scripts/spamtrap
  • Create a transport rule that sends all messages sent to spamtrap@localhost to the spamtrapper service. See 'postconf transport_maps' for which file to edit. The default is /usr/local/etc/postfix/transport:
spamtrap@localhost                      spamtrapper
  • Restart postfix. Send a couple messages to spamtrap@localhost and verify that the sending client is loaded into spamdb and into the <spamd> pf table.
$ echo "Test" | mail spamtrap@localhost
$ spamdb | grep
$ sudo pfctl -qt spamd -T show

Two things need to happen for each of your spamtrapped email addresses. First, it must resolve to an actual mailbox so it's not rejected during the SMTP dialog. I used virtual users aliased to spamtrap@localhost. Second, it needs to match a check_recipient_access rule in Postfix and get redirected to spamtrap@localhost so legit users included in the recipient list never have to see it. How I did this part:

  • Add to /usr/local/etc/postfix/main.cf:
virtual_maps = hash:/usr/local/etc/postfix/spamtrap_maps
smtpd_recipient_restrictions = check_recipient_access hash:/usr/local/etc/postfix/spamtrap_recipients
  • Format of spamtrap_maps:
spamtrappedaddress@domain.tld      spamtrap
  • Format of spamtrap_recipients:
spamtrappedaddress@domain.tld      REDIRECT spamtrap@localhost
  • Add these files to your Makefile and test.

At this point, all clients sending mail to your spamtrap addresses should be added to spamdb and <spamd>. Nothing is yet being sent to spamd. To make the whole blocking mechanism live, add to /etc/pf.conf and reload pf:

no rdr proto tcp from <local-whitelist> to port 25
rdr pass proto tcp from <spamd> to port 25 -> port 8025

And that's it.


  • It'd be trivial to modify the spamtrap script to store a copy of the message in a Bayesian spam corpus.

  • If you subscribe to any RBL rsync services, it's trivial to offload the bouncing of those message to spamd.


Well ok, looks like you're done with your own answer. Just don't blame postfix-policyd, it's much more flexible than you might think of as it provides several mechanisms for your fight against spam:

Policyd is an anti-spam plugin for Postfix (MySQL based) that does Greylisting, Sender-(envelope or SASL)-based throttling (on messages and / or volume per defined time unit), Spamtrap monitoring / blacklisting and HELO auto blacklisting.

You don't need to use all that waepons and you would probably have some more rules setup in your smtpd_recipient_restrictions.

Anyway, give it a try (and read the docs) or build up your own solution - it's up to you.

  • I had no idea policyd even existed. I'll check it out - thanks!
    – sh-beta
    Sep 22 '09 at 15:11

The solution presented by OP is rather complex. I've used an easier approach:

Step 1: Setup a filter file /etc/postfix/spamtraps

This is just an easy "match list": a regular expression (for the spamtrap) with an action to apply when met:

/spam(master|trap)?\@.*/i                DISCARD triggers spamtrap
/funkyspammer\@.*/i                      DISCARD triggers spamtrap

Adjust with your addresses.

Step 2: Setup the rules in /etc/postfix/main.cf

Look for the smtpd_recipient_restrictions block, and put the corresponding rule before your network checks (less load), but after the authenticated users. E.g.:

smtpd_recipient_restrictions = permit_sasl_authenticated
        check_recipient_access regexp:/etc/postfix/spamtraps

Step 3: Reload the daemon

Of course you need to reload postfix now, so your changes take effect. Depending on your distribution, this is done by either service postfix reload, /etc/init.d/postfix reload, or a similar command.

Step 4: Now keep those spammers out

This is an optional step you might wish to consider: Use Fail2ban, and have it watching your mail log file. As soon as someone addresses a spamtrap, have that IP banned (e.g. for 30 minutes, or maybe 1 hour – don't make it too long, as the spammer might use a dynamic IP which becomes "innocent" soon).


just a quick hint: use postfix-policyd, examples are included in config...

set it up as a policy service in your smtpd_recipient_restrictions in your main.cf, ie:

## call policyd
check_policy_service inet:,

works like a charm spamtraping together with greylisting enabled. Good Luck!


if anyone needs a sample Script to Step 4, here we go...

grep 'your_spamtrapaddress1@domain.com' /var/log/maillog >/tmp/badips_raw
grep 'your_spamtrapaddress2@domain.com' /var/log/maillog >>/tmp/badips_raw

egrep -o -e "[0-9]{2,3}\.[0-9]{2,3}\.[0-9]{2,3}\.[0-9]{2,3}" /tmp/badips_raw >> /tmp/badips_nu

sort -u /tmp/badips_nu > /tmp/badips

rm -rf /tmp/badips_raw
rm -rf /tmp/badips_nu

while read ip
fail2ban-client set postfix-sasl banip $ip
done < /tmp/badips

rm -rf /tmp/badips

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.