PF Wireguard, PF, and Nat ... oh my

Hello. :) Apparently I am unable to get PF to NAT anything across a wireguard interface. Whether this is an error in my understanding or current PF/Wireguard code is what I am trying to determine here.

To explain in detail, consider the attached conceptual diagram of my firewall setup, which shows a single packet traversing all the links as culled from tcpdump. The overall idea is to provide VPN services through the firewall for the target machine (10.0.0.15) to the VPN IP target (1.2.3.50).

Both VPN server and firewall are using FreeBSD 13.2-STABLE (a very recent onewith all the errata handled). Both servers use PF and wireguard. On the VPN server, ipforwarding is set to 1 (gateway_enable="YES").

I have tried 3 different configurations of NAT in PF on the VPN server. For reference (in case this diagram turns out wonky) they are:

Code:
nat log on vtnet0 inet from wg0 to any -> 1.2.3.50
nat log on vtnet0 inet from 10.0.0.15 to any -> 1.2.3.50
nat log on wg0 inet from 10.0.0.15 to any -> 1.2.3.50

Tcpdump shows that none of these configuration lines work to change the source address of packets from 10.0.0.15 to 1.2.3.50. Apparently, NAT is not happening at all. Now if I replace the wireguard tunnel with an openvpn tunnel, this NAT configuration works fine:

Code:
nat log on vtnet0 inet from 10.0.0.15 to any -> 1.2.3.50

which is effectively the 2nd example.

Can anyone shed any light on this situation? Thanks in advance.
 

Attachments

  • natpfwg.png
    natpfwg.png
    65.2 KB · Views: 91
Look at your wg config. You need to set which traffic is going to be routed via it.

There's many examples for site-to-site wg config.
 
