PF pf rule not being used

Howdy,

I recently started using pf (FreeBSD 13.0) as my home FW with very basic rules:
Code:
lan="bge0"
wan="bge1"
set loginterface $wan
set optimization normal
set block-policy drop
set skip on lo0
scrub on $wan all
nat on $wan from $lan:network to any -> ($wan)
block drop log all
pass in on $lan from $lan:network to any keep state
pass out on $wan from $lan:network to any keep state
pass out on $wan from $wan:network to any keep state
Everything seems to be working fine and as expected but when I was getting familiar with "pfctl -vsr", two things caught my attention:
Code:
@0 scrub on bge1 all fragment reassemble
  [ Evaluations: 31084110  Packets: 15531058  Bytes: 4728288470  States: 0     ]
  [ Inserted: uid 0 pid 941 State Creations: 0     ]
@0 block drop log all
  [ Evaluations: 120197    Packets: 4390      Bytes: 312763      States: 0     ]
  [ Inserted: uid 0 pid 941 State Creations: 0     ]
@1 pass out on bge1 inet from 192.168.128.0/24 to any flags S/SA keep state
  [ Evaluations: 120197    Packets: 0         Bytes: 0           States: 0     ]
  [ Inserted: uid 0 pid 941 State Creations: 0     ]
@2 pass out on bge1 inet from 172.60.28.0/18 to any flags S/SA keep state
  [ Evaluations: 58165     Packets: 10747127  Bytes: 11023198233  States: 113   ]
  [ Inserted: uid 0 pid 941 State Creations: 58165 ]
@3 pass in on bge0 inet from 192.168.128.0/24 to any flags S/SA keep state
  [ Evaluations: 120197    Packets: 10761154  Bytes: 11025911872  States: 116   ]
  [ Inserted: uid 0 pid 941 State Creations: 57642 ]
The first thing I noticed is that the order in which pf loaded the rules in memory is not the same as the order in the "pf.conf" file. I was wondering if this was expected behavior as I could not find a reference on this. Any pointers on this would be highly appreciated. Secondly and more importantly: I was very puzzled by rule @1 having no packets "processed" or states. From the "man pf.conf" Translations section (yes, I read the manual pages ;) )
Code:
Since translation occurs before filtering the filter engine will see packets as they look after any addresses and ports have been translated.
When I initially configured the rules, @1-3 made perfect sense to me but I forgot the fact that once the traffic enters and is allowed by rule @3 on the internal interface (lan:bge0) it would be the immediately NATed so rule @1 would never be used. is it fair to say then that rule @1 is not needed and I can take it out? I can think of a condition that would validate it but I am just beginning to know/understand pf.

Thank you for your attention!
Ivan
 
Your syntax appears to be off slightly:

Here's the /etc/pf.conf ruleset I use on my laptops:
Code:
### Macro name for external interface
ext_if = "em0"
netbios_tcp = "{ 22, 23, 25, 80, 110, 111, 123, 512, 513, 514, 515, 6000, 6010 }"
netbios_udp = "{ 123, 512, 513, 514, 515, 5353, 6000, 6010 }"

### Reassemble fragmented packets
scrub in on $ext_if all fragment reassemble

### Default deny everything
block log all

### Pass loopback
set skip on lo0

### Block spooks
antispoof for lo0
antispoof for $ext_if inet
block in from no-route to any
block in from urpf-failed to any
block in quick on $ext_if from any to 255.255.255.255
block in quick log on $ext_if from { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 255.255.255.255/32 } to any

### Block all IPv6
block in quick inet6 all
block out quick inet6 all

### Block to and from port 0
block quick proto { tcp, udp } from any port = 0 to any
block quick proto { tcp, udp } from any to any port = 0

### Block specific ports
block in quick log on $ext_if proto tcp from any to any port $netbios_tcp
block in quick log on $ext_if proto udp from any to any port $netbios_udp

### Keep and modulate state of outbound tcp, udp and icmp traffic
pass out on $ext_if proto { tcp, udp, icmp } from any to any modulate state

