spencer's blog about computers

wireguard

December 17, 2019

I am not fortunate enough to have good internet service where I live. The speed is alright (20/10mb down/up), latency is just okay (70-120ms). The biggest problem is I'm natted by my ISP. I don't have a public ip address, and if I need to access anything remotely, I have to tunnel through the nat.

I used to use ssh tunnels everywhere:

And for work:

The tunnels work fine, I even set up a specific ssh user with /bin/nologin shell and restricted tunnel port access on the VPS to that ssh key pair for security. However, all these tunnels are ugly and slow (tcp over tcp).

aside

I don't know if this is a quote, but it should be:

If ssh is open, every port is open.

It's trivial to “pull down” a service (e.x. postgres) on any port via ssh:

ssh -L localhost:5432:localhost:5432 user@example.com

Enter wireguard.

I'm not going to go too in depth about wireguard itself or how to install it, just talk about my practical application of it.

The first test I did was set up a wireguard tunnel from my laptop to my VPS server to use as a simple point to point tunnel.

On the server (ubuntu 18.04):

cat /etc/wireguard/wg0.conf
[Interface]
Address = 10.101.101.1/24, fd00::1/64
ListenPort = 55555
PrivateKey = <redacted>

[Peer]
PublicKey = <reacted>
AllowedIPs = 10.101.101.2/32, fd00::2/128

On the client (arch):

cat /etc/wireguard/wg0.conf
[Interface]
Address = 10.101.101.2/24, fd00::2/64
PrivateKey = <redacted>

[Peer]
AllowedIPs = 10.101.101.1/32, fd00::1/128
PublicKey = <redacted>
Endpoint = myvps.com:55555

I enabled both ends of the tunnel using the systemd template provided by wireguard-tools:

systemctl enable --now wg-quick@wg0

Dead simple. I can now ping my vps via the wireguard subnet from my laptop, and vice versa. The AllowedIPs section simply adds ip routes. Which means I can turn my vps into a private all-traffic vpn server:

Set up ip forwarding on the vps:

iptables -P FORWARD ACCEPT
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.d/99-wireguard.conf
echo "net.ipv6.conf.all.forwarding = 1" >> /etc/sysctl.d/99-wireguard.conf
sysctl -p /etc/sysctl.d/99-wireguard.conf

Edit /etc/wireguard/wg0.conf and set on-demand iptables rules to redirect traffic destined for the internet (%i here is the systemctl template identifier, aka interface name “wg0”) (append ip6tables for ipv6):

PostUp = iptables -I FORWARD -i %i -j ACCEPT; iptables -t nat -I POSTROUTING -o
eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING
-o eth0 -j MASQUERADE

Last, change AllowedIPs on the client to set all traffic to go through wireguard interface (remember this is just ip routes).

AllowedIPs = 0.0.0.0/0, ::0/0

Very cool. But not enough. I wanted to go in the opposite direction. I have a 24/7 home server that houses my plex and records my ip cameras. I wanted to be able to access the other devices on my lan when I'm out and about on my laptop, using the VPS as a “central hub”. Pretty easy to do with wireguard, because it is decentralized peer-to-peer, not necessarily a client-server architecture like openvpn. How?

home server:

[Interface]
Address: 10.101.101.3/24
PostUp = iptables -I FORWARD -i %i -j ACCEPT; iptables -t nat -I POSTROUTING -o
eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING
-o eth0 -j MASQUERADE
...

[Peer]
AllowedIPs: 10.101.101.1/24

vps:

[Interface]
PostUp = iptables -I FORWARD -i %i -j ACCEPT; iptables -I FORWARD -o %i -j
ACCEPT; iptables -t nat -I POSTROUTING -o eth0 -j MASQUERADE; iptables -t nat
-I POSTROUTING -o %i -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j
ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; iptables -t nat
-D POSTROUTING -o %i -j MASQUERADE
...

# laptop
[Peer]
PublicKey = <redacted>
AllowedIPs = 10.101.101.2/32

# entire home network!
[Peer]
PublicKey = <redacted>
AllowedIPs = 10.101.101.3/32, 192.168.1.0/24

Since both peers of the tunnel are acting as routers, when I connect my laptop to the same wireguard server and ping 192.168.1.1, I can ping my router at home at its private ip address.

It's as if I never left home.

Yes, I could have achieved this with openvpn by ssh tunneling an openvpn server running on my dev machine at home up to the cloud and setting up routing there. One of the major benefits to wireguard in this use case is the multi-lan capability, without the overhead of openvpn + ssh tunneling through nat.

I no longer have to set up complicated ssh config proxy jumps to get to my dev box and remember the hostname as devserver.remote.

I still didn't stop there. I added a static route to my router at home so that all devices on my lan can talk to anything connected to the wireguard network I created, without needing to run wireguard themselves.

ip route add 10.101.101.1/24 via 192.168.1.15

(.15 is my home server)

As I add more remote locations to my wireguard net, I can add the same ip routes at home to have “always available” connectivity from my dev machines.

Remember when I said I tunneled my ip cameras up to watch them remotely?

They were wide open on the internet before - only secured by rtsp username/password. Now I can access them at their private ip, from my phone, anywhere in the world via wireguard. The logical hops here look like:

phone -> vps -> home server -> camera

I've added security AND convenience. Not often both come together.

Remember when I said I tunneled my homeassistant api?

Now the letsencypt docker container running on my vps exposing homeassistant to https can access the private ip of homeassistant directly. So my nginx route becomes

proxy_pass http://192.168.1.15:8123;

instead of http://127.0.0.1:<ssh tunneled port>

Remember when I said I tunneled plex to my parent's house?

I repeated the same forwarding steps on my home server to the raspberry pi I set up at my parent's house (to run pihole) and added a static route to their router. Now my parents can access my plex server at 192.168.1.15, as if it was just on another subnet on their lan.

This is where the real major benefit is. Turning linux computers at remote locations into routers and connecting them to the wireguard tunnel allows me to create a seamless, secure “private lan network” across all lans I have a presence at. Adding static dns entries (either in /etc/hosts or on your home router) makes it even more user-friendly. For really advanced use cases, you can set up multiple wireguard tunnels and selectively allow specific hosts/subnets to-from locations.

Obviously you should use good judgement as to which networks you allow ip forwarding on (your own). Some people might not like finding out you are walking into their house, getting on their wifi with a “router” and exposing their entire network to your “wireguard net”.

After everything is set up, my dev machines don't have to have wireguard installed. DNS entry is in my router and the ip route redirects the ip to go through the wireguard tunnel running on my 24/7 server at 192.168.1.15.


Since wireguard is designed to run in the kernel and not userspace, it is much faster than userland implementations like openvpn. I'm able to achieve much faster speeds than I ever have on a vpn. The roaming aspect is awesome as well. There is no “lag” in connection reattempts on mobile devices either, its near instant.

There are other programs out there like sshuttle which can route an entire lan. But now I get a private vpn and access to my home network from anywhere, without tcp over tcp, all in one.

Goodbye ssh tunnels. I still keep a couple around if I'm ever traveling and something goes horribly wrong, but for the most part, I am very happy with wireguard.