rss logo

How to Block IPs with a Blacklist Using PF Tables on OpenBSD

OpenBSD Logo

This How-To explains how to use OpenBSD Packet Filter (PF) to block IP addresses and CIDR ranges loaded from an external blocklist. The result is similar in spirit to PeerBlock on Windows: addresses listed in a file are loaded into a PF table and blocked by firewall rules.

Configuration

  • OS: OpenBSD 7.8
  • Firewall: Packet Filter (PF)
  • Example protected host: 192.168.1.10
  • Blocklist file: /etc/blocklist

💡 Note: The commands below must be run as root. Adjust the interface macros, IP addresses, and ports to match your own network.

Commands

Download a black list

You can download blocklists from iBlocklist. For example, download the PrimaryThreats list here. Once downloaded, extract and format the file so it can be used with OpenBSD Packet Filter (PF).

  • Get the list:
root# wget "URLtomyremotelist" -O list.zip
  • Unzip the list:
root# unzip file.zip
  • Format the list:
root# cut -d ":" -f2 list.txt | grep -E "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" > blocklist.tmp
root# sed -i 's/-/:/' blocklist.tmp
root# for i in $(cat blocklist.tmp); do echo "$i"; ipcalc -r "$i"| grep "/" >> blocklist; done
  • Copy the blocklist file to /etc/blocklist:
root# cp blocklist /etc/blocklist

Packet Filter configuration

Add a table and the matching rules to /etc/pf.conf. This example blocks traffic both from the blocklisted addresses to the protected host and from the protected host to the blocklisted addresses.

wan = "em0"
lan = "em1"
protected_host = "192.168.1.10"

# Increase the global number of addresses that can be stored in PF tables.
set limit table-entries 1000000

# Load the external blocklist into a persistent table.
table <blocklist> persist file "/etc/blocklist"

pass out on $wan
pass in quick on $lan inet proto tcp from $protected_host to any port { 80 443 }

block in  quick log on $wan from <blocklist> to $protected_host
block out quick log on $wan from $protected_host to <blocklist>

Validate and reload PF

  • Check the syntax before loading the ruleset:
root# pfctl -nf /etc/pf.conf
  • Reload PF if the syntax check succeeds:
root# pfctl -f /etc/pf.conf
  • Inspect the loaded table:
root# pfctl -t blocklist -T show | more

Update the blocklist without reloading all rules

After generating a new /etc/blocklist, you can replace only the table contents:

root# pfctl -t blocklist -T replace -f /etc/blocklist

Logging

The example rules use the log keyword, allowing you to monitor matching packets on the pflog0 interface using tcpdump:

  • -n: Do not resolve IP addresses to hostnames
  • -e: Display link-layer headers
  • -ttt: Show timestamps with date and time
root# tcpdump -n -e -ttt -i pflog0
Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.

Contact :

contact mail address