I'd like to set a condition so that a firewall rule will not apply to a list of ports, something along the lines of this:

 table <my_table> persist file "/etc/pf.anchors/entries"
 set skip on lo
 block out quick proto { tcp, udp } from any to ! <my_table> port != { 66 80 } 

This gives a syntax error when running 'sudo pfctl -vnf /etc/pf.conf'.

If I put the following, it works (only specify one port).

 block out quick proto { tcp, udp } from any to ! <my_table> port != 80

How can I specify a list of ports with the '!=' operator?

  • have you tried to use comma? != { 66, 80 } (it is a suggestion based on deduction, I never did it) – Prado Aug 29 '19 at 18:39
  • Already tried that, doesn't work. – GreekFire Aug 29 '19 at 18:56
  • Take a look here: freebsd.org/cgi/… – Prado Aug 29 '19 at 19:18

One should clearly understand what Pf's "lists" are. They aren't a part of ruleset that gets loaded into kernel in fact, but macros instead. It means they're expanded during preprocessing phase of rules loading — contrary to tables. Keeping that in mind saves one from "shooting in own foot".

Let's now see what you're trying to do:

block out quick proto { tcp, udp } from any to ! <my_table> port != { 66 80 } 
  • Block immediately If
    • it's TCP or it's UDP
    • AND it's destined to
      • IPs that aren't in my_table
      • AND ports, that aren't in the list

As I've told you lists' items would get expanded into a separate rule each. And this would break the logic I've just explained: only the first port of the list would be treated right, if you block immediately (quick) it obviously means no second checking — "not allowed port, ok, blocking it".

Mastering firewall ruleset you'd better keep things as simple as possible. Well, actually it's not only for firewalls — it's general and programming common sense. Double negations, exception of exceptions aren't that simple to follow.

So what are your options then? — You can take a look at the different angle: what and when do you want to pass?

# If it's destined to IP in <my_table> -- pass:
pass  out quick proto { tcp, udp } from any to <my_table>

# ElseIf it's to allowed ports -- pass:
pass  out quick proto { tcp, udp } to port { 66 80 }

# And this point would be reached only if it wasn't to <my_table>
# and to some ports other than allowed ones:
block out quick proto { tcp, udp }

This ruleset is way more readable indeed. Moreover — macro expansion doesn't spoil its logic.

Of course, Pf has some other means for solving this very task but describing them all properly would make this answer way too lengthy.

| improve this answer | |

You can make the inverse command logic.

First Block all, then open just the ones you want:

The result is the same you tried by negating all except those chosen to be opened. :)

set skip on lo
port_pass = "{ 80 66 53 22 }"
block all
pass out on en0 proto { tcp, udp } to any port $port_pass keep state
| improve this answer | |
  • 1. you can omit keep state, it was assumed by default even in that ancient Pf's version that MacOS comes with. 2. It's not same, because your ruleset doesn't lookup <my_table>'s content – poige Sep 11 '19 at 2:59

Figured it out:

Is by negating each individual port, inside the delimiter { }

 block out quick proto { tcp, udp } from any to ! <my_table> port { != 66, != 80 } 

I found the 'op-list' section for specifying port here: https://man.openbsd.org/pf.conf.5

This shows you how to apply the logic of adding more ports

| improve this answer | |

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .