3

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.

1

1 Answer 1

1

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.

7
  • 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. Nov 4, 2022 at 7:08
  • 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. Nov 4, 2022 at 17:32
  • 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... Nov 5, 2022 at 10:43
  • 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. Nov 5, 2022 at 11:49
  • 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 vpntable 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. Nov 5, 2022 at 11:58

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .