On Wed, Feb 23, 2022 at 04:55:05PM +0000, Laura Smith wrote:
> I've never had occasion to use bi-nat before and I'm struggling a little to 
> wrap my head around the concept.
> 
> The OpenBSD FAQ (https://www.openbsd.org/faq/pf/nat.html) gives the following 
> example:
> 
> "pass on tl0 from $web_serv_int to any binat-to $web_serv_ext"
> 
> However I'm not clear on how this fits into the overall filtering strategy ?  
> i.e. building logically on the example above, how do I say "only allow 
> inbound bi-nat for ports 80 & 443".
> 
> The FAQ makes an obscure statement "TCP and UDP ports are never modified with 
> binat-to rules as they are with nat rules.", which I'm guessing is where the 
> answer lies.  But I'm not clear what this means in context ?
> 
> Thanks !

turns out binat is syntactic sugar, so it can be understood in terms of
nat and rdr rules. let's say 192.0.2.1 is your external ip, 10.0.0.1
is your internal ip, and em0 is your external interface:

dlg@ix ~$ echo 'pass on em0 inet from 10.0.0.1 to any binat-to 192.0.2.1' | 
pfctl -vnf - 
pass out on em0 inet from 10.0.0.1 to any flags S/SA nat-to 192.0.2.1 
static-port
pass in on em0 inet from any to 192.0.2.1 flags S/SA rdr-to 10.0.0.1

i read that as any connection to my external ip is forwarded to the
backend, and any connection from my backend server is rewritten to
appear as if it's coming from my external ip. this could be useful if
you've got a small public ip address allocation (eg a /29) from an
ISP and don't want to burn the network and broadcast addresses by
putting them on an actual subnet. you would binat every public IP
to a backend on a private IP instead. id personally use p2p tunnels
from the router to each backend, but maybe MTU/MRU is precious too?

anyway, in terms of policy, restricting this to ports 80 and 443
looks a bit clumsy:

dlg@ix ~$ echo 'pass on em0 inet from 10.0.0.1 to any port { 80 443 } binat-to 
192.0.2.1' | pfctl -vnf -
stdin:1: port only applies to tcp/udp
stdin:1: skipping rule due to errors
stdin:1: port only applies to tcp/udp
stdin:1: skipping rule due to errors
stdin:1: port only applies to tcp/udp
stdin:1: skipping rule due to errors
stdin:1: rule expands to no valid combination
stdin:1: port only applies to tcp/udp
stdin:1: skipping rule due to errors
stdin:1: port only applies to tcp/udp
stdin:1: skipping rule due to errors
stdin:1: rule expands to no valid combination
stdin:1: rule expands to no valid combination
dlg@ix ~$ echo 'pass on em0 inet proto tcp from 10.0.0.1 to any port { 80 443 } 
binat-to 192.0.2.1' | pfctl -vnf -
pass out on em0 inet proto tcp from 10.0.0.1 to any port = 80 flags S/SA nat-to 
192.0.2.1 static-port
pass in on em0 inet proto tcp from any port = 80 to 192.0.2.1 flags S/SA rdr-to 
10.0.0.1
pass in on em0 inet proto tcp from any port = 443 to 192.0.2.1 flags S/SA 
rdr-to 10.0.0.1
pass out on em0 inet proto tcp from 10.0.0.1 to any port = 443 flags S/SA 
nat-to 192.0.2.1 static-port
pass in on em0 inet proto tcp from any port = 443 to 192.0.2.1 flags S/SA 
rdr-to 10.0.0.1

yeah. im not sure the pass out rules are that useful in practice.

if you had a default allow policy, then binat could make sense. you'd
have a pass binat rule followed by block rules to filter out the
exceptions to your default policy.

another option could be using match and tags:

dlg@ix ~$ cat /tmp/rules
match on em0 inet from 10.0.0.1 to any binat-to 192.0.2.1 tag backend
pass out on em0 tagged backend
pass in on em0 inet proto tcp to port { 80 443 } tagged backend
dlg@ix ~$ pfctl -vnf /tmp/rules
match out on em0 inet from 10.0.0.1 to any tag backend nat-to 192.0.2.1 
static-port
match in on em0 inet from any to 192.0.2.1 tag backend rdr-to 10.0.0.1
pass in on em0 inet proto tcp from any to any port = 80 flags S/SA tagged 
backend
pass in on em0 inet proto tcp from any to any port = 443 flags S/SA tagged 
backend
pass out on em0 all flags S/SA tagged backend

dlg

Reply via email to