Abstract
On a recent-ish thread on a mailing list, I found out about Spamhaus’s Don’t Route or Peer (DROP) list; I wanted to implement it on my VPS’s PF setup and also started searching for other similar lists online to block unwanted traffic onto my machine.
What is the DROP list
According to Spamhaus, the drop list can be described as:
Don’t Route Or Peer (DROP) lists the worst of the worst IP traffic. It is an advisory “drop all traffic”, containing IP ranges which are so dangerous to internet users that Spamhaus provides access to anyone who wants to add this layer of protection, free of charge.
While the majority of users will be running hosts on infrastructure that should already implement this list, there isn’t really a downside to also implementing this on your host(s). There is the obvious benefit of blocking potentially malicious addresses if your hosting provider doesn’t do it; it also builds good habits for those that aspire to run large networks or even public infrastructure one day.
Ultimately, the main takeaway from this blog post isn’t how to implement the DROP list, though that is included below. The important information in this one is the knowledge of the DROP list’s existence, as well as why it is important to implement on your network.
Blocking with PF
Because I am running a BSD on my VPS, PF is a fantastic choice for blocking traffic such as this. The method that I chose to use, and would recommend, is setting up a new pf table for this list of IPs then block everything on that table. To keep things more manageable, I would also recommend keeping each additional block list setup on a separate table, that way each of them is easier to modify as needed. This process is fairly simple with some lines added to your PF config, however, it is worth noting that the lines have to be added in their appropriate places. PF will refuse to load if the rules are stated out of order; this is explained in the pf.conf man page. The statement types should be ordered as:
- Options
- Ethernet filtering
- Traffic Normalization
- Queueing
- Translation
- Packet Filtering
With comments, Macros, and Tables being able to go anywhere in the configuration file, though the macros and tables need to be declared before they are used within the file. Best practices are usually to have them grouped in one section.
# Add the anchor to allow pfctl to operate on it:
anchor "spamhaus/*"
# skipping some config...
# Then create a table to easily (and more efficiently) create rules for the addresses
# the persist option allows for keeping the table even if no rules apply to it
table <spamhaus> persist
# Skipping more config...
# Finally blocking the addresses on the newly created table
# Note: $ext_if is a macro setup for the interface name on the machine,
# this might be something like em0 or vtnet0 or whatever other interface
# should be blocking traffic
block quick from <spamhaus> to $ext_if
block quick from $ext_if to <spamhaus>
Notice that we are blocking not only incoming, but also outgoing traffic to the Spamhaus table. While this is a minor detail, it is important to remember the initial Spamhaus list is for addresses that should not be interacted with at all, thus we drop any and all traffic to it. Next, we simply reload the table:
# Run the service command
service pf reload
Ideally, we would be able to connect to a machine on another remote
network, then add that network to the spamhaus pf table,
then try to connect. Short of doing that, the best way to confirm that
it is working is by monitoring the connection, but before we can do
that, we will need to add some addresses.
# To view connection info live
tcpdump -n -e -ttt -i pflog0
# To view the log file...assuming log is at /var/log/pflog
tcpdump -n -e -ttt -r /var/log/pflog
Scripting
This is a little shell script that I wrote to automatically download
the DROP list and add the IP ranges to the PF table. The script could
probably be more efficient, specifically with the jq
command, however, that is the one Spamhaus provided so I just put it
directly in my script.
#!/bin/sh
# This script is for adding Spamhaus's "Don't Route or Peer (DROP)" list to PF
# https://www.spamhaus.org/blocklists/do-not-route-or-peer/
DROP_FILE=/tmp/drop.txt
JSON_FILE=/tmp/drop_v4.json
BLOCK_TABLE=spamhaus
# Download JSON
fetch https://www.spamhaus.org/drop/drop_v4.json -o ${JSON_FILE}
# Convert from JSON to TXT, found here:
# https://www.spamhaus.org/faqs/do-not-route-or-peer-drop/#how-do-i-convert-the-json-drop-files-to-txt
tail -1 ${JSON_FILE} | jq -r '"; Spamhaus DROP List \(.timestamp | strftime("%Y/%m/%d")) - \(.copyright)\n; https://www.spamhaus.org/drop/drop_v4.json\n; Last-Modified: \(.timestamp | strftime("%a, %d %b %Y %H:%M:%S UTC"))\n; Expires: \(.timestamp+93600 | strftime("%a, %^C%b %Y %H:%M:%S UTC"))"' > ${DROP_FILE}
jq -r 'select(.type == null) | (.cidr) + " ; " + (.sblid)' ${JSON_FILE} >> ${DROP_FILE}
echo "; EOF" >> ${DROP_FILE}
# First we remove all entries from the table
pfctl -t ${BLOCK_TABLE} -T flush
# Then we add the current entries to that table
for i in $(awk '/^[0-9]/{ print $1 }' ${DROP_FILE}); do
pfctl -t ${BLOCK_TABLE} -T add ${i}
done
Now, we just automate running this script. A very important note on this automation, from Spamhaus’ page on the DROP list:
Please DO NOT auto-fetch the DROP list more than once per hour!
The DROP list changes quite slowly. There is no need to update cached data more than once per hour, in fact once per day is more than enough in most cases.
Automated downloads must be at least one hour apart.
NOTE: Excessive downloads may result in your IP being firewalled from the Spamhaus website.
I am fetching a fresh list once a day; if for some reason that is not good enough for you, please heed their warning on only fetching once per hour. This is a free public service that they are maintaining, we would not be very good netizens if we begin to abuse public services like this.
The traditional method for automating something like this is using
crontab -e as root, then adding it to the crontab by hand.
This method still works, and is how I implemented mine:
# Updating DROP list
0 6 * * * /usr/local/bin/spamhaus_update.sh
The above will run the spamhaus_update.sh script (the
example one provided) run ever day at 0600.
Other Block Lists
While the DROP list is a great starting point for IP blocking, it is
far from the only list of its kind. This
site goes over several lists at a variety of price points and false
positive rates. While it is likely not strictly necessary, especially
for a small server like mine, it is again building good habits. It will
also add some amount of protection that may otherwise have to be caught
by another system such as blocklistd or
fail2ban.
Resources
- https://www.spamhaus.org/faqs/do-not-route-or-peer-drop/#how-do-i-convert-the-json-drop-files-to-txt
- https://www.openbsdhandbook.com/pf/cheat_sheet/
- https://threatlistpro.com/compare/ip-blocklist-stack-2026