WireGuard "server" on Relayd client bridge device [SOLVED]

I'm trying to get a wireguard "server" set up on a device configured as a relayd client bridge. Which means there is no WAN. The three existing interfaces are all on the LAN zone. I am having trouble getting wireguard to even hear connection attempts. I wouldn't have thought any firewall rules would be required for that. I've looked at several tutorials and none of them have firewall settings that seem appropriate since they all assume a WAN.

Ideas?

EDIT: Long thread with lots of discussion. Final solution:

  1. Create new firewall zone for the VPN and put the wireguard interface in that zone. Set input/output/forward all to accept. Set Allow Foward To Destination to the LAN zone.
  2. Edit the LAN firewall zone and turn on masquerading and set Allow Forward From Source to the VPN zone.
  3. WWAN, LAN, and RELAYD interfaces should all be on the LAN zone, and Wireguard interface on the VPN zone.
  4. Make sure the Wireguard interface, keys, and peers are all configured correctly
  5. Make sure the Wireguard listening port is forwarded from the primary router to this one.

Thanks to psherman for spending so much time.

EDIT 2: It turns out this solution is very sensitive to WHEN relayd starts. The above solution fails for me with OpenWrt snapshots after 10 Jan or so unless relayd is started after wireguard. Re-ordering the interfaces in /etc/config/network doesn't seem to accomplish this, so for now to get this solution to work I have to restart relayd in /etc/rc.local :

( RELAYD=`ps www | grep relayd | grep -v grep | cut -c27-` && killall relayd && ( $RELAYD & ) )
1 Like

I'm not sure how relayd might interact with WG, but let's first review your config files and we'll go from there.

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/firewall

Also please post your other peer config (i.e. from your phone/tablet/computer or other router) and indicate where it is with respect to the device hosting the 'server' side.

1 Like

I am now able to get handshakes successfully exchanged between "client" devices and the server. This at least validates the basic interface and key configurations. But when connected, even though the host and remote WG instances are talking to each other, no remove device can successfully ping the router (at 10.6.2.1)

Basic network structure:

  • Main house router at 192.168.3.1 providing 192.168.3.0/24.
  • OpenWrt router ("Shadow") at 192.168.3.200 as relayd client bridge
  • Relayd configured on the otherwise unused 10.10.10.1
  • Main router port-forwards incoming WG packets to Shadow.
  • Wireguard set up on Shadow with its interface on 10.6.2.1
  • Wireguard remote devices on 10.6.2.X.

I expected to have to use SNAT for the remotely connected WG devices to talk to the home 192.168.3.0/24 network, but I do not understand why the connected devices can't talk to the router on the WG 10.6.3.X. All interfaces are on the LAN firewall zone. I didn't delete some of the WAN firewall rules, just so I could reinstate a WAN later if I want. There is no WAN interface.

Config files to follow:

Thanks.

/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 packet_steering '1'
	option ula_prefix 'fd77:f48d:5635::/48'

config device
	option name 'br-lan'
	option type 'bridge'
	list ports 'lan1'
	list ports 'lan2'
	list ports 'lan3'
	list ports 'lan4'

config interface 'lan'
	option device 'br-lan'
	option proto 'static'
	option netmask '255.255.255.0'
	option ip6assign '60'
	option ipaddr '10.10.10.1'
	option gateway '192.168.3.1'
	list dns '192.168.3.1'

config interface 'wwan'
	option proto 'dhcp'

config interface 'repeater_bridge'
	option proto 'relay'
	list network 'lan'
	list network 'wwan'
	option ipaddr '192.168.3.200'

config interface 'VPN'
	option proto 'wireguard'
	option private_key '<privatekey>'
	option listen_port '<port>'
	list addresses '10.6.2.1'

config wireguard_VPN
	option description 'Cursor'
	option preshared_key '<presharedkey>'
	list allowed_ips '10.6.2.2'
	option public_key '<pubkey>'
	option route_allowed_ips '1'
	option persistent_keepalive '25'

config device
	option name 'VPN'

config wireguard_VPN
	option description 'Roswell'
	option public_key 'l<pubkey>'
	option preshared_key '<presharedkey>'
	list allowed_ips '10.6.2.3'
	option route_allowed_ips '1'
	option persistent_keepalive '25'

