Raspberry Pi 4 Wireless WireGuard client over a tagged VLAN

It's been a couple of days of attempting to make this work without complete success and I've decided to ask the community: How should public traffic come from wlan0 through a WireGuard interface to a tagged VLAN eth0.100 while eth0 is available for management connectivity to the router as a DHCP client?

Right now I have a WireGuard interface that gets a handshake, although it seems like it's connecting through eth0 instead of wlan0. I assume this requires a routes configuration to make WireGuard connect via wlan0?

The next thing I get stuck at is the link between WireGuard and eth0.100. I have attempted bridging those, and/or adding a forwarding firewall zone. Unfortunately can only see BOOTP/DHCP requests originating from the other end of the VLAN when using tcpdump -i eth0.100.

I assume there has to be a DHCP server that answers the BOOTP/DHCP request or a static IP configuration that can speak to WireGuard if there is a bridge. And maybe more, I'm not sure.

Any suggestions or instructions for how to achieve this are most welcome!

This is a very confusing topology. Could you draw a diagram?

Why is WG coming in over wifi? Is that an upstream network (i.e. another WAN)?

WG does not do anything with DHCP/BOOTP, and it is a routed protocol. This means you can't expect a bridge to work... you have to manage this with routing.

It would be helpful to understand the goal, with the assistance of the diagram, so we can see what you are trying to achieve.

The general idea behind this topology is to use a Raspberry Pi 4 as a failover WAN in case the main WAN fails. The gateway can then switch over to VLAN 100 for traffic and WG is added for security. Access to the Pi over eth0 is left for management and configuration.

WG does not do anything with DHCP/BOOTP, and it is a routed protocol. This means you can't expect a bridge to work... you have to manage this with routing.

How can something like this be managed with routing?

PS: neggles just suggested that WG is L3 and VLANs are L2, does that mean there has to be an L3 relay for this to work?

Where are the termination points for your wireguard VPN?

Does your main router have multi-wan support? If not, your configuration will not work well. There are better topologies to consider -- for example, a true wireless bridge connected as WAN2 in a multi-wan configuration where the main router is responsible for masquerading the WWAN. Or failing that, you can have a wireless > wired setup where that device handles NAT masquerading, but you'll still need the main router to be multi-wan capable.

Basically, the WG connection must be routed. The router can handle DHCP and masquerading for your WG connection.

I'm not sure what you're proposing here... yes, WG is a routed (L3) protocol... VLANs happen at L2 (switching). You need to route between the WG network and the other network(s).

Thanks for the reply!

Does your main router have multi-wan support?

It's a Ubiquiti USG and it does. I've seen something similar here, and I think I need to use VLANs to make things work: https://community.ui.com/questions/Failover-to-LAN-Port-like-U-LTE/c91af49e-e2ff-43b4-b601-a7223532d71a

I recreated the VLAN network on the router and added a DHCP server. The Pi's VLAN interface on the other end was configured as a DHCP client and both can see each other fine.

At the moment, the Pi can ping the outside internet fine through both the VPN interface and the VLAN one, however, the router can only ping the outside internet through its WAN interface. Pinging 1.1.1.1 through eth1.100 results in Destination Host Unreachable.

I've attempted updating the firewall on the Pi to have the VLAN and the VPN in their own corresponding zones with no masquerading anywhere.

Both firewall zones accept input, output, and forward. VLAN zone allows forward to destination zone VPN. VPN zone allows forward from source zones VLAN. That did not change anything.

I assume I need appropriate routing for this to work?

Yes, but let's start with some simple things.

  1. Where are the two endpoints for Wireguard?
  2. Yes, the USG supports multi-wan, but it must be done on two physical ports. This means you need to use WAN(1) for your primary, and LAN2/WAN2 (or VOIP, depending on the physical generation of your device) for the 2nd WAN. Do you currently have that setup?
  3. is the pi running OpenWrt? If not, which device is using OpenWrt?
  1. Where are the two endpoints for Wireguard?

the RPi and a host on the internet

  1. Yes, the USG supports multi-wan, but it must be done on two physical ports. This means you need to use WAN(1) for your primary, and LAN2/WAN2 (or VOIP, depending on the physical generation of your device) for the 2nd WAN. Do you currently have that setup?

my assumption was that it can be done between a physical and VLAN network by looking at the UI, nothing plugged into LAN2/WAN2 at the moment

  1. is the pi running OpenWrt? If not, which device is using OpenWrt?

Raspberry Pi 4 running OpenWrt 21.02.3 r16554-1d4dea6d4f

Ok... the best option here is to simply get the OpenWrt WG connection working first -- as in routed behavior. So basically you will want to connect a client directly to the OpenWrt router and make sure it can route through the WG connection.

Nope. You'll need to use the secondary physical port.

Good.

Please post the config of this Pi.

Please copy the output of the following commands and post it here using the "Preformatted text </> " button:
grafik
Remember to redact passwords, MAC addresses and any public IP addresses you may have:

cat /etc/config/network
cat /etc/config/wireless
cat /etc/config/dhcp
cat /etc/config/firewall

Ok... the best option here is to simply get the OpenWrt WG connection working first -- as in routed behavior. So basically you will want to connect a client directly to the OpenWrt router and make sure it can route through the WG connection.