So the only configuration constants I see in wg(8) that could likely apply to your comment are Endpoint (I've already established the tunnel as I can see packets traverse it on both sides) and AllowedIPs which is set to 0.0.0.0/0.

What am I missing here? Can you point me to these examples?
 
I have tried nat on the external interface of the VPN server (see diagram):

Code:
# ifconfig wg0 inet 10.0.0.1 netmask 255.255.255.0

Then in /etc/pf.conf

Code:
nat log on vtnet0 inet from 10.0.0.15/24 to any -> 1.2.3.50

Packets matching this 10.0.0/24 netmask are visible to tcpdump on wg0 but do not get nat to 1.2.3.50.
 
Unfortunately, the complete firewall configuration in question is not under my authority to completely share. This is what I find to be relevant that I can share from the firewall side:

Code:
# ifconfig wg0
wg0: flags=81c5<UP,DEBUG,RUNNING,NOARP,PROMISC,MULTICAST> metric 0 mtu 1420
    options=80000<LINKSTATE>
    inet 10.1.2.1 netmask 0xffffff00
    groups: wg
    tunnelfib: 2
    nd6 options=109<PERFORMNUD,IFDISABLED,NO_DAD>
# setfib 2 netstat -rn | grep default
default            5.6.7.1            UGS        ixl2
# setfib 2 netstat -rn | grep wg0
10.1.2.0/24        link#10            U           wg0
# grep wg /etc/pf.conf 
wg_if            = "wg0"
wg_port            = "50001"
pass in quick on $wg_if from any to any
pass in log quick on $fastnet_if proto udp from port $wg_port to any rtable 2

On the server side:
Code:
# ifconfig wg0
wg0: flags=81c1<UP,RUNNING,NOARP,PROMISC,MULTICAST> metric 0 mtu 1420
    options=80000<LINKSTATE>
    inet 10.1.2.2 netmask 0xffffff00
    groups: wg
    nd6 options=109<PERFORMNUD,IFDISABLED,NO_DAD>
# ifconfig vtnet0
vtnet0: flags=8963<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> metric 0 mtu 1500
    options=4c07bb<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,JUMBO_MTU,VLAN_HWCSUM,TSO4,TSO6,LRO,VLAN_HWTSO,LINKSTATE,TXCSUM_IPV6>
    ether 01:23:45:67:89:ab
    inet 1.2.3.4 netmask 0xfffffe00 broadcast 1.2.3.255
    inet 1.2.3.50 netmask 0xffffffff broadcast 1.2.3.50
# netstat -rn
...
Destination        Gateway            Flags     Netif Expire
default            1.2.3.1            UGS      vtnet0
10.1.2.0/24        link#3             U           wg0
10.1.2.2           link#3             UHS         lo0
1.2.3.0/24         link#1             U        vtnet0
1.2.3.4            link#1             UHS         lo0
127.0.0.1          link#2             UH          lo0
1.2.3.50           link#1             UH          lo0

This is the pf.conf on the server side:

Code:
public_tcp_ports    = "{ ntp domain }"
public_udp_ports    = "{ domain 50001 }"
public_icmp_types   = "{ unreach timex paramprob }"
...
set block-policy drop
set skip on { lo0 }

nat log on vtnet0 inet from 10.1.2/24  to any -> 1.2.3.50

scrub on vtnet0 no-df random-id reassemble tcp max-mss 1420

block in log all
antispoof log quick for vtnet0 inet

pass out log all modulate state

pass in on wg0 inet from any to any
pass in on vtnet0 inet proto tcp from any to any port $public_tcp_ports modulate state
pass in log on $public_ifs inet proto udp from any to any port $public_udp_ports
pass in log on $public_ifs inet proto icmp from any to any icmp-type $public_icmp_types keep state

ICMP ping from the firewall to an address on the wg0 lan (e.g. 10.1.2.15) gives me a TTL exceeded. Both wg0 interfaces ping to each other. There is zero output from a tcpdump of pflog relevant to any of these addresses, just the usual bot traffic on the external interface being blocked.
 
What are the AllowedIPs in wg config on the both sites (wg server and firewall server)?

Can you simplify the config without FIB2 and without default route 0.0.0.0/0 on the AllowedIPs? So you can check if the both subnets can reach each other first. Then you can add the default route via wg or split it on two half 0.0.0.0/1 and 128.0.0.0/1.
 
On the firewall:

AllowedIPs 0.0.0.0/0


On the server:

AllowedIPs 10.1.2.0/24


Are you saying AllowedIPs is a route and not an allowed packets mask?

I am unable to make frequent changes on the firewall server nor can I simplify it and just remove FIBs. I can change the wireguard VPN server all I want. There was a testing mode I was in where I was able to see (via tcpdump) packets on em0 go through the wireguard tunnel and out onto the internet (vtnet0) at the VPN server side, but they weren't being translated at all so they didn't go anywhere.
 
They are several routes that are created.
First there's static route for the endpoint of the wg interface ($GATEWAY4 is IPv4 address of our current gw, there's also $GATEWAY6 for IPv6)
route -q -n add -inet "$endpoint" -gateway "$GATEWAY4"

Then for the each IP address $1 in "AllowedIPs" there's a another route ($INTERFACE is our wg interface) and the $TABLE is the routing table FIB where the route will be added. If you use more than default FIB then you should have it in your wg0.conf otherwise it will not have the routes to the subnet behind the server but because you are not sharing your current configuration i cannot tell if you have the right routes created in the FIB of your wg interface. If you don't want to provide your actual configuration it's hard to guess what exactly is happening.
route -q -n add "-$family" -fib "$TABLE" "$1" -interface "$INTERFACE"

If the default route is set in "AllowedIPs" via /0 subnet then the following route is added
route -q -n add -inet 0.0.0.0/1 -interface "$INTERFACE"
route -q -n add -inet 128.0.0.0/1 -interface "$INTERFACE"

All of this is very well explained with many examples in the manual page of wg-quick(8) bash script which is used from the rc.d/wireguard
 
when you install the wireguard metaport it will create for you the /usr/local/etc/rc.d/wireguard startup script. Which script calls the /usr/local/bin/wg-quick
pkg search wireguard
 
Back
Top