Wireguard Policy Based Routing on ROS7+ - Best Practices

I am testing Wireguard configuration with a single network segment, 192.168.110.0/24. This is via NordVPN. Despite them not offering config files I found some workarounds to get the necessary endpoints and the keys. I’ve seen the links and articles that have been shared previously, but I had some more focussed quesitons

Initially I was testing with a routing mark but then I was having performance problems which I attributed to fast track (prior experience with IPSEC and Fasttrack made that quite a fast thing to rule out)

After a little digging and help from ChatGPT I have a working configuration:

/routing table add name=wg-nordvpn fib
/ip route add dst-address=0.0.0.0/0 gateway=wg-nordvpn routing-table=wg-nordvpn
/routing rule add action=lookup table=wg-nordvpn src-address=192.168.110.0/24
/ip firewall mangle add chain=prerouting src-address=192.168.110.0/24 action=mark-connection new-connection-mark=wg-nordvpn-conn
/ip firewall nat add chain=srcnat src-address=192.168.110.0/24 out-interface=wg-nordvpn action=masquerade

I then update the existing fasttrack rule to exclude traffic with the connection mark as above.

A few questions:

  1. Is this best practice for selectively routing traffic?

  2. If I wanted to allow traffic to run over my pppoe interface in the event of a failure, is that just a case of adding a second route with a higher distance for 0.0.0.0/0 on the wg-nordvpn routing table?

  3. Conversely, if I wanted to not pass any traffic without a VPN, do I actually need to do anything else here, since there’s only one route and its via the wg interface? Is there a way to block this network ever using the WAN interface? I guess a simple firewall rule would suffice - just curious how others do it.

  4. I noted that despite having a default route for all traffic over the Wireguard tunnel, that the DNS traffic still appears to be going to my DoH endpoint. This is presumably because I have specified 192.168.110.1 as the DNS server so in actual fact the router itself is making the DNS query. If I wanted the DNS traffic to be sourced from the public IP on the Wireguard connection (the Nordvpn IP in this case) I guess I cannot actually use DoH (or at least not via the Mikrotik) and I could use an alternative DNS?

4b) In my research I found every example using 10.5.0.2 for the interface address. I thought if I set my DNS to this it would then route out via Nord, but I assume that DoH takes all precedence.

The performance of Wireguard is impressive…I am actually finding speedtests to be faster than normal. This is likely anecdotal but I was wondering if there is a reason for this?

  1. Yes-ish, because there is no need of the mangle rules (the routing rule does the same) and the nat rule (the default one already does the same)

  2. No need, because action=lookup on the routing rule means that routing decisions will fall back to the main table if the route, specified in the wg-nordvpn table, becomes unreachable

  3. There’s a much easier solution than using a firewall rule - specifying allowed-address=0.0.0.0/0 for the WG peer

  4. and 4b) I’m not competent enough to answer

Thank you, I didn’t actually know that’s what the lookup action did - good to know!

As for my actual WG config, I do already have that…I was not actually sure what it meant

/interface wireguard peers
add allowed-address=0.0.0.0/0 endpoint-address=x.x.x.x endpoint-port=yyyy interface=wg-nordvpn name=peer20 persistent-keepalive=25s public-key="<pubkey>"

And good catch on my numbering fail…fixed :slight_smile:

  1. Couple of things.
    There is not usually a need to mangle or Route Rule, one or the other in most circumstances should suffice.

What is the same for both, which you have are:
a. table
b. route

The routing rule forces all SUBNET traffic into wireguard and if you have multiple subnets then that creates issues.
Easy to get around but since you dont provide requirements/config, cant say much more.

The main differences to consider… mangling is for more complex scenarios, (again with no info may or may not be applicable ),
and is for cases wehre we are not talking a whole subnet or just a few users. Mangling is very good at identifying/capturing traffic from disparate users.

Reason to avoid mangling where possible is that it can depending upon the config interfere with fasttrack connections.

  1. Typically in routing rules, one decides by setting the action.
    a. action=lookup means if wg is not active, go to main table.
    b. action=lookup-only-in-table if wg is not active, then DONT look elsewhere either.

The problem is that wireguard is not like a normal route, its basically a fancy interface with extra powers. So in the case of wireguard routing rule action has no effect as the router does not know the status of the wireguard routing.

To ensure the router is aware, we can check recursively if the gateway is available and thus inform the router if the route is active…
/ip route
add dst-address=0.0.0.0/0 gateway=pppoe distance=1 add check-gateway=ping
add dst-address 0.0.0/0 gateway=1.1.1.1 distance=2 check-gateway=ping scope=10 target-scope=12
add dst-address 1.1.1.1/32 gateway=wg distance=2 scope=10 target-scope=11
add dst-address=0.0.0./0 gateway=wg table=useWG

Couple of things.

  1. Yes, you need to NAT the wireguard traffic and it should be short N sweet like so.
    add action=masquerade chain=srcnat out-interface=wg