I think that works (meaning ping 1.1.1.1 -I vpn replies), although I think it doesn't always connect through wlan0 and sometimes might be going through eth0 (DHCP client to the switch).

Nope. You'll need to use the secondary physical port.

Can try that.

Please copy the output of the following commands...

# cat /etc/config/network

config interface 'loopback'
        option device 'lo'
        option proto 'static'
        option ipaddr '127.0.0.1'
        option netmask '255.0.0.0'

config globals 'globals'
        option ula_prefix 'fde4:81d6:977c::/48'

config interface 'lan'
        option proto 'dhcp'
        option delegate '0'
        option device 'eth0'
        option defaultroute '0'

config device
        option name 'wlan0'
        option ipv6 '0'

config interface 'wwan'
        option proto 'dhcp'
        option hostname '*'
        option peerdns '0'
        option delegate '0'
        list dns '1.1.1.1'

config interface 'vlan'
        option device 'eth0.101'
        option proto 'static'
        option ipaddr '192.168.101.1'
        option netmask '255.255.255.0'
        option delegate '0'

config interface 'vpn'
        option proto 'wireguard'
        option force_link '1'
        option private_key '***'
        list addresses '10.0.0.200/32'
        option delegate '0'
        option defaultroute '0'

config wireguard_vpn
        option public_key '***'
        list allowed_ips '0.0.0.0/0'
        option endpoint_host '185.0.0.60'
        option persistent_keepalive '25'

config device
        option type '8021q'
        option ifname 'eth0'
        option vid '101'
        option name 'eth0.101'
root@OpenWrt:~# cat /etc/config/wireless

config wifi-device 'radio0'
        option type 'mac80211'
        option path 'platform/soc/fe300000.mmcnr/mmc_host/mmc1/mmc1:0001/mmc1:0001:1'
        option channel '36'
        option band '5g'
        option htmode 'VHT80'
        option cell_density '0'

config wifi-iface 'wifinet1'
        option device 'radio0'
        option mode 'sta'
        option ssid 'AP'
        option encryption 'psk2'
        option key 'password'
        option network 'wwan'
# cat /etc/config/dhcp

config dnsmasq
        option domainneeded '1'
        option boguspriv '1'
        option filterwin2k '0'
        option localise_queries '1'
        option rebind_protection '1'
        option rebind_localhost '1'
        option local '/lan/'
        option domain 'lan'
        option expandhosts '1'
        option nonegcache '0'
        option authoritative '1'
        option readethers '1'
        option leasefile '/tmp/dhcp.leases'
        option resolvfile '/tmp/resolv.conf.d/resolv.conf.auto'
        option nonwildcard '1'
        option localservice '1'
        option ednspacket_max '1232'

config dhcp 'lan'
        option interface 'lan'
        option start '100'
        option limit '150'
        option leasetime '12h'
        option dhcpv4 'server'
        list ra_flags 'managed-config'
        list ra_flags 'other-config'
        option ra 'hybrid'
        option dhcpv6 'hybrid'

config dhcp 'wan'
        option interface 'wan'
        option ignore '1'

config odhcpd 'odhcpd'
        option maindhcp '0'
        option leasefile '/tmp/hosts/odhcpd'
        option leasetrigger '/usr/sbin/odhcpd-update'
        option loglevel '4'

config dhcp 'vlan'
        option interface 'vlan'
        option ignore '1'
        option start '100'
        option limit '150'
        option leasetime '12h'
        list ra_flags 'none'
# cat /etc/config/firewall

config defaults
        option input 'ACCEPT'
        option output 'ACCEPT'
        option forward 'REJECT'
        option synflood_protect '1'

config zone 'lan'
        option name 'lan'
        option output 'ACCEPT'
        list network 'lan'
        option input 'ACCEPT'
        option forward 'REJECT'

config zone 'wan'
        option name 'wan'
        option output 'ACCEPT'
        option forward 'REJECT'
        option masq '1'
        option mtu_fix '1'
        option input 'REJECT'
        list network 'wwan'
        list network 'vpn'

config forwarding
        option src 'lan'
        option dest 'wan'

config rule
        option name 'Allow-DHCP-Renew'
        option src 'wan'
        option proto 'udp'
        option dest_port '68'
        option target 'ACCEPT'
        option family 'ipv4'

config rule
        option name 'Allow-Ping'
        option src 'wan'
        option proto 'icmp'
        option icmp_type 'echo-request'
        option family 'ipv4'
        option target 'ACCEPT'

config rule
        option name 'Allow-IGMP'
        option src 'wan'
        option proto 'igmp'
        option family 'ipv4'
        option target 'ACCEPT'

config rule
        option name 'Allow-DHCPv6'
        option src 'wan'
        option proto 'udp'
        option src_ip 'fc00::/6'
        option dest_ip 'fc00::/6'
        option dest_port '546'
        option family 'ipv6'
        option target 'ACCEPT'

config rule
        option name 'Allow-MLD'
        option src 'wan'
        option proto 'icmp'
        option src_ip 'fe80::/10'
        list icmp_type '130/0'
        list icmp_type '131/0'
        list icmp_type '132/0'
        list icmp_type '143/0'
        option family 'ipv6'
        option target 'ACCEPT'