/etc/config/firewall

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

config zone
	option name 'lan'
	option input 'ACCEPT'
	option output 'ACCEPT'
	option forward 'ACCEPT'
	list network 'lan'
	list network 'wwan'
	list network 'repeater_bridge'
	list network 'VPN'

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

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 '0'

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

/etc/config/wireless

onfig wifi-device 'radio0'
	option type 'mac80211'
	option hwmode '11g'
	option path '1e140000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0'
	option htmode 'HT40'
	option cell_density '0'
	option country 'CA'
	option channel '3'
	option distance '100'
	option noscan '1'
	option disabled '1'


config wifi-iface 'default_radio0'
	option device 'radio0'
	option network 'lan'
	option mode 'ap'
	option ssid 'RadioActive'
	option key 'nevermind'
	option encryption 'sae-mixed'
	option disassoc_low_ack '0'
	option disabled '1'

config wifi-device 'radio1'
	option type 'mac80211'
	option hwmode '11a'
	option path '1e140000.pcie/pci0000:00/0000:00:01.0/0000:02:00.0'
	option htmode 'VHT80'
	option cell_density '0'
	option channel '153'
	option country 'CA'
	option distance '50'
	option txpower '26'
	option noscan '1'

config wifi-iface 'wifinet1'
	option device 'radio1'
	option mode 'sta'
	option network 'wwan'
	option encryption 'psk2'
	option disassoc_low_ack '0'
	option ssid '<ssid>'
	option key '<key>'

remove this:

Then, remove the VPN from the lan zone and create a new vpn zone with masquerading enabled:

What you'll want is the following:

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

config zone
	option name 'vpn'
	option input 'ACCEPT'
	option output 'ACCEPT'
	option forward 'ACCEPT'
	list network 'VPN'
	option masq '1'

And then add a forwarding rule:

config forwarding
	option src 'vpn'
	option dest 'lan'

Thanks, unfortunately there is no change. Wireguard "clients" will still successfully handshake and I see keepalive packets in their logs and wg on the server reports the same...

$ wg show
interface: VPN
  public key: <redacted>
  private key: (hidden)
  listening port: 1701

peer: <redacted>
  preshared key: (hidden)
  endpoint: <redacted>
  allowed ips: 10.6.2.2/32
  latest handshake: 11 seconds ago
  transfer: 2.26 KiB received, 344 B sent
  persistent keepalive: every 25 seconds

peer: <redacted>
  preshared key: (hidden)
  endpoint: <redacted>
  allowed ips: 10.6.2.3/32
  latest handshake: 4 minutes, 10 seconds ago
  transfer: 10.84 KiB received, 4.36 KiB sent
  persistent keepalive: every 25 seconds

...but no actual data can flow. Remote devices cannot see 10.6.2.1, let alone anything on the proper LAN subnet.

Let's see your updated firewall file and your 'client' peer configuration.

/etc/config/firewall
Still cluttered by the old WAN stuff, but the relevant parts seem to be as suggested.

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

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

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

config zone
	option name 'vpn'
	option input 'ACCEPT'
	option output 'ACCEPT'
	option forward 'ACCEPT'
	list network 'VPN'
	option masq '1'

config forwarding
	option src 'vpn'
	option dest 'lan'

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 '0'

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

Client 1 config (laptop):

[Interface]
PrivateKey = <redacted>
Address = 10.6.2.3/32
DNS = 192.168.3.1

[Peer]
PublicKey = <redacted>
PresharedKey = <redacted>
AllowedIPs = 10.6.2.1/32
Endpoint = <redacted>
PersistentKeepalive = 25

Client 2 config (phone):

[Interface]
Address = 10.6.2.2/32
PrivateKey = <redacted>
[Peer]
AllowedIPs = 10.6.2.1/32
Endpoint = <redacted>
PersistentKeepalive = 25
PreSharedKey = <redacted>
PublicKey = <redacted>

Allowed IPs on the peers are wrong.

  • If you want to tunnel everything, make it 0.0.0.0/0
  • If you only intend to tunnel the IPs associated with your LAN subnet, make it 192.168.3.0/24 (possibly also with 10.6.2.0/24 if you so choose).

