OpenBSD pf port forwarding multiple rules

openbsdpfport-forwarding

I have a few dozen servers behind OpenBSD firewall with port forwarding. Most rules are very similar and differ only in IPs or sometimes in ports forwarded, so I want to compact them to remove excessive repetition but I've found that it is impossible to use tables with rdr-to rules. Is there any way to improve this configuration? May be there is option to use pf macros to generate multiple rules at once? I can't use external preprocessor at the moment.

Example set of rules:

pass in  on $extif proto tcp from any     to 10.0.0.213   port {25,80,443} rdr-to 172.16.1.193
pass in  on $intif proto tcp from $intnet to 10.0.0.213   port {25,80,443} rdr-to 172.16.1.193
pass out on $intif proto tcp from any     to 172.16.1.193 port {25,80,443} received-on $intif nat-to $intif

pass in  on $extif proto tcp from any     to 10.0.0.214   port {25,80,443} rdr-to 172.16.1.194
pass in  on $intif proto tcp from $intnet to 10.0.0.214   port {25,80,443} rdr-to 172.16.1.194
pass out on $intif proto tcp from any     to 172.16.1.194 port {25,80,443} received-on $intif nat-to $intif

pass in  on $extif proto tcp from any     to 10.0.0.215   port {25,80,443,3389} rdr-to 172.16.1.195
pass in  on $intif proto tcp from $intnet to 10.0.0.215   port {25,80,443,3389} rdr-to 172.16.1.195
pass out on $intif proto tcp from any     to 172.16.1.195 port {25,80,443,3389} received-on $intif nat-to $intif

Best Answer

From the pf.conf(5) manpage:

Translation options apply only to packets that pass through the specified interface, and if no interface is specified, translation is applied to packets on all interfaces. For instance, redirecting port 80 on an external interface to an internal web server will only work for connections originating from the outside. Connections to the address of the external interface from local hosts will not be redirected, since such packets do not actually pass through the external interface. Redirections cannot reflect packets back through the interface they arrive on, they can only be redirected to hosts connected to different interfaces or to the firewall itself.

You might be able to condense some of your rules by not specifying which interface you want the re-direction to take place on and allow pf to evaluate it for you.

For example:

pass in on $extif proto tcp from any to 10.0.0.213 port {25,80,443} rdr-to 172.16.1.193
pass in on $intif proto tcp from $intnet to 10.0.0.213 port {25,80,443} rdr-to 172.16.1.193

Could be re-written as:

pass in proto tcp from any to 10.0.0.213 port {25, 80, 443} rdr-to 172.16.1.193
  • Inbound traffic destined for 10.0.0.213 on $extif will be redirected to 172.16.1.193.
  • Inbound traffic destined for 10.0.0.213 on $intif will be redirected to 172.16.1.193.
  • This will also allow traffic that is not part of $intnet to happily be redirected as well. This may or may not desirable.

Without seeing your entire ruleset or really knowing what you're trying to accomplish I can only offer a proof-of-concept example.

One final note: I would really avoid doing this. There is a tendency (at least I have it) to want to write the fewest rules possible by having one rule do more than "one thing". This is bad. Firewalls are already frightfully complicated and seemly always misconfigured; why make it harder on yourself by crafting a byzantine ruleset? A longer ruleset with simpler rules will be easier to understand, maintenance and debug. Avoid the temptation to be overly clever.