config rule
        option name 'Allow-ICMPv6-Input'
        option src 'wan'
        option proto 'icmp'
        list icmp_type 'echo-request'
        list icmp_type 'echo-reply'
        list icmp_type 'destination-unreachable'
        list icmp_type 'packet-too-big'
        list icmp_type 'time-exceeded'
        list icmp_type 'bad-header'
        list icmp_type 'unknown-header-type'
        list icmp_type 'router-solicitation'
        list icmp_type 'neighbour-solicitation'
        list icmp_type 'router-advertisement'
        list icmp_type 'neighbour-advertisement'
        option limit '1000/sec'
        option family 'ipv6'
        option target 'ACCEPT'

config rule
        option name 'Allow-ICMPv6-Forward'
        option src 'wan'
        option dest '*'
        option proto 'icmp'
        list icmp_type 'echo-request'
        list icmp_type 'echo-reply'
        list icmp_type 'destination-unreachable'
        list icmp_type 'packet-too-big'
        list icmp_type 'time-exceeded'
        list icmp_type 'bad-header'
        list icmp_type 'unknown-header-type'
        option limit '1000/sec'
        option family 'ipv6'
        option target 'ACCEPT'

config rule
        option name 'Allow-IPSec-ESP'
        option src 'wan'
        option dest 'lan'
        option proto 'esp'
        option target 'ACCEPT'

config rule
        option name 'Allow-ISAKMP'
        option src 'wan'
        option dest 'lan'
        option dest_port '500'
        option proto 'udp'
        option target 'ACCEPT'

config rule
        option name 'Support-UDP-Traceroute'
        option src 'wan'
        option dest_port '33434:33689'
        option proto 'udp'
        option family 'ipv4'
        option target 'REJECT'
        option enabled 'false'

config include
        option path '/etc/firewall.user'

You don't have the vlan assigned to a firewall zone. You'll want to do that -- you can create a new firewall zone for the vlan, set it to output = accept, forward = drop, and input = accept (input can be set differently later, if you want). Then allow forwarding from vlan > wan.

At that point, you should be able to route from a host on 192.168.101.0/24 through the WG tunnel and out to the internet.

side note: if the VPN tunnel goes down but the wwan connection remains active, there will still be internet connectivity... if you want a "kill switch", you'll want the VPN in a separate firewall zone.

I've created a new firewall zone for vlan as suggested, and another for the vpn (same rules as wan), and allowed forwarding from vlan > vpn. I've removed forwarding from lan to wan in the attempt to not have lan traffic leak out of wlan0 and curl ipinfo.io now replies with the VPN public IP. Can re-post the config if needed.

I also went ahead with the suggestion of having a physical connection from the switch to WAN2 on the router. This recreated the VLAN configuration on the router and it's now assigned to eth2 (WLAN2) as eth2.101. Oddly enough, it can see the Pi at its static address, although curl ipinfo.io --interface eth2.101 replies with the public IP of the WLAN1 interface.

I've attempted to reconfigure the switch ports from the Pi and to WLAN2 to have the network profile of the VLAN-only network created in the router, as per the Ubiquiti forum thread, although I lose connectivity between the router and the Pi as soon as I change either of the ports.

It seems like there's traffic from both the Pi and WAN1 on eth2.101 if I understand correctly.

Do you mean that you can access the OpenWrt administration (i.e. ssh/LuCI web)? This is expected because the vlan zone's input rule is set to accept. Set that to drop or reject and it will no longer be accessible from that network. Keep in mind that it will also prevent the use of DHCP and DNS on that network (unless you create some additional firewall rules to allow this). If you're not using the OpenWrt Pi as DNS, and if you're setting the WAN2 address as static on the USG, you don't need to do anything more.

Without seeing it, I don't know why this would happen.... try putting another computer directly onto VLAN 101 (via your switch) with a static IP, then see what happens if you google "what's my IP"

The switch port connecting to the Pi needs to be a trunk with VLAN 101 tagged and whatever VLAN you've got for management set as native/untagged.

I'm pretty sure that you need to set VLAN101 should be set as a VLAN only network because the USG isn't "in control" of it, but maybe that is not the case since the USG is actually a client on that VLAN for WAN2. I'd have to play with that (there is another way, I'll explain in a moment). But you need to make sure that the switch port that connects to USG eth2/WAN2 is set to VLAN 101 (tagged) and no other VLANs on that port.

The other way to handle this situation would be to set the switchport that connects to the USG WAN2 port to native/untagged VLAN101, and then set WAN2 on the USG such that it is not using a VLAN. That will, for sure, make it possible to make VLAN 101 a "VLAN only" network from the perspective of Unifi.

Do you mean that you can access the OpenWrt administration (i.e. ssh/LuCI web)?

That's right, ssh and LuCI web are accessible. Changing the vlan zone to input=reject or drop makes OpenWrt inaccessible.

Keep in mind that it will also prevent the use of DHCP and DNS on that network (unless you create some additional firewall rules to allow this). If you're not using the OpenWrt Pi as DNS, and if you're setting the WAN2 address as static on the USG, you don't need to do anything more.

Great, the plan is to use OpenWrt as a VPN secured WWAN without DHCP or DNS (except the 'management' eth0 DHCP client).