Also, in your WG config on the OpenWrt side, make the subnet definitions explicit.

  • On the interface: 10.6.2.1/24
  • on the peers: 10.6.2.x/32
1 Like

Ah ha! Yes, I missed that, thank-you! That seems to have been it. I can get a connection from my device to the server.

Sort of. For real use, they are wrong. For testing I had set the peers so they could only communicate with the "server". With 10.6.2.1/32 they can at least able to talk to 10.6.2.1, and they are talking now. I'll get a wider allowance in there as I proceed.

Thanks so much for all the help.

But you also mentioned that couldn't talk to the main LAN... this was the reason why.

Anyway, glad you're up and running now.

If your problem is solved, please consider marking this topic as [Solved]. See How to mark a topic as [Solved] for a short how-to.

I just widened up the allowed client IPs. So the client can talk to the router on 10.6.2.1, and also on its LAN address of 192.168.3.200. But the client can't talk to any other LAN (192.168.3.X) address. Which tells me the there is no SNAT occurring. I'm not sure what kind of NAT the "masquerade" firewall setting invokes.

I'm much more familliar with iptables, but with some judicious use of iptables-translate I was able to get it to work manually this way:

nft add table nat
nft 'add chain nat postrouting { type nat hook postrouting priority 100 ; }'
nft add rule nat postrouting ip saddr 10.6.2.0/24 ip daddr != 10.6.2.0/24 counter snat to 192.168.3.200

I can always script this in, but do you know how I can accomplish this through the interface/config?