And here it is at work:
Code:
root@bakemono:/ # pfctl -s all
FILTER RULES:
scrub in on em0 all fragment reassemble
block drop log all
block drop in on ! lo0 inet from 127.0.0.0/8 to any
block drop in on ! em0 inet from 192.168.1.0/24 to any
block drop in inet from 192.168.1.24 to any
block drop in on ! lo0 inet6 from ::1 to any
block drop in from no-route to any
block drop in from urpf-failed to any
block drop in quick on em0 inet from any to 255.255.255.255
block drop in log quick on em0 inet from 10.0.0.0/8 to any
block drop in log quick on em0 inet from 172.16.0.0/12 to any
block drop in log quick on em0 inet from 192.168.0.0/16 to any
block drop in log quick on em0 inet from 255.255.255.255 to any
block drop in quick inet6 all
block drop out quick inet6 all
block drop quick proto tcp from any port = 0 to any
block drop quick proto tcp from any to any port = 0
block drop quick proto udp from any port = 0 to any
block drop quick proto udp from any to any port = 0
block drop in log quick on em0 proto tcp from any to any port = ssh
block drop in log quick on em0 proto tcp from any to any port = telnet
block drop in log quick on em0 proto tcp from any to any port = smtp
block drop in log quick on em0 proto tcp from any to any port = http
block drop in log quick on em0 proto tcp from any to any port = pop3
block drop in log quick on em0 proto tcp from any to any port = sunrpc
block drop in log quick on em0 proto tcp from any to any port = ntp
block drop in log quick on em0 proto tcp from any to any port = exec
block drop in log quick on em0 proto tcp from any to any port = login
block drop in log quick on em0 proto tcp from any to any port = shell
block drop in log quick on em0 proto tcp from any to any port = printer
block drop in log quick on em0 proto tcp from any to any port = x11
block drop in log quick on em0 proto tcp from any to any port = x11-ssh
block drop in log quick on em0 proto udp from any to any port = ntp
block drop in log quick on em0 proto udp from any to any port = biff
block drop in log quick on em0 proto udp from any to any port = who
block drop in log quick on em0 proto udp from any to any port = syslog
block drop in log quick on em0 proto udp from any to any port = printer
block drop in log quick on em0 proto udp from any to any port = mdns
block drop in log quick on em0 proto udp from any to any port = x11
block drop in log quick on em0 proto udp from any to any port = x11-ssh
pass out on em0 proto tcp all flags S/SA modulate state
pass out on em0 proto udp all keep state
pass out on em0 proto icmp all keep state

STATES:
all tcp 192.168.1.24:29663 -> 52.40.130.105:443       ESTABLISHED:ESTABLISHED
all tcp 192.168.1.24:56712 -> 204.109.59.195:443       ESTABLISHED:FIN_WAIT_2
all tcp 192.168.1.24:62504 -> 204.109.59.195:443       FIN_WAIT_2:FIN_WAIT_2
all tcp 192.168.1.24:43801 -> 204.109.59.195:443       TIME_WAIT:TIME_WAIT
all udp 192.168.1.24:15863 -> 192.168.1.1:53       MULTIPLE:SINGLE
all udp 192.168.1.24:22209 -> 192.168.1.1:53       MULTIPLE:SINGLE
all tcp 192.168.1.24:59889 -> 204.109.59.195:443       FIN_WAIT_2:FIN_WAIT_2
all tcp 192.168.1.24:63440 -> 204.109.59.195:443       FIN_WAIT_2:FIN_WAIT_2
all tcp 192.168.1.24:47673 -> 204.109.59.195:443       TIME_WAIT:TIME_WAIT
all tcp 192.168.1.24:53044 -> 204.109.59.195:443       TIME_WAIT:TIME_WAIT
all tcp 192.168.1.24:58676 -> 204.109.59.195:443       TIME_WAIT:TIME_WAIT

INFO:
Status: Enabled for 0 days 01:17:16           Debug: Urgent

State Table                          Total             Rate
  current entries                       11              
  searches                           10634            2.3/s
  inserts                              503            0.1/s
  removals                             492            0.1/s
Counters
  match                                703            0.2/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

TIMEOUTS:
tcp.first                   120s
tcp.opening                  30s
tcp.established           86400s
tcp.closing                 900s
tcp.finwait                  45s
tcp.closed                   90s
tcp.tsdiff                   30s
udp.first                    60s
udp.single                   30s
udp.multiple                 60s
icmp.first                   20s
icmp.error                   10s
other.first                  60s
other.single                 30s
other.multiple               60s
frag                         30s
interval                     10s
adaptive.start            60000 states
adaptive.end             120000 states
src.track                     0s

LIMITS:
states        hard limit   100000
src-nodes     hard limit    10000
frags         hard limit     5000
table-entries hard limit   200000

OS FINGERPRINTS:
762 fingerprints loaded
root@bakemono:/ #
 