try putting another computer directly onto VLAN 101 (via your switch) with a static IP, then see what happens if you google "what's my IP"

I've configured port 3 on the switch to be with the profile of the VLAN-only network. Attached a computer with a static 192.168.101.50 IP and can only see the Pi on 192.168.101.1. Nothing else seems to be accessible.

The switch port connecting to the Pi needs to be a trunk with VLAN 101 tagged and whatever VLAN you've got for management set as native/untagged.

I'm not sure if this is correct, but the Pi switch port is set to Profile: All. There doesn't seem to be a lot more in the port settings through the UI as far as I can see.

The switch port connecting to the Pi needs to be a trunk with VLAN 101 tagged and whatever VLAN you've got for management set as native/untagged.

Assuming that the current configuration is as described, because I can see the Pi on via port 3 using another computer, and can see it as a client to the router's DHCP.

I'm pretty sure that you need to set VLAN101 should be set as a VLAN only network because the USG isn't "in control" of it, but maybe that is not the case since the USG is actually a client on that VLAN for WAN2.

VLAN 101 is set to VLAN-only network in the Unifi settings.

But you need to make sure that the switch port that connects to USG eth2/WAN2 is set to VLAN 101 (tagged) and no other VLANs on that port.

What I find strange is that the USG can only see the Pi if port 9 is set to Profile: All. Right now I have an identical switch port configuration for port 3 that I just tested with another computer and port 9 that goes to the USG. Not getting an answer from the Pi:

ubnt@ubnt:~$ sudo ping 192.168.101.1 -I eth2.101
PING 192.168.101.1 (192.168.101.1) from 192.168.101.2 eth2.101: 56(84) bytes of data.

The other way to handle this situation would be to set the switchport that connects to the USG WAN2 port to native/untagged VLAN101, and then set WAN2 on the USG such that it is not using a VLAN. That will, for sure, make it possible to make VLAN 101 a "VLAN only" network from the perspective of Unifi.

Maybe that is the way to go, considering the Pi can route traffic, although the Unifi forums thread suggests that the configuration there has all ports set to VLAN 200 as far as I understand.

That is expected and, IIUC, your goal (on VLAN 101). Are you saying that it becomes inaccessible on the other VLAN?

Did you set the gateway to 192.68.101.1, DNS to something valid (like 8.8.8.8, for example), and the subnet mask to /24 (255.255.255.0)?

This should be fine, although you need to ensure that the desired management network is the native network in that profile. You may also have unnecessary VLANs on that port, but that wouldn't stop things from working.

Good. You should be able to access the Pi using the address that the router's DHCP server has assigned (provided that your computer is connected to the management VLAN; if you're on another VLAN, you need to make sure you have the firewall set appropriately -- best to test while you're connected to the same network).

good.

Let's avoid testing from the USG directly. I'd recommend using a computer on each of the VLANs rather than the USG. This will make things more flexible and easier to understand the routing/switching.

I don't know which thread you're looking at (I quit the UI forums a while ago because... reasons). But that is surely just one way that this can be achieved... there are many valid ways to do this.

Let's actually get to some specifics here since I think it'll make the rest of the troubleshooting easier:

  • What switch port connects to the Pi?

  • What switch port connects to the WAN2 USG port?

  • What is the management network (VLAN ID and subnet)?

  • What is the IP address of the Pi on the management network?

  • Can you setup 2 switch ports as follows:
    -- port A: untagged/native for VLAN 101
    -- port B: untagged/native for your management network
    ----> then tell me the port numbers that correspond to A and B. the idea here is that we can easily switch a computer between these two networks simply by switching the port being used. This will give us consistent platform for testing each network.

EDIT: can you also post the latest config files. And add to that the output of wg show

Only tested through VLAN 101, and was just confirming that modifying the vlan firewall zone makes it inaccessible.

This was key - I created a new port profile that has the default management network as the native network and VLAN 101 as the tagged network. Assigning it to port 9 which goes to the USG made it start seeing the Pi.

It's bridged via an AP to AP mesh going in port 6 of the switch.

Port 9 of the switch goes to WAN2.

I assume this is 'Default' network with VLAN ID 1 and subnet 192.168.100.0/24. The Pi is at 192.168.100.13.

Port A is 1
Port B is 2

# cat /etc/config/network

config interface 'loopback'
       option device 'lo'
       option proto 'static'
       option ipaddr '127.0.0.1'
       option netmask '255.0.0.0'

config globals 'globals'
       option ula_prefix 'fde4:81d6:977c::/48'

config interface 'lan'
       option proto 'dhcp'
       option delegate '0'
       option device 'eth0'
       option defaultroute '0'

config device
       option name 'wlan0'
       option ipv6 '0'

config interface 'wwan'
       option proto 'dhcp'
       option hostname '*'
       option peerdns '0'
       option delegate '0'
       list dns '1.1.1.1'

config interface 'vlan'
       option device 'eth0.101'
       option proto 'static'
       option ipaddr '192.168.101.1'
       option netmask '255.255.255.0'
       option delegate '0'

config interface 'vpn'
       option proto 'wireguard'
       option force_link '1'
       option private_key '***'
       list addresses '10.0.0.200/32'
       option delegate '0'
       option defaultroute '0'

