Multihopping WireGuard

Table of contents

  1. Multihopping WireGuard
  2. Conclusion

Multihopping WireGuard

Multihopping is connecting to one VPN, then using that tunnel to connect to another (and if you want, using that tunnel to connect another, and so on). The main reason anyone would want this is because it makes correlation attacks more difficult. If the first server is in a jurisdiction that's not known to work with the jurisdiction the second server is in, it's unlikely they're going to share information to try to figure out your real IP. It also minimizes the data sent to a VPN provider, if you're connecting to two different VPN providers:

Some VPN providers will try to sell this feature as something that's only available with them, but the truth is that you can do this with any VPN provider. It is possible for a VPN provider to do the multihopping for you on the server's side, and that is better for speeds, but the downside is that you can't connect to a server outside of their network (like a competing VPN provider).

This page will focus on multihopping with Linux. It's probably possible in Windows, but my guess is it would be much more arduous. For example, on Linux, creating two WireGuard interfaces from the terminal is straightforward using `wg-quick`, but on Windows there's no wg-quick.exe, only a wg.exe, so you would have to recreate the functionality of wg-quick (handling the routing etc. yourself). The Windows WireGuard client did at one point have a registry hack that allowed multiple tunnels to be activated at the same time, but it seems that code has since been removed. And doing the single multihop config shown in the Triple hop section doesn't have the same effect in Windows (it just connects directly to the final hop). If you're doing something that requires the added protection of multiple tunnels, you probably shouldn't be using Windows anyways: https://www.gnu.org/proprietary/malware-microsoft.html

If you're looking for instructions on multihopping with OpenVPN, that's at https://cryptostorm.is/multihop

Double hop

Follow the instructions on https://cryptostorm.is/wireguard to get your system setup with WireGuard. You should have the `wg` and `wg-quick` commands installed, and your /etc/wireguard/ folder should look something like:

root@x:/etc/wireguard# ls
confgen.sh          cs-india.conf        cs-poland.conf
cs-atlanta.conf     cs-ireland.conf      cs-portugal.conf
cs-austria.conf     cs-la.conf           cs-romania.conf
cs-barcelona.conf   cs-latvia.conf       cs-rome.conf
cs-belgium.conf     cs-london.conf       cs-seattle.conf
cs-berlin.conf      cs-madrid.conf       cs-serbia.conf
cs-brazil.conf      cs-maine.conf        cs-singapore.conf
cs-bulgaria.conf    cs-manchester.conf   cs-sk.conf
cs-chicago.conf     cs-mexico.conf       cs-slovakia.conf
cs-czech.conf       cs-milan.conf        cs-sweden.conf
cs-dallas.conf      cs-moldova.conf      cs-switzerland.conf
cs-dc.conf          cs-montreal.conf     cs-sydney.conf
cs-denmark.conf     cs-nc.conf           cs-tokyo.conf
cs-dusseldorf.conf  cs-netherlands.conf  cs-vancouver.conf
cs-finland.conf     cs-newyork.conf      cs-vegas.conf
cs-florida.conf     cs-norway.conf       privatekey
cs-frankfurt.conf   cs-oregon.conf       publickey
cs-hungary.conf     cs-paris.conf

To avoid messing up the configs you already have (and to make things easier when you want to do a single-hop VPN), you should first make a copy of the configs for the two nodes you'll be using in this multihop chain. In this example, I'll use the Atlanta node for the first hop and the Dallas node for the second:

root@x:/etc/wireguard# cp cs-atlanta.conf hop1.conf
root@x:/etc/wireguard# cp cs-dallas.conf hop2.conf

You will need the IPs of the nodes you're going to connect to, so use `grep` to get the endpoint hostnames from hop1.conf and hop2.conf, then use `host` to lookup the IPs. And if your system doesn't have a `host` command, use `nslookup` or `dig` or whatever command you have to resolve hostnames to IPs.

