Solved pf does not load rules at boot time

I've tried every solution I could find on the forums and elsewhere, but I can't seem to get my firewall rules to load properly at boot. What's scary is I just noticed this. It's somehow related to the way interfaces come up. For example, I have FreeBSD 12.1 running on Vultr and pf is running, but the rules aren't loaded on the interface. Notice the difference of output in running service pf status before/after a reload after a reboot:

Code:
Status: Enabled for 0 days 00:00:31           Debug: Urgent

State Table                          Total             Rate
  current entries                        0              
  searches                             182            5.9/s
  inserts                                0            0.0/s
  removals                               0            0.0/s
Counters
  match                                182            5.9/s
  bad-offset                             0            0.0/s
  fragment                               0            0.0/s
  short                                  0            0.0/s
  normalize                              0            0.0/s
  memory                                 0            0.0/s
  bad-timestamp                          0            0.0/s
  congestion                             0            0.0/s
  ip-option                              0            0.0/s
  proto-cksum                            0            0.0/s
  state-mismatch                         0            0.0/s
  state-insert                           0            0.0/s
  state-limit                            0            0.0/s
  src-limit                              0            0.0/s
  synproxy                               0            0.0/s
  map-failed                             0            0.0/s
Status: Enabled for 0 days 00:00:31           Debug: Urgent

State Table                          Total             Rate
  current entries                        0              
  searches                             182            5.9/s
  inserts                                0            0.0/s
  removals                               0            0.0/s
Counters
  match                                182            5.9/s
  bad-offset                             0            0.0/s
  fragment                               0            0.0/s
  short                                  0            0.0/s
  normalize                              0            0.0/s
  memory                                 0            0.0/s
  bad-timestamp                          0            0.0/s
  congestion                             0            0.0/s
  ip-option                              0            0.0/s
  proto-cksum                            0            0.0/s
  state-mismatch                         0            0.0/s
  state-insert                           0            0.0/s
  state-limit                            0            0.0/s
  src-limit                              0            0.0/s
  synproxy                               0            0.0/s
  map-failed                             0            0.0/s

After

Code:
Status: Enabled for 0 days 00:00:54           Debug: Urgent

Interface Stats for vtnet0            IPv4             IPv6
  Bytes In                           24582              528
  Bytes Out                          38663              528
  Packets In
    Passed                             266                6
    Blocked                              1                0
  Packets Out
    Passed                             311                7
    Blocked                              7                0

State Table                          Total             Rate
  current entries                        1              
  searches                             598           11.1/s
  inserts                                1            0.0/s
  removals                               0            0.0/s
Counters
  match                                469            8.7/s
  bad-offset                             0            0.0/s
  fragment                               0            0.0/s
  short                                  0            0.0/s
  normalize                              0            0.0/s
  memory                                 0            0.0/s
  bad-timestamp                          0            0.0/s
  congestion                             0            0.0/s
  ip-option                              0            0.0/s
  proto-cksum                            0            0.0/s
  state-mismatch                         0            0.0/s
  state-insert                           0            0.0/s
  state-limit                            0            0.0/s
  src-limit                              0            0.0/s
  synproxy                               0            0.0/s
  map-failed                             0            0.0/s

This is just a simple web server with nothing complicated going on. In fact, it's pretty bare at this time. I've looked at these posts:

I don't have domain names being resolved in my pf.conf. Here are my rc.conf and pf.conf files (with IP addresses removed)

rc.conf:

Code:
hostname="example.com"
sshd_enable="YES"
ntpd_enable="YES"
static_routes="linklocal"
ifconfig_vtnet0="DHCP"

ifconfig_vtnet0_ipv6="inet6 accept_rtadv"
ipv6_activate_all_interfaces="YES"
rtsold_enable="YES"
rtsold_flags="-aF"

pf_enable="YES"
pf_rules="/etc/pf.conf"
pflog_enable="YES"
pflog_logfile="/var/log/pflog"

# Caddy webserver
caddy_enable="YES"
caddy_cert_email="admin@example.com"
caddy_config_path="/usr/local/etc/caddy/Caddyfile"
caddy_options="-disable-tls-alpn-challenge"



pf.conf

Code:
## External interface

## Set and drop these IP ranges on public interface ##
martians = "{ 127.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, \
              10.0.0.0/8, 169.254.0.0/16, 192.0.2.0/24, \
              0.0.0.0/8, 240.0.0.0/4 }"

## Set http(80)/https (443) port here ##
webports = "{http, https}"

## enable these services ##
int_tcp_services = "{domain, ntp, www, https, ssh}"
int_udp_services = "{domain, ntp}"

## Skip loop back interface - Skip all PF processing on interface ##
set skip on lo

## Sets the interface for which PF should gather statistics such as bytes in/out and packets passed/blocked ##
set loginterface vtnet0

# Deal with attacks based on incorrect handling of packet fragments
scrub in all

## Set default policy ##
block return in log all
block out all

# Drop all Non-Routable Addresses
block drop in quick on vtnet0 from $martians to any
block drop out quick on vtnet0 from any to $martians

# spoofing protection on all interfaces
block in quick from urpf-failed

## Blocking spoofed packets
antispoof quick for vtnet0
antispoof quick for vtnet0 inet6

# IMCPv6 traffic
pass in quick proto icmp6 all

## Use the following rule to enable ssh for ALL users from any IP address #
pass in inet proto tcp to vtnet0 port ssh