config wireguard_vpn
       option public_key '***'
       list allowed_ips '0.0.0.0/0'
       option endpoint_host '185.0.0.60'
       option persistent_keepalive '25'

config device
       option type '8021q'
       option ifname 'eth0'
       option vid '101'
       option name 'eth0.101'
# cat /etc/config/wireless

config wifi-device 'radio0'
        option type 'mac80211'
        option path 'platform/soc/fe300000.mmcnr/mmc_host/mmc1/mmc1:0001/mmc1:0001:1'
        option channel '36'
        option band '5g'
        option htmode 'VHT80'
        option cell_density '0'

config wifi-iface 'wifinet1'
        option device 'radio0'
        option mode 'sta'
        option ssid 'AP'
        option encryption 'psk2'
        option key 'password'
        option network 'wwan'
# cat /etc/config/dhcp

config dnsmasq
        option domainneeded '1'
        option boguspriv '1'
        option filterwin2k '0'
        option localise_queries '1'
        option rebind_protection '1'
        option rebind_localhost '1'
        option local '/lan/'
        option domain 'lan'
        option expandhosts '1'
        option nonegcache '0'
        option authoritative '1'
        option readethers '1'
        option leasefile '/tmp/dhcp.leases'
        option resolvfile '/tmp/resolv.conf.d/resolv.conf.auto'
        option nonwildcard '1'
        option localservice '1'
        option ednspacket_max '1232'

config dhcp 'lan'
        option interface 'lan'
        option start '100'
        option limit '150'
        option leasetime '12h'
        option dhcpv4 'server'
        list ra_flags 'managed-config'
        list ra_flags 'other-config'
        option ra 'hybrid'
        option dhcpv6 'hybrid'

config dhcp 'wan'
        option interface 'wan'
        option ignore '1'

config odhcpd 'odhcpd'
        option maindhcp '0'
        option leasefile '/tmp/hosts/odhcpd'
        option leasetrigger '/usr/sbin/odhcpd-update'
        option loglevel '4'

config dhcp 'vlan'
        option interface 'vlan'
        option ignore '1'
        option start '100'
        option limit '150'
        option leasetime '12h'
        list ra_flags 'none'
# cat /etc/config/firewall

config defaults
        option input 'ACCEPT'
        option output 'ACCEPT'
        option forward 'REJECT'
        option synflood_protect '1'

config zone 'lan'
        option name 'lan'
        option output 'ACCEPT'
        list network 'lan'
        option forward 'REJECT'
        option input 'ACCEPT'

config zone 'wan'
        option name 'wan'
        option output 'ACCEPT'
        option forward 'REJECT'
        option masq '1'
        option mtu_fix '1'
        option input 'REJECT'
        list network 'wwan'

config rule
        option name 'Allow-DHCP-Renew'
        option src 'wan'
        option proto 'udp'
        option dest_port '68'
        option target 'ACCEPT'
        option family 'ipv4'

config rule
        option name 'Allow-Ping'
        option src 'wan'
        option proto 'icmp'
        option icmp_type 'echo-request'
        option family 'ipv4'
        option target 'ACCEPT'

config rule
        option name 'Allow-IGMP'
        option src 'wan'
        option proto 'igmp'
        option family 'ipv4'
        option target 'ACCEPT'

config rule
        option name 'Allow-DHCPv6'
        option src 'wan'
        option proto 'udp'
        option src_ip 'fc00::/6'
        option dest_ip 'fc00::/6'
        option dest_port '546'
        option family 'ipv6'
        option target 'ACCEPT'

config rule
        option name 'Allow-MLD'
        option src 'wan'
        option proto 'icmp'
        option src_ip 'fe80::/10'
        list icmp_type '130/0'
        list icmp_type '131/0'
        list icmp_type '132/0'
        list icmp_type '143/0'
        option family 'ipv6'
        option target 'ACCEPT'

config rule
        option name 'Allow-ICMPv6-Input'
        option src 'wan'
        option proto 'icmp'
        list icmp_type 'echo-request'
        list icmp_type 'echo-reply'
        list icmp_type 'destination-unreachable'
        list icmp_type 'packet-too-big'
        list icmp_type 'time-exceeded'
        list icmp_type 'bad-header'
        list icmp_type 'unknown-header-type'
        list icmp_type 'router-solicitation'
        list icmp_type 'neighbour-solicitation'
        list icmp_type 'router-advertisement'
        list icmp_type 'neighbour-advertisement'
        option limit '1000/sec'
        option family 'ipv6'
        option target 'ACCEPT'

config rule
        option name 'Allow-ICMPv6-Forward'
        option src 'wan'
        option dest '*'
        option proto 'icmp'
        list icmp_type 'echo-request'
        list icmp_type 'echo-reply'
        list icmp_type 'destination-unreachable'
        list icmp_type 'packet-too-big'
        list icmp_type 'time-exceeded'
        list icmp_type 'bad-header'
        list icmp_type 'unknown-header-type'
        option limit '1000/sec'
        option family 'ipv6'
        option target 'ACCEPT'

config rule
        option name 'Allow-IPSec-ESP'
        option src 'wan'
        option dest 'lan'
        option proto 'esp'
        option target 'ACCEPT'

