Need help forcing domain traffic through a Wireguard VPN

Context

My work requires me to access specific domain through a VPN. Instead of connecting in my computer, I want to configure the router so that I don't need to turn the VPN on/off, but all traffic going to that domain (not a static IP) goes through the configured Wireguard VPN.
I have a Mikrotik CRS running RouterOS 7.

What I've done

Some things I've tried based on this discussion

  • Connected my Wireguard VPN to the router - wg-c8, IP 10.0.240.1/32
  • Created a Bridge interface with protocol mode none - named c8-bridge
  • Created a VRF list - c8-table - using the interface c8-bridge (this also created the Routing table)
  • Created a firewall address list with the domains I need to reroute - c8-address-list
  • Created 2 firewall mangle rules:
    • prerouting when destination address list is c8-address-list: mark connection with c8-conn-mark
    • prerouting when connection is marked with c8-conn-mark: mark routing with c8-table
  • Created a Route List with destination address 0.0.0.0/0, gateway wg-c8 and routing table c8-table

Issues

Seems that the connection is not being properly established. If I try to access, the connection TCP State gets to syn-recv, but shouldn't it change to established?

[<redacted>@MikroTik] > /ip firewall connection print detail where connection-mark=c8-conn-mark
Flags: E - expected; S - seen-reply; A - assured; C - confirmed; D - dying; F - fasttrack; H - hw-offload; s - srcnat; d - dstnat 
 3    C   s  protocol=tcp src-address=<laptop-ip>:56308 dst-address=<company-ip>:443 reply-src-address=<company-ip>:443 reply-dst-address=<wireguard-ip>:56308 
             tcp-state=syn-sent timeout=2s connection-mark="c8-conn-mark" orig-packets=1 orig-bytes=60 orig-fasttrack-packets=0 
             orig-fasttrack-bytes=0 repl-packets=0 repl-bytes=0 repl-fasttrack-packets=0 repl-fasttrack-bytes=0 orig-rate=0bps repl-rate=0bps

Could the error be that the src-address and reply-dst-address are not the same? One is my local IP and the other is the IP address from my machine in the wg-c8 network.

Hi, you don't need to create a VRF for the purpose. The discussion about VRF on that thread concerned the ability to reach the local IP address of the router.

What you need is to do is:

  • Create and configure the WG interface wg-c8 , with correct address and peer, etc... like you did.

  • Create a firewall address list with the domains you need to reroute - c8-address-list

  • Create a routing table c8-table with FIB turned on (not a VRF!).

  • Create 2 firewall mangle rules:

    • prerouting when destination address list is c8-address-list and connection-mark=no-mark: mark connection with c8-conn-mark
    • prerouting when connection is marked with c8-conn-mark AND in-interface-list is your LAN list: mark routing with c8-table
  • Add a SRCNAT masquerade rule for out-interface=wg-c8 (or add wg-c8 to the WAN interface list if you use the defconf firewall)

  • Create an IP -> Route with destination address 0.0.0.0/0, gateway wg-c8 and routing table c8-table.

Bolded are the changes you need to make.

1 Like