Your syntax appears to be off slightly:
Hey Tri, thank you for your input. Do you mean that the syntax of my filter rules defined on my pf.conf are off? Your rules are very specific on what they allow or block and I mine are more generic and more around where the traffic is coming from and to, letting valid IPv4 traffic through. As far as how they look and are reported with "pfctl -s all", I would say they look similar. Did I miss your point? Thank you again. Ivan
 
Do you mean that the syntax of my filter rules defined on my pf.conf are off?
That pretty well covers it.

You have two drop rules:
Code:
set block-policy drop
set skip on lo0
scrub on $wan all
nat on $wan from $lan:network to any -> ($wan)
block drop log all

Look at the order of my rules in comparison to yours.

The wording of my rules as compared to yours.

I have rules that do not appear in yours.

You have a NAT rule that does not appear in mine.


My advise would be to use that as a reference point and adjust your ruleset as you see fit to meet your specific needs.
I'd include that Block Spooks block of rules and the Port 0 rule if I were you.

I don't use NAT but have a version in my Beginner's Tutorial adapted to use CUPS that directs LAN traffic to specific ports.

Mine as you see it is set to block incoming traffic. I still get my daily security mail from sendmail, NTP is carried out with no interference in incoming or outgoing traffic during use as a general purpose desktop pf firewall ruleset.

As far as how they look and are reported with "pfctl -s all", I would say they look similar.
I would love to see it for myself, as would everyone else when you ask a question like this.
 
Hi Ivan,

If you're starting out I would highly recommend you to use a template such as this one and go from there. Also, adding comments helps a lot even if it's just for yourself :)
 
If you're starting out I would highly recommend you to use a template such as this one and go from there.
You've also got two default block rules.

One at the top after your scrub rule and another as the last rule before your declaration of outgoing rules;
Code:
[code]# Block policy, either silently drop packets or tell sender that request is blocked
set block-policy return

# Don't bother to process (filter) following interfaces such as loopback:
set skip on lo0

# Scrub traffic
# Add special exception for game consoles such as PS3 and PS4 (NAT type 2 vs 3)
# scrub from CHANGEME to any no-df random-id fragment reassemble
scrub on $ext_if all

Then you have another default deny rule as the last rule before your outgoing rules;
Code:
# Block everything else
block in on $ext_if all
I've got the scrub rule, followed by one Default deny everything rule trailed by pass loopback:

Code:
### Reassemble fragmented packets
scrub in on $ext_if all fragment reassemble

### Default deny everything
block log all

### Pass loopback
set skip on lo0


Perhaps the difference being paradigm:

You're correct, "everything" is allowed by default just like your standard off the shelf router.

Where mine is set to block by default.
 
Defining block-policy doesn't actually block anything. "set block-policy - The block-policy option sets the default behaviour for the packet block action" (man pf.conf)
Since pf evaluates rules in the same order as they're listed in your ruleset it may not be ideal to have a global block rule.
 
Since pf evaluates rules in the same order as they're listed in your ruleset it may not be ideal to have a global block rule.
Thank you for your input, it is also appreciated as I get familiar with pf. From my understanding and reading the documentation, the evaluation of the filtering rules is in the order that they appear in the "pf.conf", hence one of my original questions in this post: when I run "pfctl -sr", the run-time order displayed is different than the one I set on "pf.conf". More on this below.

Specific to your comment above: my ultimate goal is to block everything in and out by default and then manually select what is allowed in or out (the way I have used other firewalls) that is why I have a global block as default and after that, as long as it is IPv4 and it comes from my LAN, I will let it out and also let in the return traffic back in, that's all I need and want. So, since filtering rules are evaluated in the order that they appear (with an asterisk as noted in my comment above) but all rules have to evaluated and last best match wins (except a rule with "quick") then technically it does not matter how the rules are organized as the evaluation has to process every rule regardless. Am I way off base here? I will ask you to please please keep in mind that I am trying to learn pf, not trying to argue or be a contrarian.

The wording of my rules as compared to yours.

I have rules that do not appear in yours.

You have a NAT rule that does not appear in mine.
Thank you again for your input Tri. Our configurations and approaches are very different, beginning with the use of NAT, which yo don't use so we can't compare that and with your approach of specifically blocking and allowing specific traffic, nothing wrong with your approach obviously but is quite different to mine so I do expect syntax and rules to be different. I will evaluate what I can take from your rules and try to apply to my setup.
 