config rule
        option name 'Allow-ISAKMP'
        option src 'wan'
        option dest 'lan'
        option dest_port '500'
        option proto 'udp'
        option target 'ACCEPT'

config rule
        option name 'Support-UDP-Traceroute'
        option src 'wan'
        option dest_port '33434:33689'
        option proto 'udp'
        option family 'ipv4'
        option target 'REJECT'
        option enabled 'false'

config include
        option path '/etc/firewall.user'

config zone
        option name 'vlan'
        option output 'DROP'
        option forward 'ACCEPT'
        list network 'vlan'
        option input 'ACCEPT'

config zone
        option name 'vpn'
        option input 'REJECT'
        option output 'ACCEPT'
        option forward 'REJECT'
        list network 'vpn'

config forwarding
        option src 'vlan'
        option dest 'vpn'
# wg show
interface: vpn
  public key: ***
  private key: (hidden)
  listening port: 44768

peer: ***
  endpoint: 185.0.0.60:51820
  allowed ips: 0.0.0.0/0
  latest handshake: 26 seconds ago
  transfer: 11.77 KiB received, 33.46 KiB sent
  persistent keepalive: every 25 seconds

Ok... good.

On port 9, you do not want the management network at all since that physical port should only carry the 2nd WAN (i.e. VLAN 101). Create a profile for VLAN 101 tagged, no untagged networks and apply that to port 9 (or alternatively, use the VLAN 101 native profile and then remove the VLAN assignment on WAN2).

Test #1: So when your computer is plugged into port 1 and you set the IP manually (as per your graphic above), you are able to ping 192.168.101.1 but you cannot ping 1.1.1.1?

If that is the case, add this line to the WG configuration (in the peer stanza):
option route_allowed_ips '1'

In theory, you should have access to the internet via the Pi/WG/public wifi, and you should be able to ping 1.1.1.1 (try other sites, too, and see if you get valid domain resolution).

Test #2: When plugged into port 2, your computer will use an address on 192.168.100.0/24. From here, you should have internet access via the USG (assuming that this network is allowed to reach the internet per the USG firewall rules). You should be able to reach the pi @ 192.168.100.13.

Let me know what you see from those tests.

Had a mistake on the configuration for port 9 - VLAN 101 was set as the native network, which I think means the Pi would have been visible on eth2 instead of eth2.101. When I removed management from the port configuration and set it to VLAN 101 tagged only, the Pi became visible from the USG on eth2.101.

Correct.

I've added this through the UI, which amended /etc/config/network and appended option route_allowed_ips '1' to the config wireguard_vpn block. Restarted OpenWrt after that.

Unfortunately, with this change, devices on 192.168.101.0/24 can still only see the Pi on 192.168.101.1 and can't ping any public network IPs or sites.

Correct.

Let's take a look at the latest config files.

Also, it might be worth testing the Pi's routing with the WG tunnel stopped. This means allowing forwarding from vlan > wan (this will obviously bypass the WG tunnel, but will help us understand if the issue is related to WG or something else happening in the configuration).

# cat /etc/config/network

config interface 'loopback'
        option device 'lo'
        option proto 'static'
        option ipaddr '127.0.0.1'
        option netmask '255.0.0.0'

config globals 'globals'
        option ula_prefix 'fde4:81d6:977c::/48'

config interface 'lan'
        option proto 'dhcp'
        option delegate '0'
        option device 'eth0'
        option defaultroute '0'

config device
        option name 'wlan0'
        option ipv6 '0'

config interface 'wwan'
        option proto 'dhcp'
        option hostname '*'
        option peerdns '0'
        option delegate '0'
        list dns '1.1.1.1'

config interface 'vlan'
        option device 'eth0.101'
        option proto 'static'
        option ipaddr '192.168.101.1'
        option netmask '255.255.255.0'
        option delegate '0'

config interface 'vpn'
        option proto 'wireguard'
        option force_link '1'
        option private_key '***'
        list addresses '10.0.0.200/32'
        option delegate '0'
        option defaultroute '0'
        option peerdns '0'
        list dns '1.1.1.1'

config wireguard_vpn
        option public_key '***'
        list allowed_ips '0.0.0.0/0'
        option endpoint_host '185.0.0.60'
        option persistent_keepalive '25'
        option route_allowed_ips '1'

config device
        option type '8021q'
        option ifname 'eth0'
        option vid '101'
        option name 'eth0.101'
# cat /etc/config/wireless

config wifi-device 'radio0'
        option type 'mac80211'
        option path 'platform/soc/fe300000.mmcnr/mmc_host/mmc1/mmc1:0001/mmc1:0001:1'
        option channel '36'
        option band '5g'
        option htmode 'VHT80'
        option cell_density '0'

config wifi-iface 'wifinet1'
        option device 'radio0'
        option mode 'sta'
        option ssid 'AP'
        option encryption 'psk2'
        option key 'password'
        option network 'wwan'
# cat /etc/config/dhcp

