WireGuard VPN WalkthroughPublished on:
This article was last updated: March 29th 2018
Part of the Wireguard series:
- Wireguard VPN
- Routing Select Docker Containers through Wireguard VPN
- Viewing WireGuard Traffic with Tcpdump
- Leaning on Algo to route Docker traffic through Wireguard (most recent and consolidates the previous articels)
With the rise of privacy consciousness, people are looking to solutions like a hosted VPN (I hear one should never use a free service), or self hosted like streisand and algo. How does a VPN (a remote access VPN – not a site-to-site VPN for the pedantic) help maintain privacy?
In the scenario of maintaining privacy or getting around geographic content
blocking, the VPN connects you to a server, oftentimes in a different country,
where it forwards all your traffic to the intended recipient. The recipient
responds to the server, which dutifully forwards back to you. So, if you live
in the US, but are VPNed into a German server and request content from India,
India will think you’re in Germany (this assuming countries have thoughts
I’m going to show how to self host WireGuard, which bills itself as easier to configure than IPSec and OpenVPN, while being faster and more powerful. WireGuard is a component feature of of streisand, but since we’re going to be dealing with only a linux client and server setup we cut out the streisand middleman and just use WireGuard. Theoretically, this cuts down on the bloat and attack surfaces that are inherent with the wide array of software that streisand installs (streisand is planning on supporting modular install in the future).
It should be noted:
WireGuard is not yet complete. You should not rely on this code. It has not undergone proper degrees of security auditing and the protocol is still subject to change.
This demonstration will be on a DigitalOcean Ubuntu 16.04 box, but it should be easily adaptable for other platforms (as long as they are linux based).
The following script is to be executed on one’s server. This script will be subsequently broken down.
#!/bin/bash # The client's public key (generated in subsequent section client side) CLIENT_PUBLIC="<ENTER>" sysctl -w net.ipv4.ip_forward=1 add-apt-repository --yes ppa:wireguard/wireguard apt-get update apt-get install --yes wireguard-dkms wireguard-tools wg genkey | tee privatekey | wg pubkey > publickey PRIVATE=$(cat privatekey) echo "public: $(cat publickey)" cat > /etc/wireguard/wgnet0.conf <<EOF [Interface] Address = 10.192.122.1/24 SaveConfig = true ListenPort = 51820 PostUp = iptables -A FORWARD -i wgnet0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE PostDown = iptables -D FORWARD -i wgnet0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE PrivateKey = $PRIVATE [Peer] PublicKey = $CLIENT_PUBLIC AllowedIPs = 10.192.122.2/32 EOF
Simple enough, how does this work?
sysctl -w net.ipv4.ip_forward=1
Sysctl allow modifying kernel parameters at runtime, so here we’re allowing the kernel to forward packets from one network interface to another. We need this, as wireguard works by creating the VPN on another network interface (commonly called wg0 or wgnet0). This interface, by itself, does not have internet access, but with ip forwarding we can foward traffic from the VPN to the interface that can communicate with the internet.
Forwarding is only important for the server because once connected to the VPN the default client interface won’t be used anymore.
sysctl -w is only for changing kernel parameters at runtime. To persist these
settings, edit the relevant line in
If you’re interested in having the VPN initialized on start up execute
systemctl enable [email protected] after setting everything up.
add-apt-repository --yes ppa:wireguard/wireguard apt-get update apt-get install --yes wireguard-dkms wireguard-tools
These commands fetches the latest wireguard version and installs it. Since WireGuard hooks into the kernel, it attempts to automatically detect the correct kernel to hook into. This should work flawlessly.
The one problem I’ve had is that for DigitalOcean controls the kernel through their web interface (one can use a custom kernel but that is outside of the scope of this post). Anyways, if you had tried to install a custom kernel ontop of the one in DigitalOcean, wireguard will skip the correct kernel as it believes it’s chrooted. Sorry for the tangent, but since I experienced this problem, I figured I should document it for others.
wg genkey | tee privatekey | wg pubkey > publickey
Both the client and server need to generate a pair of keys. The server does not need to know the client’s private key and vice versa; however they do need to know each other’s public key to permit only authorized use of the VPN (else anyone who knew your VPN server’s address could use your VPN).
[Interface] Address = 10.192.122.1/24 ListenPort = 51820
When clients connect to the server, they can communicate directly with by using
10.192.122.1. We know that
10.192.122.1 can’t possibly be an internet
facing box because it falls under a private
/24 is a CIDR
subnet mask that states that
this VPN will is capable of housing 254 clients. WireGuard then listens on port
51820 for interested clients.
PostUp = iptables -A FORWARD -i wgnet0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE PostDown = iptables -D FORWARD -i wgnet0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
Probably the most convulted section of the config, yet this step must not be skipped. This is how those lines configure the firewall.
When the VPN is created:
- We accept packets from our VPN interface for packets being routed through the box
- Then whenever a new connection is created (eg. our client wants to access google.com, so the server needs to connect to google.com now), the outgoing packets are altered to have the server IP address so google.com responds to the server, which relays it back to the client.
Then when the VPN is destroyed everything in our firewall is deleted.
If you forget those lines, when you go to connect as a client your requests will blackhole and it may appear as if you lost internet connection.
[Peer] PublicKey = $CLIENT_PUBLIC AllowedIPs = 10.192.122.2/32
The peer section is for client information. The client that connects with the
given client public key is assigned
10.192.122.2 for their IP address.
The server is all setup so what does the client configuration look like?
[Interface] Address = 10.192.122.2/32 PrivateKey = CLIENT_PRIVATE_KEY DNS = 10.192.122.1 [Peer] PublicKey = SERVER_PUBLIC_KEY AllowedIPs = 0.0.0.0/0 Endpoint = SERVER_IP:51820
Breaking down the config:
[Interface] Address = 10.192.122.2/32 DNS = 10.192.122.1
10.192.122.2/32 matches the same address as the server.
We set the client’s DNS server to that of the VPN server. This is not needed, but I recommend it, as you want to communicate with servers (eg. google.com) that are closest to your VPN server to minimize latency. For instance, if you live in the US, VPNed into Singapore, and wanted google.com, you’d want to talk to Singapore Google server (and not the one’s in the US) so that packets travel the least distance.
These DNS queries will be forwarded to the server and need to be interpreted
by something. While you can use a
PREROUTING iptables rule to forward to your
favorite DNS server, I find it easier to have a local DNS server. I use
dnsmasq with the following config in
server=18.104.22.168 server=22.214.171.124 server=126.96.36.199 interface=wgnet0
This way, dnsmasq only listens on VPN traffic and forwards queries to Cloudflare or Google.
It is actually possible to create and destroy VPN boxes on demand for next level privacy. Here is how one would do this:
- Create DigitalOcean box by hand using the previous instructions.
- Verify that the VPN works (
wg-quick up wgnet0on both client and server).
- Take snapshot.
- Using the DigitalOcean cli one can create a new server from our snapshot (so the server will have the same public and private key, it’ll just have a new ip address)
- Update your client config to reference server’s new IP address
And just like that you can create and destory VPNs all around the world in under a minute.
If you'd like to leave a comment, please email [email protected]