Sunday, January 16, 2011

How do I spamtrap with Postfix?

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

  • 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:127.0.0.1:10031,
    

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

  • OVERVIEW

    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

    HOW?

    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:
    127.0.0.1
    10.0.0.0/8
    
    # amazon
    207.171.168.0/24
    207.171.180.0/24
    207.171.187.0/24
    207.171.188.0/24
    207.171.190.0/24
    
    # AOL
    64.12.137.0/24
    64.12.138.0/24
    152.163.225.0/24
    205.188.139.0/24
    205.188.144.0/24
    205.188.156.66
    205.188.157.0/24
    205.188.159.7
    
    # apple
    17.254.6.0/24
    
    # ebay
    66.135.197.0/24
    66.135.209.0/24
    
    # gmail
    64.68.80.0/21
    64.233.160.0/19
    64.233.162.192/28
    64.233.170.192/28
    64.233.182.192/28
    64.233.184.192/28
    66.249.82.192/28
    66.249.92.192/28
    66.249.64.0/19
    66.102.0.0/20
    70.89.39.152/29
    70.90.219.48/29
    70.90.219.72/29
    72.14.192.0/18
    74.125.0.0/16
    209.85.128.0/17
    216.239.32.0/19
    216.239.56.240/28
    
    # postini
    63.146.199.13/32
    63.146.199.14/32
    63.71.11.123/32
    63.71.11.124/32
    64.18.0.0/20
    67.114.133.222/32
    68.123.185.46/32
    74.125.148.0/22
    204.14.232.0/22
    207.126.144.0/20
    208.111.151.5/32
    208.74.204.5/32
    
    # skynet.be
    195.238.2.0/24
    195.238.3.0/24
    
    # yahoo
    64.94.237.0/24
    66.163.160.0/19
    66.196.64.0/18
    66.218.64.0/19
    66.218.66.0/24 
    66.218.67.0/24
    66.218.69.0/24
    69.147.92.0/24
    73.30.0.0/16
    74.6.0.0/16
    206.190.32.0/19
    216.34.77.0/25
    216.136.226.0/24
    
    • Reload PF

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

    #!/bin/sh
    /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):
    all:uatraps:override:nixspam:override:spamtrapped:override:
    
    # University of Alberta greytrap hits.
    # Addresses stay in it for 24 hours from time they misbehave.
    uatraps:\
            :black:\
            :msg="Your address %A has sent mail to a ualberta.ca spamtrap\n\
            within the last 24 hours":\
            :method=http:\
            :file=www.openbsd.org/spamd/traplist.gz:
    
    # Nixspam recent sources list.
    # Mirrored from http://www.heise.de/ix/nixspam
    nixspam:\
            :black:\
            :msg="Your address %A is in the nixspam list\n\
            See http://www.heise.de/ix/nixspam/dnsbl_en/ for details":\
            :method=http:\
            :file=www.openbsd.org/spamd/nixspam.gz:
    
    # Trapped IPs - so we can block them without using greylisting
    spamtrapped:\
            :black:\
            :msg="Your address %A has sent mail to spamtrap on this server\n\
            within the last 24 hours":\
            :method=exec:\
            :file=/usr/local/scripts/get-spamtrapped:
    
    override:\
            :white:\
            :method=file:\
            :file=/usr/local/etc/spamd/local-whitelist.txt:
    
    • 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:
    obspamd_enable="YES"
    obspamd_flags="-5"
    
    • 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:
    #!/usr/local/bin/bash
    
    # rudimentary checking - more complex checking will be done by 
    # the pfctl and spamdb commands
    ADDRESS=${1%%[!0-9.]*}
    if [[ ! ${#ADDRESS} = ${#1} ]]
    then
            echo "Invalid characters in IP address"
            exit 1
    fi
    if [ ! ${ADDRESS} ]
    then
            echo "Usage: $0 <address>"
            exit 1
    fi
    
    
    
    /usr/local/sbin/spamdb -t -a ${ADDRESS}
    if [ "$?" -ne 0 ]
    then
            echo "Failed to add ${ADDRESS} to spamdb"
            exit 1
    fi
    /sbin/pfctl -qt spamd -T add ${ADDRESS}
    if [ "$?" -ne 0 ]
    then
            echo "Failed to add ${ADDRESS} to pf"
            exit 1
    fi
    /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 127.0.0.1
    TRAPPED|127.0.0.1|1253655172
    $ sudo pfctl -qt spamd -T show
       127.0.0.1
    $
    

    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 -> 127.0.0.1 port 8025
    

    And that's it.

    POSSIBLE EXTENSIONS

    • 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.

    From sh-beta
  • 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.

    sh-beta : I had no idea policyd even existed. I'll check it out - thanks!

0 comments:

Post a Comment