Score:3

WireGuard Double VPN: Only forward WireGuard traffic

ua flag

The Goal: A VPN chain

I'm attempting to achieve a VPN chain. The first server is my own VPS, while the second one is from Mullvad VPN. I use my VPS for multiple purposes and I would like to only redirect the WireGuard traffic. All of the remaining traffic is not supposed to go through Mullvad.

Your Device <---> VPN Server 1 <---> VPN Server 2 <---> Internet

Client to VPN Server 1

The connection between the client and my VPS already works. This is the WireGuard configuration file for the server (wg0.conf):

[Interface]
PrivateKey = ********************************************
Address = 10.0.0.1/24
ListenPort = 51820
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

[Peer]
PublicKey = ********************************************
AllowedIPs = 10.0.0.2/32

And here is the client configuration file. All of the clients traffic goes through the VPN.

[Interface]
Address = 10.0.0.2/32
ListenPort = 51820
PrivateKey = ********************************************

[Peer]
PublicKey = ********************************************
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = ***.***.***.***:51820

VPN Server 1 to VPN Server 2 (Mullvad)

This is the Mullvad configuration file (wg1.conf). The problem is, it tunnels all of the servers traffic through Mullvad.

[Interface]
PrivateKey = ********************************************
Address = 10.67.221.119/32,fc00:bbbb:bbbb:bb01::4:dd76/128
DNS = 100.64.0.7
PostUp = iptables -I OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT && ip6tables -I OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT
PreDown = iptables -D OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT && ip6tables -D OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT

[Peer]
PublicKey = ********************************************
AllowedIPs = 0.0.0.0/0,::0/0
Endpoint = ***.***.***.***:51820

I guess, I'm looking for a way to only route the traffic from wg0 to wg1. All of the remaining traffic should be excluded.

us flag
Expanded my answer. This should get you started. :-)
Score:1
us flag

It is possible using what is called source based routing / policy based routing.

In a nutshell you need to create a second routing table on Server 1, where you set Server 2 as the default gateway.

I assume you are running a Linux distribution on the server and using the iproute2 package.

In that case you will have the file /etc/iproute2/rt_tables.

The content of the file will be something similar to this:

#
# reserved values
#
255     local
254     main
253     default
0       unspec
#
# local
#
#1      inr.ruhep

This file ties into the command: ip rules show, that can give output like:

lasse@vps3:~$ ip rule show
0:      from all lookup local
32766:  from all lookup main
32767:  from all lookup default

The number in front of the rules depicts priority. The lower the number the more important is the rule.

The table local contains a list of all subnets that are directly connected to the server.

So by following the logic of ip rule show it litterally says:

   "No matter where the package originates from:

  •     1st check if the destination address a local address (aka directly connected to server).

  •     2nd is the destination subnet listed in the main routing table.

  •     If all else fails check the default routing table (aka use the default gateway).

In other words we decide where a ip package has to go based on the destination address of the ip package.

Note though I have seen that the default route is being added to the main routing table, but it really shouldn't, since that rule belongs in the default routing table. The reason will be apparent further down.

--

In contrast to this we have source based routing (also know as policy based routing).

Here we decide where a package has to go, based on where the ip package originated.

Fortunately it isn't hard to implement.

In /etc/iproute2/rt_tables we need to add another entry like:

200     vpn

We can then call ip rule with the command:

ip rule add from all to 0.0.0.0/0 iif wg0 lookup vpn

What this command does is telling Server 1 that it has to use the vpn routing table for all traffic using the inbound interface wg0 and going to anywhere else.

The command ip rule show should now show:

lasse@vps3:~$ ip rule show
0:      from all lookup local
32765:  from all to 0.0.0.0/0 iif wg0 lookup vpn
32766:  from all lookup main
32767:  from all lookup default

But the vpn routing table is empty, so we do not know how to forward the packages. Therefore let us do just that.

If we assume the VPN ip of the Mulvad gateway is 10.67.221.1 then you just need to call the command

ip route add default via 10.67.221.1 table vpn

And that is basically it, since any packages going from the client to the internet will be matched against the vpn table, while any ip package originating from Mulvad connection will be matched against the local and main routing table.

However:

If you want to use want to use the VPN connection to connect to the network behind Your device you will need to add a static route to the vpn routing table and most likely also add the same rule to the main routing table.

The syntax is simple since it is just a slight expansion on the ip route add command. In essence you just need to add the table vpn to the end of the statement to add it to the VPN routing table and table main if you want to add it to the main routing table.

See my add default route above for example.

I hope this helps to get you started.

Andy Sukowski-Bang avatar
ua flag
Note that I cannot edit the configuration of Server 2, because it's managed by the Mullvad VPN provider. I can only configure the Client and Server 1.
us flag
**Edit**: I can see you have already setup Masqurade as part of the VPN, so that simplifies things a bit, since this should in theory only leave us with the issue of source based routing. I will expand on that part then.
Andy Sukowski-Bang avatar
ua flag
I executed this command `# ip route add 10.67.221.119/32 via 10.0.0.1 table vpn` in an attempt to route traffic from _Server 2_ back to the client, but it doesn't seem to work. I ran `# ip rule add from all to 0.0.0.0/0 iif wg0 lookup vpn` as well, but that also doesn't seem to have solved the problem...
us flag
You got the purpose of the route add command wrong. First of `/32` net is a single host and you do not need to add it to the `vpn` table, since it is already listed in the `local` table. Its purpose is to tell how to get to a remote network via a given host. The `add default route` that I wrote, tells the server how to get to any network not specifically defined in the `vpn` table, which is the must be forwarded to the Mulvad gateway and due to masqurade settings in the Mulvad configuration file will make it appear as if the traffic originated from the server itself.
us flag
The `ip rule` command tells the server that for any traffic comming from any network from the remote side of the VPN link (aka from the client) must be looked up in the `vpn`table in order to find out where the traffic has to be forwarded to. The next lookup the server does it to find out how to get to the Mulwad gateway, which it can find in the `local` table. This is why the traffic gets forwarded from your VPN to Mulvad. I use similar tricks in order to provide IPv6 network at home - in spite of being located behind a carrier grade NAT and no native IPv6 from my provider.
Nate-Wilkins avatar
cn flag
This is SO helpful. You have no idea how long I've been trying to figure this out. I'm not a networking person so I am not familiar with this at all but this helps. I appear to be having trouble routing a DNS record back to my server (Server 1 in the example above) do I need to add another routing rule to resolve this?
us flag
Well you can't use DNS to access your network via the Mulvad VPN. The reason is that your connection is hinden behind the NAT running on Mulvads exit node. Even if Mulvad did not use NAT, there is still the NAT between your server and Mulvad.
I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.