Starting `pf` Firewall at System Startup

firewallNetworkpfctlstartup

I have a particular machine that I keep buttoned down very tight. Only a few specific ports/protocols are allowed. I have a pretty basic set of rules that work very well.

While working on a different problem, I realized I could add a few more rules and button things down even tighter by specifying specific ports on specific network interfaces. So for example, on one network interface, only access to 80 & 443 are allowed, no mail or vpn access to that interface.

So here is what one of the old rules looked like:

pass in quick proto tcp from any to any port { 80 443 } keep state

And this is what one of the new rules looks like:

pass in quick proto tcp from any to en1 port { 80 443 } keep state

The difference is subtle but I'm specifying a specific network interface, not just in to 'any'. This particular server has three connections to the internet.

The problem is… pf will no longer start up right at boot with these new rules. When I specify the interface, my pf startup script fails to start pf. And I have to do it manually through the terminal.

At first glance, the problem seems like my script is probably just trying to use network interfaces that aren't up yet. But I'm already waiting for them to come up. Here is my pf startup script, it has been working perfectly for years, until this rule change.

#!/bin/bash
ipconfig waitall
/sbin/pfctl -e -f /etc/pf.conf

With the new rules, nothing gets loaded at system startup when this runs. But if I run the same pfctl command once the system is up, it loads the rules and starts the firewall just fine.

I modified my script to save multiple copies of ifconfig so I could see how the status of the two interfaces en0 and en1 progress. Before the ipconfig waitall command, they are there with no IPs. After the command, they still have no ips and they show up as 'inactive'. Over the next few seconds, the various IPs start to load up. But the two interfaces show up the whole time.

So the easy patch solution would be to just run ipconfig waitall followed by sleep 6 and call it a day. But I'd rather learn exactly what is causing the hangup so I can wait for that exact 'thing' to be ready, instead of leaving the firewall completely open for 6 seconds. It is a high traffic server and gets lots of hack/ddos attempts so every second may count.

Best Answer

There is a small error within your added rule:

pass in quick proto tcp from any to en1 port { 80 443 } keep state

the fields after the key from and to shoud be IP address and port specification, but not an interface name.

To apply your rule explicitly on incoming traffic on interface en1, you should write like this, with macro so as to be able to maintain your PF rules very quickly:

ext_if = "en1"

pass in quick on $ext_if proto tcp from any to any port { 80 443 } keep state

If you test the loading of your pf.conf interactively, pfctl should warn you about a syntax error. As a positive side effect this rule will be independant of the IP address attributed to en1, thus you won't have any kind os synchronization do wait.

Related Question