config dnsmasq
        option domainneeded '1'
        option boguspriv '1'
        option filterwin2k '0'
        option localise_queries '1'
        option rebind_protection '1'
        option rebind_localhost '1'
        option local '/lan/'
        option domain 'lan'
        option expandhosts '1'
        option nonegcache '0'
        option authoritative '1'
        option readethers '1'
        option leasefile '/tmp/dhcp.leases'
        option resolvfile '/tmp/resolv.conf.d/resolv.conf.auto'
        option nonwildcard '1'
        option localservice '1'
        option ednspacket_max '1232'

config dhcp 'lan'
        option interface 'lan'
        option start '100'
        option limit '150'
        option leasetime '12h'
        option dhcpv4 'server'
        list ra_flags 'managed-config'
        list ra_flags 'other-config'
        option ra 'hybrid'
        option dhcpv6 'hybrid'

config dhcp 'wan'
        option interface 'wan'
        option ignore '1'

config odhcpd 'odhcpd'
        option maindhcp '0'
        option leasefile '/tmp/hosts/odhcpd'
        option leasetrigger '/usr/sbin/odhcpd-update'
        option loglevel '4'

config dhcp 'vlan'
        option interface 'vlan'
        option ignore '1'
        option start '100'
        option limit '150'
        option leasetime '12h'
        list ra_flags 'none'
# cat /etc/config/firewall

config defaults
        option input 'ACCEPT'
        option output 'ACCEPT'
        option forward 'REJECT'
        option synflood_protect '1'

config zone 'lan'
        option name 'lan'
        option output 'ACCEPT'
        list network 'lan'
        option forward 'REJECT'
        option input 'ACCEPT'

config zone 'wan'
        option name 'wan'
        option output 'ACCEPT'
        option forward 'REJECT'
        option masq '1'
        option mtu_fix '1'
        option input 'REJECT'
        list network 'wwan'

config rule
        option name 'Allow-DHCP-Renew'
        option src 'wan'
        option proto 'udp'
        option dest_port '68'
        option target 'ACCEPT'
        option family 'ipv4'

config rule
        option name 'Allow-Ping'
        option src 'wan'
        option proto 'icmp'
        option icmp_type 'echo-request'
        option family 'ipv4'
        option target 'ACCEPT'

config rule
        option name 'Allow-IGMP'
        option src 'wan'
        option proto 'igmp'
        option family 'ipv4'
        option target 'ACCEPT'

config rule
        option name 'Allow-DHCPv6'
        option src 'wan'
        option proto 'udp'
        option src_ip 'fc00::/6'
        option dest_ip 'fc00::/6'
        option dest_port '546'
        option family 'ipv6'
        option target 'ACCEPT'

config rule
        option name 'Allow-MLD'
        option src 'wan'
        option proto 'icmp'
        option src_ip 'fe80::/10'
        list icmp_type '130/0'
        list icmp_type '131/0'
        list icmp_type '132/0'
        list icmp_type '143/0'
        option family 'ipv6'
        option target 'ACCEPT'

config rule
        option name 'Allow-ICMPv6-Input'
        option src 'wan'
        option proto 'icmp'
        list icmp_type 'echo-request'
        list icmp_type 'echo-reply'
        list icmp_type 'destination-unreachable'
        list icmp_type 'packet-too-big'
        list icmp_type 'time-exceeded'
        list icmp_type 'bad-header'
        list icmp_type 'unknown-header-type'
        list icmp_type 'router-solicitation'
        list icmp_type 'neighbour-solicitation'
        list icmp_type 'router-advertisement'
        list icmp_type 'neighbour-advertisement'
        option limit '1000/sec'
        option family 'ipv6'
        option target 'ACCEPT'

config rule
        option name 'Allow-ICMPv6-Forward'
        option src 'wan'
        option dest '*'
        option proto 'icmp'
        list icmp_type 'echo-request'
        list icmp_type 'echo-reply'
        list icmp_type 'destination-unreachable'
        list icmp_type 'packet-too-big'
        list icmp_type 'time-exceeded'
        list icmp_type 'bad-header'
        list icmp_type 'unknown-header-type'
        option limit '1000/sec'
        option family 'ipv6'
        option target 'ACCEPT'

config rule
        option name 'Allow-IPSec-ESP'
        option src 'wan'
        option dest 'lan'
        option proto 'esp'
        option target 'ACCEPT'

config rule
        option name 'Allow-ISAKMP'
        option src 'wan'
        option dest 'lan'
        option dest_port '500'
        option proto 'udp'
        option target 'ACCEPT'

config rule
        option name 'Support-UDP-Traceroute'
        option src 'wan'
        option dest_port '33434:33689'
        option proto 'udp'
        option family 'ipv4'
        option target 'REJECT'
        option enabled 'false'

config include
        option path '/etc/firewall.user'

config zone
        option name 'vlan'
        option output 'DROP'
        option forward 'ACCEPT'
        list network 'vlan'
        option input 'ACCEPT'

config zone
        option name 'vpn'
        option input 'REJECT'
        option output 'ACCEPT'
        option forward 'REJECT'
        list network 'vpn'

config forwarding
        option src 'vlan'
        option dest 'vpn'

Changing the firewall to allow forwarding from vlan > wan instead of vlan > vpn makes /etc/config/firewall look like:

# cat /etc/config/firewall

config defaults
        option input 'ACCEPT'
        option output 'ACCEPT'
        option forward 'REJECT'
        option synflood_protect '1'