lan="bge0"
wan="bge1"
set loginterface $wan
set optimization normal
set block-policy drop
set skip on lo0
scrub on $wan all
nat on $wan from $lan:network to any -> ($wan)
block drop log all
The "drop" here is redundant. You already set the block policy to drop above. You might want to add pass in quick inet proto icmp all icmp-type { echoreq, unreach } for path MTU discovery.
pass in on $lan from $lan:network to any keep state
You don't need "keep state" -- it's the default. I had this pointed out to me on these forums.
pass out on $wan from $lan:network to any keep state
This rule does nothing, your analysis is correct.
From the "man pf.conf" Translations section (yes, I read the manual pages ;) )
My kind of person!

Edit: One question, it seems to me your firewall is not able to initiate connections to the LAN side in these rules. Can you ssh back to a machine on the LAN side from the firewall?
 
Edit: One question, it seems to me your firewall is not able to initiate connections to the LAN side in these rules. Can you ssh back to a machine on the LAN side from the firewall?
That is correct, and it is by design. As long as the connection is initiated from the LAN, yes, I can ssh, ping, transfer files, etc.
 
Jose interesting, after your comments, I changed my rules to:
Code:
block log all

pass in on $lan from $lan:network to any
pass out on $wan from $wan:network to any

and now the run-time rule order (pfctl -sr) of the rules match the .conf file:
Code:
scrub on bge1 all fragment reassemble
block drop log all
pass in on bge0 inet from 192.168.128.0/24 to any flags S/SA keep state
pass out on bge1 inet from 168.70.76.0/20 to any flags S/SA keep state

:)
 
Jose interesting, after your comments, I changed my rules to:
Code:
block log all

pass in on $lan from $lan:network to any
pass out on $wan from $wan:network to any
Do you really need $wan:network here? Would pass out on $wan from ($wan) to any be better?
 
Do you really need $wan:network here? Would pass out on $wan from ($wan) to any be better?
Hmm, interesting. I do need the lan:network to automatically add the subnet associated with it, otherwise I would have to manually enter it but good question on the WAN side, I technically only need the IP address assigned to it to have access and not the whole subnet (because this is PAT, not NAT, technically). Imma try that and see what happens. :)
 
Thank you for your input, it is also appreciated as I get familiar with pf. From my understanding and reading the documentation, the evaluation of the filtering rules is in the order that they appear in the "pf.conf", hence one of my original questions in this post: when I run "pfctl -sr", the run-time order displayed is different than the one I set on "pf.conf". More on this below.

Specific to your comment above: my ultimate goal is to block everything in and out by default and then manually select what is allowed in or out (the way I have used other firewalls) that is why I have a global block as default and after that, as long as it is IPv4 and it comes from my LAN, I will let it out and also let in the return traffic back in, that's all I need and want. So, since filtering rules are evaluated in the order that they appear (with an asterisk as noted in my comment above) but all rules have to evaluated and last best match wins (except a rule with "quick") then technically it does not matter how the rules are organized as the evaluation has to process every rule regardless. Am I way off base here? I will ask you to please please keep in mind that I am trying to learn pf, not trying to argue or be a contrarian.


Thank you again for your input Tri. Our configurations and approaches are very different, beginning with the use of NAT, which yo don't use so we can't compare that and with your approach of specifically blocking and allowing specific traffic, nothing wrong with your approach obviously but is quite different to mine so I do expect syntax and rules to be different. I will evaluate what I can take from your rules and try to apply to my setup.

No, that is correct however if you want to optimize your ruleset further (quick for example) you'll always have one evaluation extra if you place block first although that wont probbaly matter much unless you push a lot of data. You can also rearrange the rules to make them match your traffic flow for even more optimization.
 
Since pf evaluates rules in the same order as they're listed in your ruleset it may not be ideal to have a global block rule.
It is ideal to have a global block rule exacly where I have it..

These two rules make a functional pf ruleset in themselves:

Code:
Block in all
pass out all keep state

That will server as a functional firewall on a FreeBSD and an OpenBSD box.

My full ruleset above is what I used on my OpenBSD box with a syntax change of one word in the egress rule to, egress:

Code:
### FreeBSD Keep and modulate state of outbound tcp, udp and icmp traffic
pass out on $ext_if proto { tcp, udp, icmp } from any to any modulate state
Code:
### OpenBSD Keep and modulate state of outbound tcp, udp and icmp traffic
pass out on egress proto { tcp, udp, icmp } from any to any modulate state
 
Back
Top