Putting the Spamhaus DROP list in FreeBSD’s ipfw

Are you aware of the Spamhaus DROP list? According to the ladies and gentlemen of Spamhaus:

DROP (Don’t Route Or Peer) is an advisory “drop all traffic” list, consisting of stolen ‘zombie’ netblocks and netblocks controlled entirely by professional spammers. DROP is a tiny sub-set of the SBL designed for use by firewalls and routing equipment.

So DROP is simply a short-ish list of CIDR numbers and Spamhaus SBL references, and that we can definitely use in our FreeBSD ipfw rules. There’s a couple of perl scripts in the DROP FAQ, but none of them is suitable for generating ipfw rules, so I went ahead and made my own script. Yup, a good old-fashioned shell script. I don’t speak perl (not very well, anyway), and a shell script can be made equally no-nonsense and portable in my opinion. I operate several internet-facing FreeBSD servers, and they all use this script to generate ipfw rules.

You obviously need ipfw set up and operating on your FreeBSD server or workstation. How you deploy ipfw is up to you, but all “my” servers are already behind other types of firewalls, which means the open mode ipfw is purely a second line of defense set up to block unwanted traffic on common, open ports.

The basic idea behind the script is this:

1. Download the DROP list once a day
2. Compare today’s version to yesterday’s version. Exit if no change
3. Strip out all unneccessary info and generate ipfw syntax around the CIDR blocks
4. Write the converted DROP list to a file
5. Include the file in /etc/rc.firewall (or whatever script your ipfw rules are in)
6. Flush the ruleset and load it again

Here’s the script (comments and all). I try to not be a smartass while scripting, so please excuse the lack of finesse and obscure commands. If you want to fetch the script directly, use this link.

#!/bin/sh
# Generates an IPFW-format deny list based on the Spamhaus DROP list
# http://www.spamhaus.org/drop/
#
# Example ipfw rule:
# ipfw add [nnnn] deny log tcp from 63.148.99.0/24 to 192.168.1.1
#
# Released into the public domain by Break Left Outings
# 20070724/twa

############################################
# Change these to suit your installation
############################################
# The starting ipfw rule number
I=50000
# The interface you want protected.
IFACE=192.168.1.1
# Rules will be written to this file. Remember to include it near the end
# in your "real" ipfw rules script with ". /filename"
IPFWSCRIPT=/usr/local/etc/ipfw.DROPlist
REALIPFWRULES=/etc/rc.firewall
############################################

# No need to change anything below here!
# Vars
DROPLIST=http://www.spamhaus.org/drop/drop.lasso
LOCALNEWFILE=/tmp/droplist.new
LOCALOLDFILE=/tmp/droplist.old
TMP=`mktemp -t tmpdroplist`
TMP1=`mktemp -t tmpdroplist`
TMP2=`mktemp -t tmpdroplist`

# fetch the list
fetch -q -o $LOCALNEWFILE $DROPLIST
if [ $? -ne 0 ] ; then
        echo "`date`: makedroplist failed to fetch $DROPLIST. Aborting"
        exit 0
fi

#
# Compare the old and new file
# No need to generate new rules if nothing have changed
#

# if old DROP list have disappeared, create a dummy
if [ ! -f $LOCALOLDFILE ] ; then
        echo "Spamhaus DROP List" > $LOCALOLDFILE
fi
# Remove first line from both files to ensure a real comparison
# Spamhaus seems to change the first line every day, even if there are no changes
grep -v "Spamhaus DROP List" $LOCALOLDFILE >$TMP
cp $TMP $LOCALOLDFILE
grep -v "Spamhaus DROP List" $LOCALNEWFILE >$TMP
cp $TMP $LOCALNEWFILE

#
# Use diff to see if they are different. We don't need to know WHAT has been changed
#
/usr/bin/diff -q $LOCALNEWFILE $LOCALOLDFILE >/dev/null
if [ $? -eq 0 ] ; then
        # Be quiet when nothing has changed. Uncomment next line if you need to know
        #echo "`date`: makedroplist: DROP list not changed. Exiting"
        rm -f $TMP $TMP1 $TMP2 $LOCALNEWFILE
        exit 0
fi

# Clean up the file. We only want the CIDR blocks for ipfw
#echo "cleaning up"
cat -s $LOCALNEWFILE | cut -f 1 -d ";" | tr -d " " >$TMP
# Get rid of blank lines in file
sed '/./,$!d' $TMP >$TMP1
sed -e :a -e '/^n*$/{$d;N;};/n$/ba' $TMP1 >$TMP2

# Write a comment header etc, to the script
echo "#!/bin/sh" >$IPFWSCRIPT
echo "# The Spamhaus DROP list in IPFW format" >>$IPFWSCRIPT
echo "# See http://www.spamhaus.org/drop/ for details" >>$IPFWSCRIPT
echo "# This file was generated on `hostname`, `date`" >>$IPFWSCRIPT

# Generate the ipfw rules
while read line ;do
   echo "/sbin/ipfw -q add $I deny log all from $line to $IFACE" >>$IPFWSCRIPT
   I=`expr $I + 1`
done < $TMP2

# Load the rules
sh $REALIPFWRULES

# Clean up and exit
rm -f $TMP $TMP1 $TMP2 $LOCALOLDFILE
mv -f $LOCALNEWFILE $LOCALOLDFILE
echo "`date`: ipfw rules successfully generated from $DROPLIST"
# End of script

To have the DROP rules included in your regular ipfw rules, use the following lines near the end of your regular rule set (e.g. /etc/rc.firewall):

        # Load the generated rules from the Spamhaus DROP list
        . /usr/local/etc/ipfw.DROPlist

Basically, that’s it. Put in crontab and run it once a day or every other day. If a new list is loaded, you’ll get a mail telling you so. If you don’t want to be notified, comment out the final echo statement. The DROP list changes no more than once a week in my experience, so there’s no need to hammer Spamhaus with hourly cronjobs!

Please use the comments if you have any questions or suggestions.

4 Responses to “Putting the Spamhaus DROP list in FreeBSD’s ipfw”

  1. ggl Says:

    I have modified your script to generate rules for the OpenBSD PF.
    http://blog.devnull.ro/node/138

  2. Amza Marian Says:

    too more sed / awk ?

    fetch http://www.spamhaus.org/drop/drop.lasso
    grep SBL drop.lasso | awk -F ” ” ‘{print $1}’ |while read m;do ${fwcmd} add reject ip from `echo $m` to me;done

    for cleaning file try:
    curl -s http://www.spamhaus.org/drop/drop.lasso | grep SBL | awk -F ” ” ‘{print $1}’
    or
    curl -s http://www.spamhaus.org/drop/drop.lasso | grep SBL | awk -F ” ” ‘{print $1}’ >> clean-drop-file

  3. Drop din firewall la adresele listate la Spamhaus | Read The Fucking Manual Says:

    […] Sugerør […]

  4. neant Says:

    Another script for *BSD with pf, using a table: http://www.neant.ro/2012/07/script-to-add-spamhaus-drop-list-to-pf/ . Written on OpenBSD, should work just fine on any BSD.

Leave a Reply