IPFW Unable to have kill switch along with NAT rules

I've been trying to get a jail to work as a Wireguard gateway in conjunction with my ipfw kill switch and NAT rules.
I'm able to get one or the other to work, but unfortunately not both.

My ipfw ruleset to block all traffic except traffic destined for the Wireguard server and my two LANs (172.16.2.0/24 - which I want to route through Wireguard via my jail and 192.168.1.0/24 which I just want to access) :
00001 allow ip from any to any via lo0
00005 check-state :default
00010 allow ip from any to any via wg0 keep-state :default
00101 allow ip from me to 172.16.2.0/24
00102 allow ip from 172.16.2.0/24 to me
00103 allow ip from me to 192.168.1.0/24
00104 allow ip from 192.168.1.0/24 to me
00110 allow udp from any to any 443 out via epair0b keep-state :default
00115 deny log ip from any to any
65535 allow ip from any to any

This works fine and when I'm not connected to Wireguard, I can't reach any device outside both of my LANs.

But now, when I want to add the appropriate NAT rules to allow any device on my LAN (172.16.2.0/24) to route traffic through this jail via Wireguard, I added these 3 rules, which work fine without the other rules above:
ipfw -q -f flush
ipfw -q nat 1 config if wg0
ipfw -q add 00050 nat 1 all from 172.16.2.0/24 to any via wg0
ipfw -q add 00051 nat 1 all from any to any in via wg0

From this point on, I am able to route all traffic from my LAN (172.16.2.0/24) through Wireguard, which is what I want.

However, when I try to merge these rules with the kill switch rules, I am no longer able to route traffic from my LAN, only the Wireguard jail is able to do so :
00001 allow ip from any to any via lo0
00005 check-state :default
00010 allow ip from any to any via wg0 keep-state :default
00050 nat 1 ip from 172.16.2.0/24 to any via wg0
00051 nat 1 ip from any to any in via wg0

00101 allow ip from me to 172.16.2.0/24
00102 allow ip from 172.16.2.0/24 to me
00103 allow ip from me to 192.168.1.0/24
00104 allow ip from 192.168.1.0/24 to me
00105 allow ip from me to 192.168.196.0/24
00106 allow ip from 192.168.196.0/24 to me
00110 allow udp from any to any 443 out via epair0b keep-state :default
00115 deny log ip from any to any
65535 allow ip from any to any

I can see from the tcpdump that I'm not able to send traffic back to my LAN:
root@jailgw:~ # tcpdump -nni epair0b host 172.16.2.50
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on epair0b, link-type EN10MB (Ethernet), capture size 262144 bytes
20:41:38.727012 IP 172.16.2.50.53714 > 142.251.36.14.443: Flags [ S], seq 1414858473, win 65535, options [mss 1460,nop,wscale 6,sackOK,TS val 1267885730 ecr 0], length 0
20:41:39.727245 IP 172.16.2.50.53714 > 142.251.36.14.443: Flags [ S], seq 1414858473, win 65535, options [mss 1460,nop,wscale 6,sackOK,TS val 1267886730 ecr 0], length 0
20:41:41.951914 IP 172.16.2.50.53714 > 142.251.36.14.443: Flags [ S], seq 1414858473, win 65535, options [mss 1460,nop,wscale 6,sackOK,TS val 1267888955 ecr 0], length 0

I thought about removing the deny rule just to see if the connection from my test vm (172.16.2.50) would be able to reach the Internet through the Wireguard jail, but unfortunately that doesn't seem to work either.

So at this point, I'm not sure why NAT stopped working. Without the kill switch rules this works but not together.

Does anyone have any pointers as to what might be causing this as I have run out of ideas?

Thanks.
 
00050 nat 1 ip from 172.16.2.0/24 to any via wg0
00051 nat 1 ip from any to any in via wg0
It is better to use something like this:
00050 nat 1 ip from 172.16.2.0/24 to any out via wg0
00051 nat 1 ip from any to <WAN_IP_HERE> in via wg0
or
00050 nat 1 ip from 172.16.2.0/24 to not 172.16.2.0/24 out via wg0
00051 nat 1 ip from any to <WAN_IP_HERE> in via wg0

If your rules after the NAT does not work then try to read about "the sysctl variable net.inet.ip.fw.one_pass".
man ipfw()
NETWORK ADDRESS TRANSLATION (NAT)
ipfw support in-kernel NAT using the kernel version of libalias(3).

To let the packet continue after being (de)aliased, set the sysctl
variable net.inet.ip.fw.one_pass to 0.
 
Sorry it took me so long to reply.

Thanks for the suggestions, but my WAN_IP is dynamic, so probably not a good idea since every time it changes I have to update the rules, although I see that it is better to scope down the rule instead of just throwing 'any'.

In any event, after some troubleshooting I finally found the problem, the NAT rules were deployed before the wireguard interface was created, so NAT was never enabled.

I was able to fix this by simply adding a post-up action to the wireguard script to ensure that NAT is not enabled on the wg interface until after the interface is created:

PostUp = script_type=up route add -host 192.168.1.0/24 172.16.2.1; ipfw nat 1 config if wg0

It may not be a very elegant workaround, but I don't see any other way to do it.

If you have any other suggestions, I'm all ears.

Thanks!
 
Back
Top