$_ BSDHowTo.ch
How To... Why not..? Scripts Patches RSS logo

How to save and restore tables for pf(4)

Last update: 2020-05-28

Introduction

In this post I show you how you can save and restore pf(4) tables. This is useful to preserve the content of dynamically built tables during reboots of OpenBSD.

Filling tables

The one table I use on all my exposed systems running OpenBSD is called <bruteforce>. Two rules in pf.conf make sure the table is well populated:

# Drop all traffic from bad IPs
block drop in quick on egress from <bruteforce> to any

# Allow SSH but catch the bad IPs
pass in log on egress inet proto tcp from any to egress port ssh \
(max-src-conn 5 max-src-conn-rate 5/10 overload <bruteforce> flush global)

Saving the table

One way to save the table for pf(4) is to run a cronjob as root that does the saving for you. But I prefer to save the table during the shutdown of the system. That is what rc.shutdown is for. My /etc/rc.shutdown looks like this:

pfctl -t bruteforce -T show > /etc/pf.bruteforce
chmod 0600 /etc/pf.bruteforce

The command writes the content of the table <bruteforce> to the file /etc/pf.bruteforce during the shutdown of the system.

Loading the table

In /etc/pf.conf you can add one or more file directives to the table definition:

table <bruteforce> file "/etc/pf.bruteforce"

This makes pf(4) load the contents of the file /etc/pf.bruteforce into the table <bruteforce> whenever /etc/pf.conf gets (re)loaded. pf(4) will add the entries, not replace the existing content of the table.

Maintaining the table

The table keeps growing over time so you should remove no longer needed entries from time to time. The best way to do this is running the following command periodically via cron(8):

# pfctl -t bruteforce -T expire 604800

This will remove all IPs that haven't contacted the system for one week from the the table. I put this in /etc/daily.local. This way I get the number on cleared IPs in the daily mail.

The method above saves the IPs in the table, but it cannot save the date of the last access for each IP. As soon as the system gets rebooted the dates are lost. When pf(4) loads the IPs from the file it sets the field to the current date and time.