PF pf reloading

New to FreeBSD and PF. How do I reload my pf rules after making changes to the anchor file or pf.conf?
I usually go pfctl -a my.anchor -Fr or simply pfctl -Fr (I guess -a is for when you have more than one anchor and you only want to flush one of them, correct? And pfctl -Fr would flush all anchors referenced in the pf.conf file?):

Code:
-F modifier
             Flush the filter parameters specified by modifier (may be
             abbreviated) [...]
-F rules      Flush the filter rules.

I've also tried pfctl -f /etc/pf.conf and it worked as well:
Code:
-f file
             Load the rules contained in file.  This file may contain macros,
             tables, options, and normalization, queueing, translation, and
             filtering rules.  With the exception of macros and tables, the
             statements must appear in that order.

And also tried service pf reload and it worked as well.
Is there any best practice for production environments? What are the implications of each option?
 
And also tried service pf reload and it worked as well.
It does a syntax check with -n then a pfctl -f /etc/pf.conf.

Code:
pf_reload()
{
        echo "Reloading pf rules."
        pf_resync
}

pf_resync()
{
        $pf_program -n -f "$pf_rules" $pf_flags || return 1
        $pf_program -f "$pf_rules" $pf_flags
}

I typically reload with pfctl -f /etc/pf.conf.

If you do remote work you might want to take a different approach though, as you might lock yourself out if you mess up the rules.

pfctl -f /etc/pf.conf.new && sleep 60 && pfctl -f /etc/pf.conf
If you accidentally lock yourself out wait 60 seconds and the old ruleset will get loaded again.
 
I prefer the service pf reload approach. Internally this checks that there are no syntax errors in /etc/pf.conf (or the pf_rules path if set in rc.conf) and then loads ( pfctl -f $pf_rules) it.

Note that established TCP connections are not disconnected when reloading rules. By default, pf's TCP rules are only evaluated during the initial SYN packet processing of the three-way handshake. So even if your new rules would block a connection, if one was already in the established state when the new rules are loaded, it will continue to function. Be sure to verify that needed connections are able to be newly established after updating rules.
 
Fantastic. I love this! I will start using it from now on an alias.
I'm using a similar yet slightly different approach: On remote hosts that I absolutely cannot loose access to (i.e. no IPMI or other means of OOB access) I have a minimalistic pf.conf.failsafe which basically only allows ssh from a limited set of IPs and 'pf reload' is an alias to pfctl -f /etc/pf.conf && sleep 60 && pfctl -f /etc/pf.conf.failsafe.
 
Note that established TCP connections are not disconnected when reloading rules. By default, pf's TCP rules are only evaluated during the initial SYN packet processing of the three-way handshake. So even if your new rules would block a connection, if one was already in the established state when the new rules are loaded, it will continue to function. Be sure to verify that needed connections are able to be newly established after updating rules.
Yep I've been bitten by this more than once, now I like to flush everything(when it's possible) just to be sure the new rules are taken into account: pfctl -Fa -f /etc/pf.conf

I'm using a similar yet slightly different approach: On remote hosts that I absolutely cannot loose access to (i.e. no IPMI or other means of OOB access) I have a minimalistic pf.conf.failsafe which basically only allows ssh from a limited set of IPs and 'pf reload' is an alias to pfctl -f /etc/pf.conf && sleep 60 && pfctl -f /etc/pf.conf.failsafe.
That's smart! I never thought of that, I like the idea of the fallback set of rules. Thanks for the idea :)
 
now I like to flush everything(when it's possible) just to be sure the new rules are taken into account
Which will also kill your current SSH session (you also flush the state that allowed the connection), so careful with this if you do remote work.
 
That's smart! I never thought of that, I like the idea of the fallback set of rules. Thanks for the idea :)
I also started out using a 'pf.conf.bak' and at some point even had an alias ("vipf") in place that automagically copied the current pf.conf to pf.conf.bak. This works perfectly fine until you introduced an error earlier and it then propagates to that backup file...
To protect me from myself, pf.conf.failsafe is an immutable file ( chflags uchg).
 
I also started out using a 'pf.conf.bak' and at some point even had an alias ("vipf") in place that automagically copied the current pf.conf to pf.conf.bak. This works perfectly fine until you introduced an error earlier and it then propagates to that backup file...
I had the same kind of experience too, for that reason I prefer your "failsafe" solution.

To protect me from myself, pf.conf.failsafe is an immutable file ( chflags uchg).
Good point.
You know things get tough when you can't even trust yourself 😄
 
Which will also kill your current SSH session (you also flush the state that allowed the connection), so careful with this if you do remote work.
Exactly.

My workflow:
  • Make changes to pf.conf
  • Reload pf
  • Test that I can make new required connections. (Be very aware of what you are testing if you use controlmaster on SSH.)
  • Profit. (?)
I also have a custom failsafe service (rc.d/failsafe) that uses boot environments and zfs properties. If I don’t log in and “commit” (by updating the failsafe:be user property) it loads the “known good” boot environment after five minutes and reboots. There is a similar “bootonce” option to bectl, I believe, but it won’t automatically reboot after a timeout, so it is less useful as a failsafe for a remote system. As a boot environment, it’s good for testing more major overhauls or updates.

If that’s of interest, perhaps I’ll polish it up and post it somewhere.
 
One other PF gotcha since you’re new to it: don’t use host names, only IP addresses. A config with host names will happily load on an up-and-running system, but will fail at reboot before networking services (local_unbound, for example) are alive.
 
Back
Top