config zone 'lan'
        option name 'lan'
        option output 'ACCEPT'
        list network 'lan'
        option forward 'REJECT'
        option input 'ACCEPT'

config zone 'wan'
        option name 'wan'
        option output 'ACCEPT'
        option forward 'REJECT'
        option masq '1'
        option mtu_fix '1'
        option input 'REJECT'
        list network 'wwan'

config rule
        option name 'Allow-DHCP-Renew'
        option src 'wan'
        option proto 'udp'
        option dest_port '68'
        option target 'ACCEPT'
        option family 'ipv4'

config rule
        option name 'Allow-Ping'
        option src 'wan'
        option proto 'icmp'
        option icmp_type 'echo-request'
        option family 'ipv4'
        option target 'ACCEPT'

config rule
        option name 'Allow-IGMP'
        option src 'wan'
        option proto 'igmp'
        option family 'ipv4'
        option target 'ACCEPT'

config rule
        option name 'Allow-DHCPv6'
        option src 'wan'
        option proto 'udp'
        option src_ip 'fc00::/6'
        option dest_ip 'fc00::/6'
        option dest_port '546'
        option family 'ipv6'
        option target 'ACCEPT'

config rule
        option name 'Allow-MLD'
        option src 'wan'
        option proto 'icmp'
        option src_ip 'fe80::/10'
        list icmp_type '130/0'
        list icmp_type '131/0'
        list icmp_type '132/0'
        list icmp_type '143/0'
        option family 'ipv6'
        option target 'ACCEPT'

config rule
        option name 'Allow-ICMPv6-Input'
        option src 'wan'
        option proto 'icmp'
        list icmp_type 'echo-request'
        list icmp_type 'echo-reply'
        list icmp_type 'destination-unreachable'
        list icmp_type 'packet-too-big'
        list icmp_type 'time-exceeded'
        list icmp_type 'bad-header'
        list icmp_type 'unknown-header-type'
        list icmp_type 'router-solicitation'
        list icmp_type 'neighbour-solicitation'
        list icmp_type 'router-advertisement'
        list icmp_type 'neighbour-advertisement'
        option limit '1000/sec'
        option family 'ipv6'
        option target 'ACCEPT'

config rule
        option name 'Allow-ICMPv6-Forward'
        option src 'wan'
        option dest '*'
        option proto 'icmp'
        list icmp_type 'echo-request'
        list icmp_type 'echo-reply'
        list icmp_type 'destination-unreachable'
        list icmp_type 'packet-too-big'
        list icmp_type 'time-exceeded'
        list icmp_type 'bad-header'
        list icmp_type 'unknown-header-type'
        option limit '1000/sec'
        option family 'ipv6'
        option target 'ACCEPT'

config rule
        option name 'Allow-IPSec-ESP'
        option src 'wan'
        option dest 'lan'
        option proto 'esp'
        option target 'ACCEPT'

config rule
        option name 'Allow-ISAKMP'
        option src 'wan'
        option dest 'lan'
        option dest_port '500'
        option proto 'udp'
        option target 'ACCEPT'

config rule
        option name 'Support-UDP-Traceroute'
        option src 'wan'
        option dest_port '33434:33689'
        option proto 'udp'
        option family 'ipv4'
        option target 'REJECT'
        option enabled 'false'

config include
        option path '/etc/firewall.user'

config zone
        option name 'vlan'
        option output 'DROP'
        option forward 'ACCEPT'
        list network 'vlan'
        option input 'ACCEPT'

config zone
        option name 'vpn'
        option input 'REJECT'
        option output 'ACCEPT'
        option forward 'REJECT'
        list network 'vpn'

config forwarding
        option src 'vlan'
        option dest 'wan'

After rebooting OpenWrt, it appears that the USG can ping the public internet over eth2.101.

ubnt@ubnt:~$ sudo ping 1.1.1.1 -I eth2.101
PING 1.1.1.1 (1.1.1.1) from 192.168.101.2 eth2.101: 56(84) bytes of data.
64 bytes from 1.1.1.1: icmp_req=2 ttl=52 time=202 ms
64 bytes from 1.1.1.1: icmp_req=3 ttl=52 time=246 ms

Setting the Load Balancing to "Distributed" on the USG makes it possible to curl through both eth0 and eth2.101.

ubnt@ubnt:~$ sudo curl ipinfo.io --interface eth0
{
  "ip": "WAN1-IP",
...
ubnt@ubnt:~$ sudo curl ipinfo.io --interface eth2.101
{
  "ip": "WAN2-IP",

This, as expected goes through the wan firewall group on OpenWrt and not through WG. Makes me wonder if I missed to forward vpn to wan in the firewall.

This is exactly backwards of what it should be....
input = drop or reject
forward = drop or reject
output = accept

So now it appears that you are able to get internet access via the pi. This means that maybe something is wrong with the WG setup.

I'd recommend using a computer or a mobile device with WG installed to test the 'raw' connectivity of that setup... stop the WG interface on your OpenWrt side, and then use the same WG config (private key, public key for the remote side, and the same interface IP address) to verify that that WG config is working as expected. This will effectively remove all OpenWrt variables from the equation, and if it doesn't work, it means that the problem now resides with the WG side of things.