Script to add spamhaus’ DROP list to pf

SpamhausDROP (Don’t Route Or Peer) and EDROP lists are sets of IPs controlled by bad people. Basically IPs that are very likely going to cause trouble so might as well block them completely. Details.

The following script will load a pf table with these networks. It will get drop.txt and, if uncommented, edrop.txt, cut the comments, compare to the existing list, if different flush the table and add the new blocks, then kill all existing connections to bad IPs.

Should probably be run once per day, do not run it more often than once per hour or you’ll be banned. OpenBSD doesn’t have fetch, so I’m using ftp, but ftp works fine on FreeBSD too, so no need to change that.

pf.conf should contain lines similar to these:

table <spamhaus_drop> persist
block drop log quick from <spamhaus_drop> to any
block drop log quick from any to <spamhaus_drop>

spamdrop.sh:

#!/bin/sh

#####
# it will fetch drop.txt and edrop.txt from spamhaus.org and add the IPs
# to pf's table $TABLE
# 
# in pf.conf:
# table <spamhaus_drop> persist
# block drop log quick from <spamhaus_drop> to any
# block drop log quick from any to <spamhaus_drop>
#####

WORKDIR="/tmp" # where to put temporary files
TABLE="spamhaus_drop" # name of the table in pf.conf
STDOUT="/dev/null" # /dev/null to suppress messages, /dev/stdout to see them


if [[ ! -d $WORKDIR ]]; then
    echo "$WORKDIR doesn't exist, abandoning"
    exit 1
fi
# check if table is really used
pfctl -s rules | grep $TABLE > $STDOUT
if [[ $? > 0 ]]; then
    echo "WARNINIG: seems like $TABLE has no rules associated with it in pf.conf"
fi

cd $WORKDIR

# get drop.txt, prepare drop.work for pfctl
ftp http://www.spamhaus.org/drop/drop.txt > $STDOUT
if [[ $? != 0 ]]; then
    echo "ERROR: failed fetching drop.txt, abandoning"
    exit 1
fi
sed 's/\;.*$// ; s/\ // ; /^$/d' drop.txt > drop.work

# Get edrop.txt too, uncomment this section if you want it
#ftp http://www.spamhaus.org/drop/edrop.txt > $STDOUT
#if [[ $? != 0 ]]; then
#    echo "ERROR: failed fetching edrop.txt, abandoning"
#    rm drop.txt
#    exit 1
#fi
#sed 's/\;.*$// ; s/\ // ; /^$/d' edrop.txt >> drop.work

# final file will need to be sorted by IP for comparing it with pf's table
sort -t . -k 1,1n -k 2,2n -k 3,3n -k 4,4n drop.work > drop.temp
mv drop.temp drop.work

# compare drop.work with the table, replace table if different
pfctl -t $TABLE -T show | sed s/\ *// > spamhaus.work
diff drop.work spamhaus.work > /dev/null
if [[ $? = 0 ]]; then
    # uncoment if you want cron reports
    #echo "pf's $TABLE table didn't change since last update"
    rm -f drop.txt edrop.txt drop.work spamhaus.work # cleanup
    exit 0
fi

# flush the table
pfctl -t $TABLE -T flush > $STDOUT
# load the new drop list
pfctl -f drop.work -t $TABLE -T add > $STDOUT
# kill all existing connections to IPs in the list
DEATHLIST=`pfctl -t $TABLE -T show`
for i in $DEATHLIST; do
    pfctl -k $i 2>&1 | grep -v "killed 0 states from 1 sources and 0 destinations" > $STDOUT
done

# uncomment if you want cron reports
#echo "pf's $TABLE table updated"
rm -f drop.txt edrop.txt drop.work spamhaus.work # cleanup
exit 0

The way it is, it will NOT fetch edrop.txt and it will be silent if successful. Uncomment the respective lines if you want this behaviour changed.

To add it to OpenBSD’s daily run create or edit daily.local to something like:

#!/bin/sh

/etc/netconf/spamdrop.sh

Mind the permissions on daily.local, it should be o-rwx.