The masquerade rule makes the WG interface (including the 'clients') appear as a single IP on your main LAN (192.168.3.20, the OpenWrt device's address), rather than as individual IPs in the 10.6.2.0/24 network. The avoids the need to have static routes on the main router.

That said, if you can set static routes on the main router, you should be able to disable the masquerading rule, if you like. The static route you need to set would be 10.6.2.0/24 via 192.168.3.20

Right, got that part, the issue is just that it isn't working. My client can talk to the router on any IP the router is listening on, but only the router itself. Clients can't reach any other LAN resource. This tells me the packets aren't being masqueraded properly.

What I meant was that I wasn't sure what the masquerade firewall setting is causing nftables to do under the hood.

Now that I think of it more, I don't think masquerade can work in this instance. Masquerade is a special case of SNAT where the translated source address is set to the address of the output interface. In this case, the output interface is the wg VPN interface and its IP is 10.6.2.1. I need the source address to be translated to 192.168.3.200 for clients to see LAN resources.

If I force feed an explicit SNAT rule with nftables, then clients can see the LAN:
# nft add rule inet fw4 srcnat ip saddr 10.6.2.0/24 ip daddr != 10.6.2.0/24 counter snat to 192.168.3.200

This forces the SNAT address to the router's LAN IP address. I'm just wondering if there is a way to get clients to see the LAN devices with baked in OpenWrt settings and not a homebrew nftables rule.

I want this to end up in a wiki article, and fussing with custom nftables rules does not make for a good wiki howto.

Maybe, maybe not. As I said in the beginning of the thread, I'm not sure how this will interact with relayd, so there is a variable that I can't be sure of.

If you set the 'client' allowed IPs to 0.0.0.0/0, can you reach the internet (don't forget that you might need to set DNS -- use a public server like 8.8.8.8)? This would give an indication of the packets being masqueraded because otherwise the return traffic would not know how to get back to your client devices.

Feel free to write a wiki page. Your config is more of an outlier, though -- most people do not attempt to use WG on top of a device that is running relayd. That may explain some of the difficulties.

1 Like

Probably true about it being an outlier. I mean, I think there is a great usage case for it. It ends up being a completely plug-and-play VPN-in-a-box you can drop into an existing network without changing anything. Lots of small non-profits and small (non-it) businesses around here packages where everything is provided and set up by the ISP and they can't really change it. Welcome to Nova Scotia, please set your clocks back ten years.

Being able to walk in and just plug a device in to add instant VPN (slash SAMBA slash syncthing slash whatever) is a benefit here. I've donated half a dozen setups for that sort of thing (using DD-WRT and Fresh Tomato). I have a little brushing up to do to get up to speed with OpenWrt, which is still new to me.

Anyway, thank-you again for your time.

I think that there are a number of factors that play in here, and your idea of not needing to change anything may be a bit ambitious.

  • If your OpenWrt device is a VPN 'server' (i.e. listening for inbound connections), you must be able to ensure that you have a public IP on the main router and that you have port forwarding enabled to the OpenWrt box. The latter requires changes to the upstream network.

    • If the OpenWrt system is acting as a 'client', the above is not an issue.
  • Would argue that using relayd makes this a less predictable and more complex solution than simply connecting the upstream "LAN" to an OpenWrt "WAN" for the purposes of a plug-and-play VPN-in-a-box.

    • This above is true for both a 'server' mode VPN and a 'client'

In my networks, I have an OpenWrt device with WG and OpenVPN in a server capacity. This device connects via its WAN port to the upstream LAN. The OpenWrt device behaves as any other device on the LAN, and the VPN 'clients' are allowed to reach the LAN and the internet by means of forwarding from the vpn zone > wan zone (because everything is upstream of the OpenWrt device).

  • This does require port forwarding to be setup, but all 'server' configs will have this requirement, unless it is on the main/internet facing router (at which point it is just simply 'opening' a port).
  • If the main router supports static routes, the appropriate routes are added to the routing table, masquerading can be disabled on the wan zone, and zone forwarding from wan > vpn is allowed.
  • Where the main router doesn't support static routes, masquerading will be enabled on the wan zone and it will 'just work.' (wan > vpn forwarding is not required in this case).

On the other side, I have a travel router (or the WG or OpenVPN client directly installed on a computer/tablet/phone) that will connect back to my home network VPN. When I'm using the travel router, that device sets up the VPN tunnel back to my home and it tunnels that traffic of all of my devices completely transparently, through my home internet connection. Since this is a 'client' (outbound) connection, no port forwarding is necessary, nor are static routes or a public IP on the WAN of this network. The OpenWrt travel/client device connects to the upstream device much like the 'server' does on its network -- the WAN of the 'client' device connects to the upstream network using ethernet or wifi (Travelmate is great for this). Enable the VPN tunnel, and done. A plug-and-play box.

I do not run relayd on any of these devices and the setup is easy on both sides (after the initial configuration is complete).

I don't really see how relayd makes the solution you are proposing more 'plug-and-play' than simply using the OpenWrt WAN the uplink method. Maybe you can elaborate on why you think relayd is better.

Correct, I do need one port forward. This is the only upstream support required, though, and even the dumbest routers ISPs here provide tend to support port forwarding.

I don't always use client bridge. That's just one topology, and the one I am experimenting with at the moment. I use it when co-locating another router with the original is not possible or not desirable. Like when they are looking for some extended wi-fi coverage without running 100m of ethernet. You'd be surprised how many museums have their wi-fi stuck in some utility room off in the back because that was easiest for the installer. Using a client bridge I can just drop a device in their office, which generally has far better antennae and signal levels than their office equipment and

No, I still think it's a good usage case but not for every situation. I really don't think relayd is what is getting in the way of masquerading on my setup. Masquerading just isn't the right tool here since the IP address of the WG interface isn't the IP address needed for SNATing to. Masquerading is appropriate for a WAN interface where its IP address is the one you need to SNAT to. Masquerading the WG interface will let LAN devices get SNATed to the WG IP, and I need WG clients to be SNATed to the LAN IP. I'm becoming more convinced the only solution is the nftables hack.

Oh man. I just realized I’ve been telling you to masquerade the wrong network/zone. It is the lan that needs to be masqueraded. Wg does not.

Try that.

2 Likes

Hmmm. In that case, yes, relayd makes that problematic and might have something to say about it. WIth the client bridge config, the LAN interface gets that otherwise unused subnet and the WWAN owns the LAN IP. I'll play and see if I can get to work.

Ha! Perfect. Works a treat. I would have just assumed that wouldn't have worked.

Fantastic solution, and can be 100% accomplished through Luci which is even better. No other firmware can do a client bridge with added VPN without fiddling around with iptables.

I am (more) in love with OpenWrt.

Thank-you a bunch.