# Allow ping
pass inet proto icmp icmp-type echoreq

# Allow access to webserver
pass proto tcp from any to vtnet0 port $webports

# Allow essential outgoing traffic
pass out quick on vtnet0 proto tcp to any port $int_tcp_services
pass out quick on vtnet0 proto udp to any port $int_udp_services

### IPv6

pass out on vtnet0 inet6 proto icmp6 all icmp6-type echoreq keep state

# ND solicitation out
pass out on vtnet0 inet6 proto icmp6 all icmp6-type {neighbradv, neighbrsol}

# ND advertisement in
pass in on vtnet0 inet6 proto icmp6 all icmp6-type {neighbradv, neighbrsol}

# Router advertisement out
pass out on vtnet0 inet6 proto icmp6 all icmp6-type routeradv

# Router solicitation in
pass in on vtnet0 inet6 proto icmp6 all icmp6-type routersol

# Allow Ping pong in
pass in on vtnet0 inet6 proto icmp6 all icmp6-type echoreq



What's keeping PF firewall rules from being properly applied to my interface at boot time? I also tried modifying my rules to not include vtnet0 anywhere and that didn't seem to work either. Seriously considering trying IPFW if there isn't a solution to this.
 
Usually this is due to using a host/FQDN that can't be resolved until DNS is up, and DNS isn't up in time. However, you aren't doing that in your rules, so it's something else. I assume you can load the rules manually, after boot, without issue, so it's not a syntax problem either.

The only other thing I can think of is a permissions issue. Who is the owner and what are the permissions on /etc/pf.conf?

Edit: Actually, are you sure they're not loaded? In all your examples, it says it's enabled, but check the loaded rules with # pfctl -sr.

A few other things I've noticed:
  • You're using DHCP, but when you use your interface where it expects an IP address, you're not enclosing it in parenthesis (e.g "pass proto tcp from any to vtnet0 port $webports"). You should enclose it there, i.e. as (vtnet0), unless you want to have to manually reload every time the IP would change.
  • Again related to DHCP, you might want to use ifconfig_vtnet0="SYNCDHCP" in your rc.conf, to make sure the interface has an address before pf starts to load. If you do enclose all the instances where it's looking for an address for an interface in parenthesis, it should work without it though.
 
In addition to the `Orum about the DHCP and enclosing the interface name in parenthesis i will add the following:

man pfctl(8)

pfctl -e #Enable the packet filter
pfctl -d #Disable the packet filter
pfctl -sr #show loaded rules
pfctl -vsr #show loaded rules and display the counters for each rule (Evaluations, matched packets, Bytes, States,)

When the packet match a rule which has "quick" option, this rule is considered the last matching rule and all other rules for that packet are skipped.
In your case you have
pass in quick proto icmp6 all
So all inbound icmp6 packet are pass. So there's no need for any other rules for the inbound traffic for icmp6. To verify this observe your rule counters using pfctl -vsr to see how many packets are matched for the other icmp6 rules that you have below " pass in quick proto icmp6 all"
 
The only other thing I can think of is a permissions issue. Who is the owner and what are the permissions on /etc/pf.conf?

Edit: Actually, are you sure they're not loaded? In all your examples, it says it's enabled, but check the loaded rules with # pfctl -sr.

Hey `Orum! The ownership of pf.conf is root:wheel and the permissions are 640. I did do a pfctl -sr at boot, I'm sorry I left that crucial information out. That's actually what led me to discover this issue. The output of pfctl -sr is empty until I do a service pf restart or reload.

As for the parenthesis around vtnet0 in my pf.conf, I tried that and I'd get errors on some lines. Can I not use (vtnet0) in each circumstance? I couldn't understand the pattern. I did however do both the SYNCDHCP as well as (vtnet0). Right now I set it so only 2 rules specify vtnet0 (antispoof rules), however, in most other places I get an error when I dry run my pf.conf file. Such as here:

Code:
# Drop all Non-Routable Addresses
block drop in quick on (vtnet0) from $martians to any
block drop out quick on (vtnet0) from any to $martians

$ sudo pfctl -nf /etc/pf.conf
/etc/pf.conf:29: syntax error
/etc/pf.conf:30: syntax error
/etc/pf.conf:37: rule expands to no valid combination

Line 37 here is:
Code:
antispoof quick for vtnet0 inet6

VladiBG, I admittedly have to go over these firewall rules again at some point. These were more copy/paste before I really knew how to put together a good ruleset, so you will likely find places that might merit a facepalm :) haha. At this point, dropping out vtnet0 from my rules wherever possible works, and the 2 places it remains I was able to close it with parenthesis, plus doing SYNCDHCP, and that has solved my problem overall!
 
Rule of thumb: use (vtnet0) when you want the address of vtnet0, as this format will update to whatever address(es) is/are assigned to the interface — even if it changes after rule loading. So use for “in on vtnet0” with no parens since you are saying in on <interface> but then use “to (vtnet0) port foo” to always reflect the current address(es) of vtnet0.

Search for parentheses in pf.conf(5)to see the full description.
 
Thanks for the help everyone! I found the spot in the pf.conf docs that were mentioned

If you could say what specific instruction that you found in pf.conf(5), and what fixed the pf problem Status: Enabled for 0 days 00:00:31 Debug: Urgent it would be useful. I have the same problem.

Thank you
 
Back
Top