The reason is that NordVPN is expecting ONLY to see the single IP address assigned to your account and thus we use the rule above to do so.
Any return traffic is unsourcenatted by the router and sent to the originator.

  1. The ONLY reasonable choice for allowed IPs is 0.0.0.0/0, if the intent is to use nordvpn for internet.

add allowed address=0.0.0.0/0 endpoint=NordvpnAddress endpoint-port=Nordprovided port
interface=wg persistant-keep-alive=35s public-key=“------------”

Thanks for that.

So I went ahead and just removed the mangle rule and…nothing negative happened. I was following along from some research and chatgpt and saw both in example configurations. The route rule on it’s own (with the separate routing table) is working fine, and I removed the connection mark exclusion from fasttrrack, and there’s no performance hit now (not sure if you saw I mentioned that before, but I have also seen that problem with fasstrack in the past with ipsec so it stood out as the likely cause immediately)

Indeed in this case, I wish to send the entire subnet 192.168.110.0/24 via the tunnel. I believe I now have that

I have 3 other subnets in their own VLANs with associated WiFi config:

192.168.1.0/24
192.168.100.0/24
192.168.120.0/24

These are ticking along nicely, too. I believe that I have avoided any issue there by creating the separate routing instance and only creating routes applicable to the 110.0 (GUEST) Subnet:

The config is now:

/interface wireguard peers
add allowed-address=0.0.0.0/0 endpoint-address=x.x.x.x endpoint-port=yyyy interface=wg-nordvpn name=nord-peer persistent-keepalive=25s public-key="<pubkey>"

/routing table add name=wg-nordvpn fib
/ip route add dst-address=0.0.0.0/0 gateway=wg-nordvpn routing-table=wg-nordvpn
/routing rule add action=lookup table=wg-nordvpn src-address=192.168.110.0/24
/ip firewall nat add chain=srcnat src-address=192.168.110.0/24 out-interface=wg-nordvpn action=masquerade

Thank you for explaining the nuances around wireguard and how to handle those. In this case, having a fallback to the regular routing table is fine, and probably preferable. Bundling the traffic over a VPN is just a nice to have in this case, and largely more an experiment in Wireguard :slight_smile:

Some comments.

  1. Avoid trying to use sourcenat like firewall rules.
    This suffices
    /ip firewall nat
    add chain=srcnat out-interface=wg-nordvpn action=masquerade

adding the source address subnet does nothing…

In fact when doing third party vpn another option is:
/interface list members
add interface=wireguard list=WAN

and then the standard default Sourcenat rule covers the wireguard sourcenatting.
add chain=srcnat action=masquerade out-interface-list=WAN

  1. Ensure you understand the ramifications of the routing rule.
    /routing rule add action=lookup table=wg-nordvpn src-address=192.168.110.0/24

That means ALL traffic coming from that subnet is going out wireguard.
That means, no users on the 110 subnet can reach any other subnets on the router
That means, no users on other subnets can reach this subnet.

  1. You didnt address the fact that if NordVPN isnt working the users will not have any internet as the router will not know the wireguard is not working and thus 110 users internet traffic will not go out the local WAN.

Thanks..you know..I was just thinking about where to put the Wireguard interface when it comes to NAT and Firewalling.

I have been doing a spring clean on my firewall rules and now that I have new VLANs and interfaces I’ve dropped in a drop rule for the forward chain at the end. I forgot a few things naturally, one of which being Wireguard. For now I created a new firewall rule, but I actually thought I’d go back and change that after best practices digging.

The ramifications are slightly broader than expected; however, that’s what I want (indeed I want all VLANs generally totally unable to talk to eachother, save a few specific services. Alas, definitely noted for future uses.

On point 3, fair enough - but as you provided me already with a workable solution there, I will go back and play with that. It’s low priority in my case.

This has been super helpful. Thanks again.

  1. Ensure you understand the ramifications of the routing rule.

So here is a challenge/question…

There are some services that I would like to bypass the Wireguard tunnel sourced from the GUEST VLAN

For the sake of example, could I simply set a route on that routing table that is for instance X.X.X.X/24 to go via the standard gateway, or does the routing rule and other associated config make this impossible.

If so, what I do i need to do to change my approach such that I can keep a list of IPs not to be tunneled?

And…is it possible to update an address list dynamically based on a list of allowed DNS domains? Keeping an address list for say, Amazon, is otherwise not terribly easy, and they are a bit evasive with publishing the IP space.

If its a fair number of users but not a full subnet, then you will need to capture/identify via mangling rules and possibly create another table and routing rule for the traffic.
In any case, once you know all the requirements, I dont chase a changing config, then articulate them and an efficient config can be reached.

So yeah it would be for the full subnet.

The route rule says ‘use this routing table’ and in that routing table there is a sole default route out of the wg interface

Is it as simple as adding more entries to that table?

Nothing is as simple as adding to the table,
One has to carefully consider all the traffic being affected… but generally subnets are very easy to deal with