root@x:/etc/wireguard# grep Endpoint hop1.conf hop2.conf
hop1.conf:Endpoint = atlanta.cstorm.is:443
hop2.conf:Endpoint = dallas.cstorm.is:443
root@x
:/etc/wireguard# host atlanta.cstorm.is
atlanta.cstorm.is has address 64.42.181.244
root@x:/etc/wireguard# host dallas.cstorm.is
dallas.cstorm.is has address 108.181.101.28
dallas.cstorm.is has address 209.58.147.38
dallas.cstorm.is has address 108.181.101.29
dallas.cstorm.is has address 209.58.150.203
dallas.cstorm.is has address 209.58.150.202
dallas.cstorm.is has address 108.181.101.70
dallas.cstorm.is has address 108.181.101.30
dallas.cstorm.is has address 108.181.101.69

Open up hop1.conf in your favorite text editor and change the Endpoint line in hop1.conf to use the IP instead of the hostname (In this example, you would change atlanta.cstorm.is to it's IP, which is 64.42.181.244). I'll use sed for my text editor:

root@x:/etc/wireguard# sed -e's/Endpoint = atlanta.cstorm.is/Endpoint = 64.42.181.244/' -i hop1.conf

While you're editing hop1.conf, go ahead and remove the default "DNS = 10.31.33.8" line too since we don't need it for the first hop (and there won't be any routing to 10.31.33.8 yet anyways):

root@x:/etc/wireguard# sed -e'/^DNS = .*/d' -i hop1.conf

Next, you're going to copy the IP of the hostname used for the endpoint in the second hop. If the hostname resolves to multiple IPs like the Dallas node does, just pick a single IP from that list (I'll use 209.58.147.38). Still in hop1.conf, change the line "AllowedIPs = 0.0.0.0/0" to "AllowedIPs = 209.58.147.38/32". I'll use sed again:

root@x:/etc/wireguard# sed -e's_AllowedIPs = 0.0.0.0/0_AllowedIPs = 209.58.147.38/32_' -i hop1.conf

Next, you're going to copy the IP of the hostname used for the endpoint in the first hop. In my example, it's 64.42.181.244, the Atlanta node. We need to exclude it from AllowedIPs in hop2.conf, but WireGuard doesn't have an inverse of AllowedIPs (like a DisallowedIPs), so we need to get a list of every possible subnet on the internet except for 64.42.181.244

Luckily, we have something on our website that makes it easy (use curl if you don't have wget):

root@x:/etc/wireguard# wget -qO- https://cryptostorm.is/wga?ip=64.42.181.244
AllowedIPs = 128.0.0.0/1, 0.0.0.0/2, 96.0.0.0/3, 80.0.0.0/4, 72.0.0.0/5, 68.0.0.0/6, 66.0.0.0/7, 65.0.0.0/8, 64.128.0.0/9, 64.64.0.0/10, 64.0.0.0/11, 64.48.0.0/12, 64.32.0.0/13, 64.44.0.0/14, 64.40.0.0/15, 64.43.0.0/16, 64.42.0.0/17, 64.42.192.0/18, 64.42.128.0/19, 64.42.160.0/20, 64.42.184.0/21, 64.42.176.0/22, 64.42.182.0/23, 64.42.180.0/24, 64.42.181.0/25, 64.42.181.128/26, 64.42.181.192/27, 64.42.181.224/28, 64.42.181.248/29, 64.42.181.240/30, 64.42.181.246/31, 64.42.181.245/32

Open up hop2.conf in a text editor and replace the 0.0.0.0/0 part of the line "AllowedIPs = 0.0.0.0/0" with that long list of subnets. I'll use sed as my text editor again:

root@x:/etc/wireguard# sed -e's_AllowedIPs = 0.0.0.0/0_AllowedIPs = 128.0.0.0/1, 0.0.0.0/2, 96.0.0.0/3, 80.0.0.0/4, 72.0.0.0/5, 68.0.0.0/6, 66.0.0.0/7, 65.0.0.0/8, 64.128.0.0/9, 64.64.0.0/10, 64.0.0.0/11, 64.48.0.0/12, 64.32.0.0/13, 64.44.0.0/14, 64.40.0.0/15, 64.43.0.0/16, 64.42.0.0/17, 64.42.192.0/18, 64.42.128.0/19, 64.42.160.0/20, 64.42.184.0/21, 64.42.176.0/22, 64.42.182.0/23, 64.42.180.0/24, 64.42.181.0/25, 64.42.181.128/26, 64.42.181.192/27, 64.42.181.224/28, 64.42.181.248/29, 64.42.181.240/30, 64.42.181.246/31, 64.42.181.245/32_' -i hop2.conf

Next, while you still have hop2.conf open in your editor, replace the hostname in the "Endpoint = dallas.cstorm.is:443" line with the IP you got from the `host` command earlier. So, again, with sed:

root@x:/etc/wireguard# sed -e's/dallas.cstorm.is/209.58.147.38/' -i hop2.conf


Now your hop2.conf should look something like:

root@x:/etc/wireguard# cat hop2.conf
[Interface]
PrivateKey = 4H5chAJHcdBGXhvizIFrs1VKYA6yvS67D6Uz8F38d0E=
Address = 10.10.200.146
DNS = 10.31.33.8

[Peer]
Presharedkey = 4aRLSgf/1AtPgPTQ+xYEP9eh2BY7hELR+Vk1LMBIT4o=
PublicKey = qrZ3+Jp0y2+eYlOE0heVBfFzcHhuWJ31Y5UF/mHQLRA=
Endpoint = 209.58.147.38:443
AllowedIPs = 128.0.0.0/1, 0.0.0.0/2, 96.0.0.0/3, 80.0.0.0/4, 72.0.0.0/5, 68.0.0.0/6, 66.0.0.0/7, 65.0.0.0/8, 64.128.0.0/9, 64.64.0.0/10, 64.0.0.0/11, 64.48.0.0/12, 64.32.0.0/13, 64.44.0.0/14, 64.40.0.0/15, 64.43.0.0/16, 64.42.0.0/17, 64.42.192.0/18, 64.42.128.0/19, 64.42.160.0/20, 64.42.184.0/21, 64.42.176.0/22, 64.42.182.0/23, 64.42.180.0/24, 64.42.181.0/25, 64.42.181.128/26, 64.42.181.192/27, 64.42.181.224/28, 64.42.181.248/29, 64.42.181.240/30, 64.42.181.246/31, 64.42.181.245/32
PersistentKeepalive = 25

If you don't have word wrapping enabled in your text editor, that AllowedIPs line will probably appear shorter. As long as the subnets after AllowedIPs are all on the same line, it's fine. I'm only word-wrapping it here to show what the line contains.

Your hop1.conf should look something like:

root@x:/etc/wireguard# cat hop1.conf
[Interface]
PrivateKey = 4H5chAJHcdBGXhvizIFrs1VKYA6yvS67D6Uz8F38d0E=
Address = 10.10.200.146

[Peer]
Presharedkey = 4aRLSgf/1AtPgPTQ+xYEP9eh2BY7hELR+Vk1LMBIT4o=
PublicKey = EZZUsR5l+Oe47s5900Q1JamvMYF6HB2Dbs6+ZAhXAzU=
Endpoint = 64.42.181.244:443
AllowedIPs = 209.58.147.38/32
PersistentKeepalive = 25


Now you're ready to bring up the interface for the first hop:

root@x:/etc/wireguard# wg-quick up hop1
[#] ip link add hop1 type wireguard
[#] wg setconf hop1 /dev/fd/63
[#] ip -4 address add 10.10.200.146 dev hop1
[#] ip link set mtu 1420 up dev hop1
[#] ip -4 route add 209.58.147.38/32 dev hop1

Then start up the second hop:

root@x:/etc/wireguard# wg-quick up hop2
[#] ip link add hop2 type wireguard
[#] wg setconf hop2 /dev/fd/63
[#] ip -4 address add 10.10.200.146 dev hop2
[#] ip link set mtu 1340 up dev hop2
[#] resolvconf -a tun.hop2 -m 0 -x
[#] ip -4 route add 64.42.181.245/32 dev hop2
[#] ip -4 route add 64.42.181.246/31 dev hop2
[#] ip -4 route add 64.42.181.240/30 dev hop2
[#] ip -4 route add 64.42.181.248/29 dev hop2
[#] ip -4 route add 64.42.181.224/28 dev hop2
[#] ip -4 route add 64.42.181.192/27 dev hop2
[#] ip -4 route add 64.42.181.128/26 dev hop2
[#] ip -4 route add 64.42.181.0/25 dev hop2
[#] ip -4 route add 64.42.180.0/24 dev hop2
[#] ip -4 route add 64.42.182.0/23 dev hop2
[#] ip -4 route add 64.42.176.0/22 dev hop2
[#] ip -4 route add 64.42.184.0/21 dev hop2
[#] ip -4 route add 64.42.160.0/20 dev hop2
[#] ip -4 route add 64.42.128.0/19 dev hop2
[#] ip -4 route add 64.42.192.0/18 dev hop2
[#] ip -4 route add 64.42.0.0/17 dev hop2
[#] ip -4 route add 64.43.0.0/16 dev hop2
[#] ip -4 route add 64.40.0.0/15 dev hop2
[#] ip -4 route add 64.44.0.0/14 dev hop2
[#] ip -4 route add 64.32.0.0/13 dev hop2
[#] ip -4 route add 64.48.0.0/12 dev hop2
[#] ip -4 route add 64.0.0.0/11 dev hop2
[#] ip -4 route add 64.64.0.0/10 dev hop2
[#] ip -4 route add 64.128.0.0/9 dev hop2
[#] ip -4 route add 65.0.0.0/8 dev hop2
[#] ip -4 route add 66.0.0.0/7 dev hop2
[#] ip -4 route add 68.0.0.0/6 dev hop2
[#] ip -4 route add 72.0.0.0/5 dev hop2
[#] ip -4 route add 80.0.0.0/4 dev hop2
[#] ip -4 route add 96.0.0.0/3 dev hop2
[#] ip -4 route add 0.0.0.0/2 dev hop2
[#] ip -4 route add 128.0.0.0/1 dev hop2

Your multihop tunnel should now be active. You can check your IP with:

root@x:/etc/wireguard# wget -qO- https://cryptostorm.is/test
209.58.147.37 IS cryptostorm

On all of our VPN servers, only one IP is used for WireGuard's exit IP, which is why this test shows the IP as 209.58.147.37 instead of 209.58.147.38 (the hop2 entry IP). Dallas has 3 physical servers behind it, and on one of them 209.58.147.37 is what all WireGuard clients would get as the exit IP, no matter which entry IP they connect to (209.58.147.38, 209.58.150.202, or 209.58.150.203).

Anyways, if you run tcpdump or any other packet sniffer against your main network interface, you can confirm that traffic leaving your system is using the IP of hop1 (64.42.181.244 in this example):

root@x:/etc/wireguard# tcpdump -nni enp0s3 & ping -c 1 yahoo.com
[1] 5397
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on enp0s3, link-type EN10MB (Ethernet), snapshot length 262144 bytes
PING yahoo.com (98.137.11.164) 56(84) bytes of data.
20:34:25.962928 IP 64.42.181.244.443 > 192.168.12.213.53515: UDP, length 272
20:34:25.966461 IP 64.42.181.244.443 > 192.168.12.213.53515: UDP, length 336
20:34:25.966807 IP 192.168.12.213.53515 > 64.42.181.244.443: UDP, length 192
20:34:26.086830 IP 64.42.181.244.443 > 192.168.12.213.53515: UDP, length 192
20:34:26.086964 IP 192.168.12.213.53515 > 64.42.181.244.443: UDP, length 192
64 bytes from media-router-fp73.prod.media.vip.gq1.yahoo.com (98.137.11.164): icmp_seq=1 ttl=52 time=120 ms

--- yahoo.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 120.071/120.071/120.071/0.000 ms

If I login to the Dallas server that hosts 209.58.147.38 and use the `wg` command to try to see what the real IP of the peer 10.10.200.146 is, I'll get:

[root@dal-1 ~]# wg | grep -B4 -A4 10.10.200.146
peer: YYIoxxHPu6S2n0YiKQzELJmz8Axb4fkEuHgjoCIme2Y=
preshared key: (hidden)
endpoint: 64.42.181.228:59594
allowed ips: 10.10.200.146/32
latest handshake: 1 minute, 43 seconds ago
transfer: 42.32 KiB received, 250.76 KiB sent
persistent keepalive: every 25 seconds

So from the Dallas server's perspective, that client is connecting from the IP used for WireGuard on the Atlanta server.

What we're basically doing here is telling the system to only use hop1 to connect to hop2, then telling hop2 to send all traffic through it, except for the IP of hop1, because that needs to be routed through the main interface (otherwise recursive routing would happen and nothing would go out).

When you want to close the multihop tunnels, you would use `wg-quick down hop2` then `wg-quick down hop1`. It's important that you take down hop2 first, because if you take down hop1 first then no traffic will go anywhere since all traffic would still be redirecting to hop2, but hop1 was the only route to it.

That's also why you don't really need a firewall preventing direct access to hop2. But if you have other programs running that might be changing your routing table, for added protection, you could setup a few iptables rules to prevent any traffic from going directly to hop2's IP. Just replace enp0s3 with your main network interface, and 209.58.147.38 with whatever the second hop's IP is:

root@x:/etc/wireguard# iptables -A OUTPUT -o enp0s3 ! -d 209.58.147.38 -j ACCEPT
root@x:/etc/wireguard# iptables -A OUTPUT -o hop1 -j ACCEPT
root@x:/etc/wireguard# iptables -A OUTPUT -o enp0s3 -j DROP

Triple hop

To add a third hop to all of this, you would set AllowedIPs in hop1 to only connect to hop2, hop2 to only connect to hop3, and hop3 to send everything to it (excluding hop1 and hop2's IPs). You can actually do all of this multihop stuff in a single config, I was just using two configs in the above examples to make it easier to separate so I could better explain the steps. 

In this triple hop example, I'll use Atlanta as the first hop, Bulgaria as the second, Dallas as the third (I'll turn off word wrap here since the subnet list gets pretty long, but there should be a scrollbar at the bottom that you can use to see the whole thing). Here's what the config would look like (with comments):

root@x:/etc/wireguard# cat triplehop.conf
[Interface]
PrivateKey = 4H5chAJHcdBGXhvizIFrs1VKYA6yvS67D6Uz8F38d0E=
Address = 10.10.200.146
DNS = 10.31.33.8

[Peer]
Presharedkey = 4aRLSgf/1AtPgPTQ+xYEP9eh2BY7hELR+Vk1LMBIT4o=
PublicKey = qrZ3+Jp0y2+eYlOE0heVBfFzcHhuWJ31Y5UF/mHQLRA=
# dallas.cstorm.is, the 3rd hop
Endpoint = 209.58.147.38:443
# Everything through hop3, except for hop1 & hop2's IP. Generated with:
# wget -qO- https://cryptostorm.is/wga?ip=37.120.152.238,64.42.181.244
AllowedIPs = 128.0.0.0/1, 0.0.0.0/3, 48.0.0.0/4, 40.0.0.0/5, 32.0.0.0/6, 38.0.0.0/7, 36.0.0.0/8, 37.128.0.0/9, 37.0.0.0/10, 37.64.0.0/11, 37.96.0.0/12, 37.112.0.0/13, 37.124.0.0/14, 37.122.0.0/15, 37.121.0.0/16, 37.120.0.0/17, 37.120.192.0/18, 37.120.160.0/19, 37.120.128.0/20, 37.120.144.0/21, 37.120.156.0/22, 37.120.154.0/23, 37.120.153.0/24, 37.120.152.0/25, 37.120.152.128/26, 37.120.152.192/27, 37.120.152.240/28, 37.120.152.224/29, 37.120.152.232/30, 37.120.152.236/31, 37.120.152.239/32, 96.0.0.0/3, 80.0.0.0/4, 72.0.0.0/5, 68.0.0.0/6, 66.0.0.0/7, 65.0.0.0/8, 64.128.0.0/9, 64.64.0.0/10, 64.0.0.0/11, 64.48.0.0/12, 64.32.0.0/13, 64.44.0.0/14, 64.40.0.0/15, 64.43.0.0/16, 64.42.0.0/17, 64.42.192.0/18, 64.42.128.0/19, 64.42.160.0/20, 64.42.184.0/21, 64.42.176.0/22, 64.42.182.0/23, 64.42.180.0/24, 64.42.181.0/25, 64.42.181.128/26, 64.42.181.192/27, 64.42.181.224/28, 64.42.181.248/29, 64.42.181.240/30, 64.42.181.246/31, 64.42.181.245/32
PersistentKeepalive = 25

[Peer]
Presharedkey = 4aRLSgf/1AtPgPTQ+xYEP9eh2BY7hELR+Vk1LMBIT4o=
PublicKey = 5JBGYHJx5cZMRWHot4yKd5fPuH7cvGupppO4p97WfQk=
# bulgaria.cstorm.is, the 2nd hop
Endpoint = 37.120.152.238:443
# dallas.cstorm.is, the 3rd hop
AllowedIPs = 209.58.147.38
PersistentKeepalive = 25

[Peer]
Presharedkey = 4aRLSgf/1AtPgPTQ+xYEP9eh2BY7hELR+Vk1LMBIT4o=
PublicKey = EZZUsR5l+Oe47s5900Q1JamvMYF6HB2Dbs6+ZAhXAzU=
# atlanta.cstorm.is, the 1st hop
Endpoint = 64.42.181.244:443
# bulgaria.cstorm.is, the 2nd hop
AllowedIPs = 37.120.152.238
PersistentKeepalive = 25


Starting it up should show:

root@x:/etc/wireguard# wg-quick up triplehop
[#] ip link add triplehop type wireguard
[#] wg setconf triplehop /dev/fd/63
[#] ip -4 address add 10.10.200.146 dev triplehop [#] ip link set mtu 1420 up dev triplehop [#] resolvconf -a tun.triplehop -m 0 -x
[#] ip -4 route add 64.42.181.245/32 dev triplehop [#] ip -4 route add 37.120.152.239/32 dev triplehop [#] ip -4 route add 37.120.152.238/32 dev triplehop [#] ip -4 route add 209.58.147.38/32 dev triplehop [#] ip -4 route add 64.42.181.246/31 dev triplehop [#] ip -4 route add 37.120.152.236/31 dev triplehop [#] ip -4 route add 64.42.181.240/30 dev triplehop [#] ip -4 route add 37.120.152.232/30 dev triplehop [#] ip -4 route add 64.42.181.248/29 dev triplehop [#] ip -4 route add 37.120.152.224/29 dev triplehop [#] ip -4 route add 64.42.181.224/28 dev triplehop [#] ip -4 route add 37.120.152.240/28 dev triplehop [#] ip -4 route add 64.42.181.192/27 dev triplehop [#] ip -4 route add 37.120.152.192/27 dev triplehop [#] ip -4 route add 64.42.181.128/26 dev triplehop [#] ip -4 route add 37.120.152.128/26 dev triplehop [#] ip -4 route add 64.42.181.0/25 dev triplehop [#] ip -4 route add 37.120.152.0/25 dev triplehop [#] ip -4 route add 64.42.180.0/24 dev triplehop [#] ip -4 route add 37.120.153.0/24 dev triplehop [#] ip -4 route add 64.42.182.0/23 dev triplehop [#] ip -4 route add 37.120.154.0/23 dev triplehop [#] ip -4 route add 64.42.176.0/22 dev triplehop [#] ip -4 route add 37.120.156.0/22 dev triplehop [#] ip -4 route add 64.42.184.0/21 dev triplehop [#] ip -4 route add 37.120.144.0/21 dev triplehop [#] ip -4 route add 64.42.160.0/20 dev triplehop [#] ip -4 route add 37.120.128.0/20 dev triplehop [#] ip -4 route add 64.42.128.0/19 dev triplehop [#] ip -4 route add 37.120.160.0/19 dev triplehop [#] ip -4 route add 64.42.192.0/18 dev triplehop [#] ip -4 route add 37.120.192.0/18 dev triplehop [#] ip -4 route add 64.42.0.0/17 dev triplehop [#] ip -4 route add 37.120.0.0/17 dev triplehop [#] ip -4 route add 64.43.0.0/16 dev triplehop [#] ip -4 route add 37.121.0.0/16 dev triplehop [#] ip -4 route add 64.40.0.0/15 dev triplehop [#] ip -4 route add 37.122.0.0/15 dev triplehop [#] ip -4 route add 64.44.0.0/14 dev triplehop [#] ip -4 route add 37.124.0.0/14 dev triplehop [#] ip -4 route add 64.32.0.0/13 dev triplehop [#] ip -4 route add 37.112.0.0/13 dev triplehop [#] ip -4 route add 64.48.0.0/12 dev triplehop [#] ip -4 route add 37.96.0.0/12 dev triplehop [#] ip -4 route add 64.0.0.0/11 dev triplehop [#] ip -4 route add 37.64.0.0/11 dev triplehop [#] ip -4 route add 64.64.0.0/10 dev triplehop [#] ip -4 route add 37.0.0.0/10 dev triplehop [#] ip -4 route add 64.128.0.0/9 dev triplehop [#] ip -4 route add 37.128.0.0/9 dev triplehop [#] ip -4 route add 65.0.0.0/8 dev triplehop [#] ip -4 route add 36.0.0.0/8 dev triplehop [#] ip -4 route add 66.0.0.0/7 dev triplehop [#] ip -4 route add 38.0.0.0/7 dev triplehop [#] ip -4 route add 68.0.0.0/6 dev triplehop [#] ip -4 route add 32.0.0.0/6 dev triplehop [#] ip -4 route add 72.0.0.0/5 dev triplehop [#] ip -4 route add 40.0.0.0/5 dev triplehop [#] ip -4 route add 80.0.0.0/4 dev triplehop [#] ip -4 route add 48.0.0.0/4 dev triplehop [#] ip -4 route add 96.0.0.0/3 dev triplehop [#] ip -4 route add 0.0.0.0/3 dev triplehop [#] ip -4 route add 128.0.0.0/1 dev triplehop

After that, https://cryptostorm.is/test should say your IP is 209.58.147.37 (Dallas).

And as before, if you run tcpdump against the main interface, traffic will appear to be going to 64.42.181.244 (Atlanta). If I login to the Bulgaria server to try to get the real IP of the WireGuard client on 10.10.200.146, I would instead see the Atlanta server's WireGuard IP:

[root@bulgaria ~]# wg | grep -B4 -A4 10.10.200.146
peer: YYIoxxHPu6S2n0YiKQzELJmz8Axb4fkEuHgjoCIme2Y=
preshared key: (hidden)
endpoint: 64.42.181.228:51371
allowed ips: 10.10.200.146/32
latest handshake: 1 minute, 43 seconds ago
transfer: 42.32 KiB received, 250.76 KiB sent
persistent keepalive: every 25 seconds

And if I used tcpdump on the server to sniff the packets of 10.10.200.146, all I would see is traffic going to 209.58.147.38 (Dallas). All of the traffic would be encrypted at this point too, so the Bulgaria server can't see what the traffic is, only where it's going. If I login to the Dallas server, the 10.10.200.146 client would appear to be coming from 37.120.152.237 (Bulgaria's WireGuard IP):

[root@dal-1 ~]# wg | grep -B4 -A4 10.10.200.146
peer: YYIoxxHPu6S2n0YiKQzELJmz8Axb4fkEuHgjoCIme2Y=
preshared key: (hidden)
endpoint: 37.120.152.237:38084
allowed ips: 10.10.200.146/32
latest handshake: 1 minute, 30 seconds ago
transfer: 104.01 KiB received, 655.07 KiB sent
persistent keepalive: every 25 seconds

Septuple hop

You probably shouldn't do this. It'll be very slow. It increased my ping time to 8.8.8.8 by about 900ms. I'm just putting this here to show that it's possible. But I'm too lazy to setup each individual hop manually, so instead I repurposed the https://cryptostorm.is/wg_confgen.txt script to create a config with all the hops:

root@x:/etc/wireguard# cat hoppity.sh
#!/bin/bash
psk=4aRLSgf/1AtPgPTQ+xYEP9eh2BY7hELR+Vk1LMBIT4o=
myip=10.10.200.146
DNS=10.31.33.8
declare -a nodes=(
"austria:S5ymECx1eYP9g3CH9SSRKWC40TnfccBqjNjkC2Y6ljo="
"belgium:qdG2OTZYTcgUfvk96NYGy2q137AF6tlOsf1C7HHR6xo="
"brazil:Z+UMUQdQKUIXOtTwd2aF59mu2ZLk+Xsrh93Xu37Vhgc="
"bulgaria:5JBGYHJx5cZMRWHot4yKd5fPuH7cvGupppO4p97WfQk="
"montreal:w8+i77Jy0/rVVXe3jGa93Yu1nbAEZiysTJb9T4QsCiw="
"vancouver:oRDFg9PmkpiukA99BM0yewysFHJpDHdVcIhsqZLCTUk="
"czech:FB/YP7iTtOL4F1SyqpPlmLOqq53UgtCMxvvkwL3ikgs="
)
declare node_ips
# dns lookup the hosts first
for node in "${nodes[@]}"; do
 host=`echo $node | awk -F: '{print $1".cstorm.is"}'`
 echo -n "Looking up $host... "
 host_ip=`host $(echo $host) | awk '{print $NF}' | shuf -n1`
 echo $host_ip
 node_ips+=("$host_ip")
done
echo Generating config...
cat > /etc/wireguard/bouncy.conf <<EOF
[Interface]
PrivateKey = `cat privatekey`
Address = ${myip}
DNS = ${DNS}

EOF
n=0
for node in "${nodes[@]}"; do
 echo "Adding hop $(( n + 1 ))..."
 host_ip=${node_ips[$n]}
 allowed_ips=`echo ${allowed_ips}${host_ip},`
 next_host=`echo ${nodes[($n + 1)]} | awk -F: '{print $1".cstorm.is"}'`
 pubk=`echo $node | awk -F: '{print $2}'`
 if [[ "$next_host" == ".cstorm.is" ]]; then
  allowed_ips=`echo $allowed_ips|sed -e's/,$//'`
  next_host_ip=`wget -qO- https://cryptostorm.is/wga?ip=$allowed_ips|sed -e's/^AllowedIPs = //'`
 else
  next_host_ip=${node_ips[$n + 1]}
 fi
cat >> /etc/wireguard/bouncy.conf <<EOF
[Peer]
Presharedkey = ${psk}
PublicKey = ${pubk}
Endpoint = ${host_ip}:443
AllowedIPs = ${next_host_ip}
PersistentKeepalive = 25

EOF
 ((n++))
done

This script makes assumptions like: you have `wget` installed, and a `shuf` command, and a `host` command. Obviously you need to change the psk= line to your actual PSK, and myip= to your 10.10.x.x IP (both of which our https://cryptostorm.is/wireguard page generates). What this script does is, using the same node list format that confgen.sh uses, iterates through each host in the node list, resolves their IPs, then iterates through it again, placing each peer inside the WireGuard config file. It sets the AllowedIPs for each hop to the next hop's IP, until it reaches the last hop. For that one it uses https://cryptostorm.is/wga?ip= along with a comma separated list of all the endpoint IPs (except the last one). It then puts the output of that /wga page into AllowedIPs for the next hop.

After changing psk/ip, place the script in /etc/wireguard/ and do a `chmod +x hoppity.sh`, then run it with `./hoppity.sh`
It'll create a bouncy.conf WireGuard config that you can bring up with `wg-quick up bouncy`. In this example, it would bounce from Austria to Belgium to Brazil to Bulgaria to Montreal to Vancouver then finally to Czech. If you want it to go through different hops, use the node list format that the https://cryptostorm.is/wg_confgen.txt script uses ("nodename:nodepublickey").

Conclusion

Not that anyone at CS would do it, but theoretically if we wanted to match a client's real IP with their outgoing traffic, we could run tcpdump on all the servers in a multihop chain. The first hop to match the client's real IP and to get the second hop's IP, the second hop's server to figure out the third hop's IP, etc. etc. until we get to the final server to sniff the client's outgoing traffic. To prevent that theoretical situation from happening, you could always combine this type of multihop with another VPN provider that supports WireGuard. Connecting to them before us would mean we can't get your real IP, but we can see your outgoing traffic (which should be encrypted, so only the metadata), and they can get your real IP but not see your outgoing traffic. Connecting to us before them would mean we can see your real IP, but not your outgoing traffic (even the metadata), and they could see your outgoing traffic but not your real IP.

Posted on