Freebsd – Port-Forward for all IPs on an interface

freebsdpfport-forwarding

I'm running a java-based web server as a non-privileged user. Therefore it listens on ports 8080 and 8443 instead of 80 and 443.

Right now I'm using the following pf rules to forward the ports internally:

rdr pass on $ext_if proto tcp from any to $ext_ip port 80 -> $ext_ip port 8080
rdr pass on $ext_if proto tcp from any to $ext_ip port 443 -> $ext_ip port 8443

(with macros defined for $ext_if and $ext_ip, obviously)

Now I want to add more IPs to the interface (for SSL vHosts) — what's the best way, to do this? Do I have to repeat those rules for every IP added? Or can I somehow make a rule for "every IP on that interface"?

EDIT: Unfortunately, the IPs are not from the same subnet. I tried listing them in the $ext_ip macro, but I still would prefer a solution where I don't have to add every IP explicitly.

Thanks!

Best Answer

My research shows that there's no pf.conf clause that will get you this level of one-to-one expansion of rules.

I was hoping that you could do this with macros, which in your case would be something like:

vhost_ips = "{ 198.51.100.4, 198.51.100.5 }"
rdr pass on $ext_if proto tcp from any to $vhost_ips port 80  -> $ext_ip port 8080
rdr pass on $ext_if proto tcp from any to $vhost_ips port 443 -> $ext_ip port 8443

... but if the macro is used more than once on the same line, it actually expands the latter ones to the entire macro, and invokes round-robin:

rdr pass on int0 proto tcp from any to 198.51.100.4 port 80  -> { 198.51.100.4, 198.51.100.5 } port 8080 round-robin
rdr pass on int0 proto tcp from any to 198.51.100.5 port 443 -> { 198.51.100.4, 198.51.100.5 } port 8443 round-robin

... which I'm pretty sure was not what you intended. :-) Tables also appear to be out. The man page explicitly says:

"Tables can also be used for the redirect address of nat and rdr rules and in the routing options of filter rules, but only for round-robin pools."

So unfortunately, it looks as though your only option is to either manually maintain the list, or generate it as a separate file and include it.

Also unfortunately, the include clause is not mentioned in the pf.conf man pages on my 7.4, 8.2 or 9.0-RC2 systems, so if you are running the stock PF that comes with FreeBSD, I believe that they are running the PF imported from OpenBSD 4.5, which doesn't appear to support include ... but just in case, here's a way to generate the include:

$ cat vhost.list
198.51.100.4
198.51.100.5

$ cat gen-vhosts-pf.sh
#!/bin/sh
for ip in `egrep -v ^# /etc/vhost.list`; do
        echo "rdr pass on \$ext_if proto tcp from any to $ip port 80  -> $ip port 8080"
        echo "rdr pass on \$ext_if proto tcp from any to $ip port 443 -> $ip port 8443"
done

$ ./gen-vhosts-pf.sh | sudo tee /etc/pf.conf.vhosts
rdr pass on $ext_if proto tcp from any to 198.51.100.4 port 80  -> 198.51.100.4 port 8080
rdr pass on $ext_if proto tcp from any to 198.51.100.4 port 443 -> 198.51.100.4 port 8443
rdr pass on $ext_if proto tcp from any to 198.51.100.5 port 80  -> 198.51.100.5 port 8080
rdr pass on $ext_if proto tcp from any to 198.51.100.5 port 443 -> 198.51.100.5 port 8443

... which would be imported into pf.conf with:

include "/etc/pf.conf.vhosts"

If you're not running a PF that supports includes, it'll be a little trickier, but you get the idea.

Sorry for the slightly scattered research; I also couldn't find a canonical way to ask PF for its version information, so had to flail around a bit.

EDIT: You may be able to do the including with the load anchor clause, though it appears you will have to repeat your $ext_if macro within the anchor.