Of the two rule stated there, technically correct for the first rule is that its a forward chain, not a prerouting chain. ( for the mark connections that is. Traffic being marked is lan to wan (forward). Both methods work.

1 Like

Thanks for the help!

I've made the changes and things look better, but I still have some failures...
I'm trying to connect to a Mongo server, and I still see src-address being different than reply-dst-address:

protocol=tcp
src-address=<laptop-ip::46584 dst-address=<mongo-ip>:27017
reply-src-address=<mongo-ip>:27017 reply-dst-address=192.168.1.2:46584
tcp-state=syn-sent timeout=4s connection-mark="c8-conn-mark" orig-packets=2 
orig-bytes=120 orig-fasttrack-packets=0 orig-fasttrack-bytes=0 repl-packets=0
repl-bytes=0 repl-fasttrack-packets=0 repl-fasttrack-bytes=0 orig-rate=480bps repl-rate=0bps

I really don't know where the 192.168.1.2 IP came from, since my LAN uses the 10.64.0.0/16 network...

When SRCNAT is involved (like when you have a masquerade rule, then when you look at the connection tracking entries, src-address is the originating IP address (of your device in LAN), and reply-dst-address is the source address of the packet after SRCNAT has been applied. Did you specify 192.168.1.2 in the to-addresses field of the SRCNAT rule? If not, and if you have a masquerade action, then 192.168.1.2 is the IP address of the router on the out-interface.

Check the export of /ip address export and /ip firewall nat export to see where the address is specified.

No, I haven't specified the to-addressed. I also ran both exports and haven't found this IP.

To be clear, I gave a MongoDB example, but this happens with other connections too.

Then do a /ip address print. Your router might take that IP address from DHCP. Then you can see which interface was used as out-interface.

If that interface is not the one you expect (not wg-c8), maybe you could post your (censored) configuration export.

/export file=anynameyouwish (minus router serial number, any public WANIP information, keys, dhcp lease lists)

Sorry for the late response. Here are some export to help debug the issue:

/ip address print
Flags: X - DISABLED; D - DYNAMIC
Columns: ADDRESS, NETWORK, INTERFACE
#    ADDRESS         NETWORK      INTERFACE
0    10.64.0.1/16    10.64.0.0    bridge1  
1    10.64.10.1/24   10.64.10.0   vlan10   
---
3    10.64.240.1/24  10.64.240.0  vlan240  
---
---
6    10.0.240.1/32   10.0.240.0   wg-c8
7  D ----------/24  -----------   ether1
/ip firewall nat print
Flags: X - disabled, I - invalid; D - dynamic 
 0    chain=srcnat action=masquerade out-interface=wg-c8 log=no log-prefix="" 
---
 2    chain=srcnat action=masquerade out-interface=ether1 
/ip firewall mangle print   
Flags: X - disabled, I - invalid; D - dynamic 
 0    chain=prerouting action=mark-connection new-connection-mark=c8-conn-mark passthrough=yes dst-address-list=c8-address-list connection-mark=no-mark log=no log-prefix="" 
 1    chain=prerouting action=mark-routing new-routing-mark=c8-table passthrough=no connection-mark=c8-conn-mark in-interface=bridge1 log=no log-prefix="" 
/ip route print
Flags: D - DYNAMIC; A - ACTIVE; c - CONNECT, s - STATIC, d - DHCP
Columns: DST-ADDRESS, GATEWAY, ROUTING-TABLE, DISTANCE
#     DST-ADDRESS     GATEWAY      ROUTING-TABLE  DISTANCE
  DAd 0.0.0.0/0       ---          main                  1
  DAc 10.0.240.0/32   wg-c8        main                  0
  DAc 10.64.0.0/16    bridge1      main                  0
  DAc 10.64.10.0/24   vlan10       main                  0
  DAc 10.64.240.0/24  vlan240      main                  0
---
  DAc 192.168.1.0/24  ether1       main                  0
0  As 0.0.0.0/0       wg-c8        c8-table              1
/routing table print
Flags: D - dynamic; X - disabled, I - invalid; U - used 
 0 D   name="main" fib 
 1     name="c8-table" fib 

It looks like 192.168.1.2 is the IP address that you get assigned by DHCP on ether1 (there's the connected route for destination 192.168.1.0/24 gateway ether1 automatically added by that assignment too). So when your connection table shows the connections with that as reply-dst-address then it means ether1 was chosen as the out-interface (and then the masquerade is applied and does that address change). Which means the mark-routing rule did not match as intended.

As I wrote in the previous post, for that rule you should use in-interface-list together with an interface list that contains your LAN interfaces. In your case it will be your vlanXXX interfaces. Currently, your mangle rule only has in-interface=bridge1, which means it only applies to devices attached to the bridge1 interface (which is like the untagged implicit VLAN 1 if you have turned on vlan-filtering=yes). The rule would not match for any clients in the other listed vlanXXX interfaces at all.

You should create an interface list LAN (if not already created), then add to that list all your VLAN interfaces (vlan10, ... vlan240).

/interface list
add name=LAN

/interface list member
add interface=vlan10 list=LAN
# ...
add interface=vlan240 list=VLAN
Note about bridge1

If you still use the bridge1 interface with devices (devices not in the individual VLAN interfaces) then add bridge1 to the LAN interface list too:

/interface list member
add interface=bridge1 list=LAN

However, if you use bridge1 for that purpose, then the current address assignment 10.64.0.1/16 to it is questionable, because it overlaps with the other VLAN interfaces, devices in bridge1 won't be able to talk to devices in the VLANs. If, instead, you no longer have devices attached to bridge1, only to the vlan10, ... vlan240 interfaces, then you don't need to assign any IP address to bridge1, and the 10.64.0.1/16 assignment can be removed.

Then your mangle mark-routing rule should look like this instead:

/ip firewall mangle
add chain=prerouting action=mark-routing new-routing-mark=c8-table \
    passthrough=no connection-mark=c8-conn-mark in-interface-list=LAN

(changed from in-interface=bridge1 to in-interface-list=LAN).

I am not sure why showed prints instead of entire config minus the bits not needed?????

That was it! Sorry I missed that in the first place… Thanks again for the patience and help with this. Will help me a lot :folded_hands: