Site-to-Site + PBR with one end behind NAT

I'm struggling with configuring firewall settings as I try to achieve a scenario:

2 sites:

  • (Site A) router is directly connected to the internet
  • (Site B) router is NAT'd with no ability to do port forwarding to it.

Via a wireguard tunnel, I want to be able to site-to-site access each other's LAN, enable policy routing from Site A --> Site B, and have Site B's tunnel interface initiate the handshake/connection (due to the inability to open ports on it's upstream gateway router).

I've tried doing the S2S setup, with both tunnel endpoint interfaces in their respective LAN fw zones and the peer's LAN subnet (+/- 0.0.0.0/0) in the allowed ips of the peer config. They could access each other's LAN, pinging from a client on one end through to a client on the other. But policy routing does not work from either end.

Moving Site B's wg interface to the WAN fw zone + adding a gateway metric (for pbr), I have access to Site A's LAN and can successfully policy route through Site A with an odd caveat- a client at Site B can ping/ssh/etc clients at Site A, but the Site B router cannot do the same.

But in the diretion that I truly need, I get almost nothing:
Site A clients cannot access the Site B network (can't so much as ping the Site B router), nor policy route to it.
Site A's router can ping the Site B router's address (lan@192.168.43.1, wg@192.168.9.2), but not clients on the Site B lan, and ssh to either dropbear instance on it doesn't work:

  • passworded port 22: gets connection refused,
  • pubkey-only port 2222 that is wan>lan port forwarded: times-out

I feel like I'm blind to the solution (is it setup a 'vpn' fw zone and the like? bunch o' traffic rules?), so I'm hoping someone can hand hold me a little bit.

Currently:

Site A

network.lan=interface
network.lan.proto='static'
network.lan.ipaddr='192.168.1.1'
network.lan.netmask='255.255.255.0'
network.lan.device='br-lan'
network.lan.delegate='0'
network.wg_s2s_a=interface
network.wg_s2s_a.proto='wireguard'
network.wg_s2s_a.private_key=''
network.wg_s2s_a.listen_port='51840'
network.wg_s2s_a.addresses='192.168.9.1/24'
network.s2s_vpn_site_b=wireguard_wg_s2s_a
network.s2s_vpn_site_b.public_key=''
network.s2s_vpn_site_b.preshared_key=''
network.s2s_vpn_site_b.description='Site B'
network.s2s_vpn_site_b.route_allowed_ips='1'
network.s2s_vpn_site_b.persistent_keepalive='25'
network.s2s_vpn_site_b.allowed_ips='192.168.9.2/32' '192.168.43.0/24' '0.0.0.0/0'

firewall.@defaults[0]=defaults
firewall.@defaults[0].input='ACCEPT'
firewall.@defaults[0].output='ACCEPT'
firewall.@defaults[0].forward='REJECT'
firewall.@defaults[0].synflood_protect='1'
firewall.@zone[0]=zone
firewall.@zone[0].name='lan'
firewall.@zone[0].input='ACCEPT'
firewall.@zone[0].output='ACCEPT'
firewall.@zone[0].forward='ACCEPT'
firewall.@zone[0].network='lan'
firewall.@zone[1]=zone
firewall.@zone[1].name='wan'
firewall.@zone[1].input='REJECT'
firewall.@zone[1].output='ACCEPT'
firewall.@zone[1].forward='REJECT'
firewall.@zone[1].masq='1'
firewall.@zone[1].mtu_fix='1'
firewall.@zone[1].network='wan' 'wan6' 'wg_s2s_a'
firewall.@forwarding[0]=forwarding
firewall.@forwarding[0].src='lan'
firewall.@forwarding[0].dest='wan'
firewall.@rule[0]=rule
firewall.@rule[0].name='Allow-DHCP-Renew'
firewall.@rule[0].src='wan'
firewall.@rule[0].proto='udp'
firewall.@rule[0].dest_port='68'
firewall.@rule[0].target='ACCEPT'
firewall.@rule[1]=rule
firewall.@rule[1].name='Allow-Ping'
firewall.@rule[1].src='wan'
firewall.@rule[1].proto='icmp'
firewall.@rule[1].icmp_type='echo-request'
firewall.@rule[1].family='ipv4'
firewall.@rule[1].target='ACCEPT'
firewall.@rule[2]=rule
firewall.@rule[2].name='Allow-IGMP'
firewall.@rule[2].src='wan'
firewall.@rule[2].proto='igmp'
firewall.@rule[2].family='ipv4'
firewall.@rule[2].target='ACCEPT'
firewall.@rule[3]=rule
firewall.@rule[3].name='Allow-DHCPv6'
firewall.@rule[3].src='wan'
firewall.@rule[3].proto='udp'
firewall.@rule[3].dest_port='546'
firewall.@rule[3].family='ipv6'
firewall.@rule[3].target='ACCEPT'
firewall.@rule[4]=rule
firewall.@rule[4].name='Allow-MLD'
firewall.@rule[4].src='wan'
firewall.@rule[4].proto='icmp'
firewall.@rule[4].src_ip='fe80::/10'
firewall.@rule[4].icmp_type='130/0' '131/0' '132/0' '143/0'
firewall.@rule[4].family='ipv6'
firewall.@rule[4].target='ACCEPT'
firewall.@rule[5]=rule
firewall.@rule[5].name='Allow-ICMPv6-Input'
firewall.@rule[5].src='wan'
firewall.@rule[5].proto='icmp'
firewall.@rule[5].icmp_type='echo-request' 'echo-reply' 'destination-unreachable' 'packet-too-big' 'time-exceeded' 'bad-header' 'unknown-header-type' 'router-solicitation' 'neighbour-solicitation' 'router-advertisement' 'neighbour-advertisement'
firewall.@rule[5].limit='1000/sec'
firewall.@rule[5].family='ipv6'
firewall.@rule[5].target='ACCEPT'
firewall.@rule[6]=rule
firewall.@rule[6].name='Allow-ICMPv6-Forward'
firewall.@rule[6].src='wan'
firewall.@rule[6].dest='*'
firewall.@rule[6].proto='icmp'
firewall.@rule[6].icmp_type='echo-request' 'echo-reply' 'destination-unreachable' 'packet-too-big' 'time-exceeded' 'bad-header' 'unknown-header-type'
firewall.@rule[6].limit='1000/sec'
firewall.@rule[6].family='ipv6'
firewall.@rule[6].target='ACCEPT'
firewall.@rule[7]=rule
firewall.@rule[7].name='Allow-IPSec-ESP'
firewall.@rule[7].src='wan'
firewall.@rule[7].dest='lan'
firewall.@rule[7].proto='esp'
firewall.@rule[7].target='ACCEPT'
firewall.@rule[8]=rule
firewall.@rule[8].name='Allow-ISAKMP'
firewall.@rule[8].src='wan'
firewall.@rule[8].dest='lan'
firewall.@rule[8].dest_port='500'
firewall.@rule[8].proto='udp'
firewall.@rule[8].target='ACCEPT'
firewall.@redirect[0]=redirect
firewall.@redirect[0].dest='lan'
firewall.@redirect[0].target='DNAT'
firewall.@redirect[0].name='SSH_Ext-2233'
firewall.@redirect[0].proto='tcp'
firewall.@redirect[0].src='wan'
firewall.@redirect[0].dest_ip='192.168.1.1'
firewall.@redirect[0].src_dport='2222'
firewall.@redirect[0].dest_port='2222'
firewall.pbr=include
firewall.pbr.fw4_compatible='1'
firewall.pbr.type='script'
firewall.pbr.path='/usr/share/pbr/pbr.firewall.include'
firewall.wg_s2s_51840=rule
firewall.wg_s2s_51840.name='Allow-WireGuard-51840'
firewall.wg_s2s_51840.src='wan'
firewall.wg_s2s_51840.dest_port='51840'
firewall.wg_s2s_51840.proto='udp'
firewall.wg_s2s_51840.target='ACCEPT'

Site B

network.lan.device='br-lan'
network.lan.proto='static'
network.lan.ipaddr='192.168.43.1'
network.lan.netmask='255.255.255.0'
network.lan.ip6assign='60'
network.lan.ports='lan'
network.wg_s2s_b=interface
network.wg_s2s_b.proto='wireguard'
network.wg_s2s_b.private_key=''
network.wg_s2s_b.listen_port='51840'
network.wg_s2s_b.addresses='192.168.9.2/24'
network.wg_s2s_b.metric='20'
network.s2s_vpn_site_a=wireguard_wg_s2s_b
network.s2s_vpn_site_a.public_key=''
network.s2s_vpn_site_a.preshared_key=''
network.s2s_vpn_site_a.description='Site A'
network.s2s_vpn_site_a.persistent_keepalive='25'
network.s2s_vpn_site_a.endpoint_host='sitea.goeshere.com'
network.s2s_vpn_site_a.endpoint_port='51840'
network.s2s_vpn_site_a.route_allowed_ips='1'
network.s2s_vpn_site_a.allowed_ips='0.0.0.0/0'

firewall.@defaults[0]=defaults
firewall.@defaults[0].input='ACCEPT'
firewall.@defaults[0].output='ACCEPT'
firewall.@defaults[0].forward='REJECT'
firewall.@defaults[0].synflood_protect='1'
firewall.@zone[0]=zone
firewall.@zone[0].name='lan'
firewall.@zone[0].input='ACCEPT'
firewall.@zone[0].output='ACCEPT'
firewall.@zone[0].forward='ACCEPT'
firewall.@zone[0].network='lan'
firewall.@zone[1]=zone
firewall.@zone[1].name='wan'
firewall.@zone[1].input='REJECT'
firewall.@zone[1].output='ACCEPT'
firewall.@zone[1].forward='REJECT'
firewall.@zone[1].masq='1'
firewall.@zone[1].mtu_fix='1'
firewall.@zone[1].network='wan' 'wan6' 'wgclient' 'wg_s2s_b' 'wwan'
firewall.@forwarding[0]=forwarding
firewall.@forwarding[0].src='lan'
firewall.@forwarding[0].dest='wan'
firewall.@rule[0]=rule
firewall.@rule[0].name='Allow-DHCP-Renew'
firewall.@rule[0].src='wan'
firewall.@rule[0].proto='udp'
firewall.@rule[0].dest_port='68'
firewall.@rule[0].target='ACCEPT'
firewall.@rule[0].family='ipv4'
firewall.@rule[1]=rule
firewall.@rule[1].name='Allow-Ping'
firewall.@rule[1].src='wan'
firewall.@rule[1].proto='icmp'
firewall.@rule[1].icmp_type='echo-request'
firewall.@rule[1].family='ipv4'
firewall.@rule[1].target='ACCEPT'
firewall.@rule[2]=rule
firewall.@rule[2].name='Allow-IGMP'
firewall.@rule[2].src='wan'
firewall.@rule[2].proto='igmp'
firewall.@rule[2].family='ipv4'
firewall.@rule[2].target='ACCEPT'
firewall.@rule[3]=rule
firewall.@rule[3].name='Allow-DHCPv6'
firewall.@rule[3].src='wan'
firewall.@rule[3].proto='udp'
firewall.@rule[3].dest_port='546'
firewall.@rule[3].family='ipv6'
firewall.@rule[3].target='ACCEPT'
firewall.@rule[4]=rule
firewall.@rule[4].name='Allow-MLD'
firewall.@rule[4].src='wan'
firewall.@rule[4].proto='icmp'
firewall.@rule[4].src_ip='fe80::/10'
firewall.@rule[4].icmp_type='130/0' '131/0' '132/0' '143/0'
firewall.@rule[4].family='ipv6'
firewall.@rule[4].target='ACCEPT'
firewall.@rule[5]=rule
firewall.@rule[5].name='Allow-ICMPv6-Input'
firewall.@rule[5].src='wan'
firewall.@rule[5].proto='icmp'
firewall.@rule[5].icmp_type='echo-request' 'echo-reply' 'destination-unreachable' 'packet-too-big' 'time-exceeded' 'bad-header' 'unknown-header-type' 'router-solicitation' 'neighbour-solicitation' 'router-advertisement' 'neighbour-advertisement'
firewall.@rule[5].limit='1000/sec'
firewall.@rule[5].family='ipv6'
firewall.@rule[5].target='ACCEPT'
firewall.@rule[6]=rule
firewall.@rule[6].name='Allow-ICMPv6-Forward'
firewall.@rule[6].src='wan'
firewall.@rule[6].dest='*'
firewall.@rule[6].proto='icmp'
firewall.@rule[6].icmp_type='echo-request' 'echo-reply' 'destination-unreachable' 'packet-too-big' 'time-exceeded' 'bad-header' 'unknown-header-type'
firewall.@rule[6].limit='1000/sec'
firewall.@rule[6].family='ipv6'
firewall.@rule[6].target='ACCEPT'
firewall.@rule[7]=rule
firewall.@rule[7].name='Allow-IPSec-ESP'
firewall.@rule[7].src='wan'
firewall.@rule[7].dest='lan'
firewall.@rule[7].proto='esp'
firewall.@rule[7].target='ACCEPT'
firewall.@rule[8]=rule
firewall.@rule[8].name='Allow-ISAKMP'
firewall.@rule[8].src='wan'
firewall.@rule[8].dest='lan'
firewall.@rule[8].dest_port='500'
firewall.@rule[8].proto='udp'
firewall.@rule[8].target='ACCEPT'
firewall.@redirect[0]=redirect
firewall.@redirect[0].dest='lan'
firewall.@redirect[0].target='DNAT'
firewall.@redirect[0].name='SSH_Ext'
firewall.@redirect[0].family='ipv4'
firewall.@redirect[0].proto='tcp'
firewall.@redirect[0].src='wan'
firewall.@redirect[0].src_dport='2222'
firewall.@redirect[0].dest_ip='192.168.43.1'
firewall.@redirect[0].dest_port='2222'
firewall.pbr=include
firewall.pbr.fw4_compatible='1'
firewall.pbr.type='script'
firewall.pbr.path='/usr/share/pbr/pbr.firewall.include'
firewall.wg_s2s_51840=rule
firewall.wg_s2s_51840.name='Allow-WireGuard-51840'
firewall.wg_s2s_51840.src='wan'
firewall.wg_s2s_51840.dest_port='51840'
firewall.wg_s2s_51840.proto='udp'
firewall.wg_s2s_51840.target='ACCEPT'

What do you want each site to be able to access at the other end? Is it just the respective LANs? Or do you want one site using the internet access from the other end as well?

What 'policy-based routing' are you doing/intending to do?

2 Likes

For proper access the WG interfaces have to be placed in the lan firewall zones.

The 0.0.0.0/0 should probably be removed

I hope with these changes you can have bidirectional traffic.

On site B you have

network.s2s_vpn_site_a.allowed_ips='0.0.0.0/0'

Not sure if this is what you want now all traffic goes to site A, for a site-to-site setup where you only want to be able to have bi-drectional traffic and use your own local internet gateway change it in WG address of the other side and the LAN subnet of the other side e.g.: 192.168.9.1/32, 192.168.1.0/24

At minimum, Site A can access Site B's lan as well as route traffic through Site B. Bonus if Site B can do the same back to/through Site A.

Basically, I have two locations I go between- call it Site A/cabin and Site B/home. Cabin has it's own public ip, Home is behind a NAT connection. It'd be nice if I can access the cabin network when I'm at home, but I need to access the home net while I'm at the cabin (services, storage, etc). Geographic differences mean I get different content served if I'm at the cabin and use it's default gateway, hence the policy routing to go back through the Home connection so I'm "there."

In Site A change network.s2s_vpn_site_b.allowed_ips='192.168.9.2/32' '192.168.43.0/24' '0.0.0.0/0' to network.s2s_vpn_site_b.allowed_ips='0.0.0.0/0'

In Site B change network.s2s_vpn_site_a.allowed_ips='0.0.0.0/0' to network.s2s_vpn_site_a.allowed_ips='192.168.9.1/32' '192.168.43.0/24

In both sites move the WG interface into the LAN firewall zone.

1 Like

I was able to achieve lan-to-lan access with both endpoint interfaces in the lan fw zone (NAT'd Site B connects to Site A which has a port forward open to allow it), but in testing, pbr doesn't work doing it that way.

It'd be nice to bidirectionally route traffic (but not all) through the other side, hence the attempt with 0.0.0.0/0 + gateway metric + pbr on Site B's peer config. I can at least get that working. I haven't gotten A > B like I need.

This is what I need to get to:

With both ends in the lan fw zone, pbr does not work. Active policies that should send traffic meant for the other side just time out. (Don't mind that 10.x wan IP address, this is in a lab setup before I deploy)

root@Site-A_Cabin-need-pbr:~# ping 192.168.43.1
PING 192.168.43.1 (192.168.43.1): 56 data bytes
^C
--- 192.168.43.1 ping statistics ---
4 packets transmitted, 0 packets received, 100% packet loss

and ipinfo (or any configured whats-my-ip site) all come up with my IP at the Site A router, not the supposed-to-be-routed-to Site-B IP.

Disable PBR for the time being. Get a working site 2 site setup with Site A also using Site B's internet. Once that's all working then can start adding in other things, like PBR.

2 Likes

With Site A having the peer allowed_ips='0.0.0.0/0' and Site B having the peer allowed_ips='192.168.9.1/32' '192.168.1.0/24' & endpoint_host=xxxxxxxx, both wg interfaces in lan fw zone, no metrics, just the one traffic rule for allow input port 51840: After a restart of the interface on Site A, I lose connectivity- can't ping anything outside the local lan. Route table looks like this:

root@Site-A_Cabin-need-pbr:~# ip route show
default dev wg_s2s_a proto static scope link 
10.19.76.0/24 dev eth3 proto kernel scope link src 10.19.76.152 
192.168.1.0/24 dev br-lan proto kernel scope link src 192.168.1.1 
192.168.9.0/24 dev wg_s2s_a proto kernel scope link src 192.168.9.1 

root@Site-A_Cabin-need-pbr:~# ping 192.168.9.2
PING 192.168.9.2 (192.168.9.2): 56 data bytes
^C
--- 192.168.9.2 ping statistics ---
4 packets transmitted, 0 packets received, 100% packet loss
root@Site-A_Cabin-need-pbr:~# ping 192.168.43.1
PING 192.168.43.1 (192.168.43.1): 56 data bytes
^C
--- 192.168.43.1 ping statistics ---
3 packets transmitted, 0 packets received, 100% packet loss
root@Site-A_Cabin-need-pbr:~# ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
^C
--- 8.8.8.8 ping statistics ---
4 packets transmitted, 0 packets received, 100% packet loss

when I /etc/init.d/network restart, I get back a default route:

root@Site-A_Cabin-need-pbr:~# /etc/init.d/network restart

root@Site-A_Cabin-need-pbr:~# ip route show
default via 10.19.76.1 dev eth3 proto static src 10.19.76.152 
10.19.76.0/24 dev eth3 proto kernel scope link src 10.19.76.152 
192.168.1.0/24 dev br-lan proto kernel scope link src 192.168.1.1 
192.168.9.0/24 dev wg_s2s_a proto kernel scope link src 192.168.9.1 

root@Site-A_Cabin-need-pbr:~# ping 192.168.9.2
PING 192.168.9.2 (192.168.9.2): 56 data bytes
64 bytes from 192.168.9.2: seq=0 ttl=64 time=674.518 ms
64 bytes from 192.168.9.2: seq=1 ttl=64 time=67.282 ms
64 bytes from 192.168.9.2: seq=2 ttl=64 time=393.225 ms
^C
--- 192.168.9.2 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 67.282/378.341/674.518 ms

root@Site-A_Cabin-need-pbr:~# ping 192.168.43.1
PING 192.168.43.1 (192.168.43.1): 56 data bytes
^C
--- 192.168.43.1 ping statistics ---
8 packets transmitted, 0 packets received, 100% packet loss

root@Site-A_Cabin-need-pbr:~# ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: seq=0 ttl=116 time=25.499 ms
64 bytes from 8.8.8.8: seq=1 ttl=116 time=22.135 ms
64 bytes from 8.8.8.8: seq=2 ttl=116 time=21.809 ms
64 bytes from 8.8.8.8: seq=3 ttl=116 time=22.027 ms
^C
--- 8.8.8.8 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 21.809/22.867/25.499 ms

Removing 0.0.0.0/0 and putting the allowed_ips='192.168.43.0/24' '192.168.9.2/32' back and restarting the interface, I can ping the other lan again, but other traffic is (obviously) using my wan connection, not the tunnel:

root@Site-A_Cabin-need-pbr:~# ip route show
default via 10.19.76.1 dev eth3 proto static src 10.19.76.152 
10.19.76.0/24 dev eth3 proto kernel scope link src 10.19.76.152 
192.168.1.0/24 dev br-lan proto kernel scope link src 192.168.1.1 
192.168.9.0/24 dev wg_s2s_a proto kernel scope link src 192.168.9.1 
192.168.9.2 dev wg_s2s_a proto static scope link 
192.168.43.0/24 dev wg_s2s_a proto static scope link 

root@Site-A_Cabin-need-pbr:~# ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: seq=0 ttl=116 time=26.787 ms
64 bytes from 8.8.8.8: seq=1 ttl=116 time=21.917 ms
64 bytes from 8.8.8.8: seq=2 ttl=116 time=21.484 ms
^C
--- 8.8.8.8 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 21.484/23.396/26.787 ms

root@Site-A_Cabin-need-pbr:~# ping 192.168.43.1
PING 192.168.43.1 (192.168.43.1): 56 data bytes
64 bytes from 192.168.43.1: seq=0 ttl=64 time=194.799 ms
64 bytes from 192.168.43.1: seq=1 ttl=64 time=154.991 ms
64 bytes from 192.168.43.1: seq=2 ttl=64 time=114.426 ms
^C
--- 192.168.43.1 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 114.426/154.738/194.799 ms

root@Site-A_Cabin-need-pbr:~# ping 192.168.9.2
PING 192.168.9.2 (192.168.9.2): 56 data bytes
64 bytes from 192.168.9.2: seq=0 ttl=64 time=243.261 ms
64 bytes from 192.168.9.2: seq=1 ttl=64 time=196.297 ms
64 bytes from 192.168.9.2: seq=2 ttl=64 time=162.987 ms
^C
--- 192.168.9.2 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 162.987/200.848/243.261 ms

What are the 10.19.x.x addresses? You said router A was connected directly to the internet. Is that not the case?

At site A a route_allowed_ips of 0.0.0.0/0 should install three routes into the regular routing table:

  • Two routes as a "split default" 0.0.0.0/1 and 128.0.0.0/1 via wireguard. This is the same thing that OpenVPN does to nullify the existing default route (a /1 route is more specific than /0, so it will always be taken) yet leave it in the table so it can be re-established should the VPN be torn down.
  • A "hole punch" of Site B's (NATd) public IP address /32 via the existing WAN.

Without the latter one, encrypted (outside the tunnel) packets to site B won't go like they need to through the actual Internet. They will instead follow the default routes to inside the tunnel, and be lost. So attempts to tunnel anything (or everything) to B also fail.

1 Like

Will be. Can't break the network with people WFH, so it is behind the real router with ports open to sim.

Should, but apparently isn't. Any thoughts on why it's not?

First I would leave the 0.0 allowed_ip out of A and configure it only to access B and B's LAN. Confirm that the tunnel still works and wg show shows B's public IP.

This has been through so many changes that you may want to start over with a basic site-site configuration.

Yeah, I'm basically at square 1, site-to-site lan access only.

root@Site-A_Cabin-need-pbr:~# wg show
interface: wg_s2s_a
  public key: Ex8r+kxEU1+2+e4HQe/7BbywWmwbBx/YeXH5wv6RhWs=
  private key: (hidden)
  listening port: 51840

peer: 594zRNxdbOIFld9qybmM55f21z9qVNaV9vGAlRFP/C4=
  preshared key: (hidden)
  endpoint: 174.239.83.233:7016
  allowed ips: 192.168.43.0/24, 192.168.9.2/32
  latest handshake: 13 seconds ago
  transfer: 741.20 KiB received, 5.05 MiB sent
  persistent keepalive: every 25 seconds
root@Site-A_Cabin-need-pbr:~# ssh root@192.168.43.1

Host '192.168.43.1' is not in the trusted hosts file.
(ssh-ed25519 fingerprint SHA256:aZ+mCSv0+ea/JpXkf8rRGefszX93gdXz0AIs0Dd7MQo)
Do you want to continue connecting? (y/n) yes
root@192.168.43.1's password: 


BusyBox v1.36.1 (2023-10-30 23:44:03 UTC) built-in shell (ash)

  _______                     ________        __
 |       |.-----.-----.-----.|  |  |  |.----.|  |_
 |   -   ||  _  |  -__|     ||  |  |  ||   _||   _|
 |_______||   __|_____|__|__||________||__|  |____|
          |__| W I R E L E S S   F R E E D O M
 -----------------------------------------------------
 OpenWrt 23.05.0, r23497-6637af95aa
 -----------------------------------------------------
root@Site-B_Home-behind-NAT:~# 

There was a similar topic recently:
Reverse WireGuard tunnel
It was resolved using custom routing tables.

I documented the critical steps here:
Route VPN server LAN via VPN client

Ooooh that may be it. Skimmed quick, looks like I do need a separate zone just for the vpn tunnel, and then the routing to/from/through it for lan and wan. Getting late here tonight, so I'll have to play with it tomorrow morning.

Actually not, this is easier to implement when the VPN interface is assigned to the LAN firewall zone on both VPN client and server.
Be sure to set up a site-to-site connection and verify that LAN clients from each site can ping the other site before you proceed to the next step.

I think @mk24 is on to something
I am not sure about the 0.0.0.0/1, 128.0.0.0/1 (that is what OpenVPN is doing and probably what WG should do but not do here) but the route to the endpoint via the WAN is missing.

Now scratching my head why

Both methods can work, but that static route is an extra dependency which is best to avoid unless you can propose a reliable watchdog script to keep it up to date.