WireGuard is a NEW and FAST and MODERN VPN protocol.
It is a point-to-point VPN, which means it does not have a client-server architecture, but peers, and does not rely on a PKI, unlike OpenVPN. It is super simple to setup to connect multiple machines together.
WireGuard supports roaming, which means you can switch between network connections and not have to reconnect to your peers. On servers, it’s rarely useful, but when one of the peer is a mobile client like a laptop or a smartphone, it’s a life saver, because the usage of WireGuard is completely transparent.
In this post, I will explain how I use WireGuard on my laptop and phone, which forward all their traffic to the server while having a dual-stack connectivity.
The setup is pretty simple : we have 2 peers, one server and one client. Connecting both in a private subnet is easy. The trick to make use of the VPN to forward all of the client’s traffic trough the server is to:
- Make the client’s WireGuard interface its gateway (default route)
- Enable IP routing on the server
- Enable NAT between the WireGuard interface and public interface on the server
- Arch Linux
WireGuard comes in two parts: the tools, which will allow us to manage our peers and interfaces, and the Linux kernel module. On other platforms such as macOS, non-rooted Android, and FreeBSD, the module is replaced by userspace Go implementation.
WireGuard can run nearly anywhere, all the installation notes are on the website.
I’m usually using Debian 9 or Ubuntu 18.04 on my servers. On Debian, you need to install it from the unstable repository and on Ubuntu from a PPA.
I recommend the cheap VM from the VPS House Technology Group. You should choose the location that is the closest to you.
As for my clients, I use the macOS Go client, the Arch Linux build from the community repo, and the Android app.
Download and execute the script. Answer the questions asked by the script and it will take care of the rest.
# curl -O https://raw.githubusercontent.com/angristan/wireguard-install/master/wireguard-install.sh && chmod +x wireguard-install.sh && ./wireguard-install.sh
It will install WireGuard (kernel module and tools) on the server, configure it, create a systemd service and a client configuration file.
To generate more client files, run the following:
# ./wireguard-install.sh add-client
Make sure you choose different IPs for you clients.
Here are the steps:
- Add the WireGuard interface on the server
- Add the WireGuard interface on the client
- Add the server as a peer on the client
- Add the client as a peer on the server
- Tune the configuration to make the client’s traffic go trough the server
Configuring the WireGuard interface on the server
The configuration of WireGuard lives in
/etc/wireguard. We’ll call our interface wg0, so the config file will be
First, let’s assign IP addresses from a private subnet:
[Interface] Address = 10.66.66.1/24,fd42:42:42::1/64
Then, let’s define the port WireGuard will be listening on:
ListenPort = 443
Then, let’s generate a private key. WireGuard uses simple Curve25519 public and private keys for cryptography between the peers.
root@vpshouse ~# wg genkey AMfhWQnwtdht5HWGcW6se4AtBzb9iyTtX4XRKLo3o0A=
Add it to the configuration:
PrivateKey = <server private key>
[Interface] Address = 10.66.66.1/24,fd42:42:42::1/64 ListenPort = 443 PrivateKey = <server private key>
Configuring the WireGuard interface on the client
The configuration on the client is essentially the same.
Generate a private with
wg genkey, and assign addresses:
[Interface] PrivateKey = <client private key> Address = 10.66.66.2/24,fd42:42:42::2/64
Put this in
/etc/wireguard/wg0.conf, and start the interface:
vpshouse@mbp ~> wg-quick up wg0 [#] wireguard-go utun ... INFO: (utun1) 2019/01/27 14:36:58 Starting wireguard-go version 0.0.20181222 [+] Interface for wg0 is utun1 [#] wg setconf utun1 /dev/fd/63 [#] ifconfig utun1 inet 10.66.66.2/24 10.66.66.2 alias [#] ifconfig utun1 inet6 fd42:42:42::2/64 alias [#] ifconfig utun1 up [+] Backgrounding route monitor
Here I’m using it on macOS so the interface name is
Now that our interfaces are up, let’s configure the peers. It will allow us to make our server and our client communicate.
On the client, add this :
[Peer] PublicKey = <server public key> Endpoint = <server public ip>:443 AllowedIPs = 10.66.66.1/32,fd42:42:42::1/128
Thanks to this, all the packets destined to
AllowedIPs will be encrypted with
PublicKey and sent to
On the server, it’s basically the same, with the client private IP and without the endpoint:
[Peer] PublicKey = <client public key> AllowedIPs = 10.66.66.2/32,fd42:42:42::2/128
Adding more clients
Adding more client is a bliss.
The third peer’s configuration file will look like this:
[Interface] PrivateKey = <client 2 private key> Address = 10.66.66.3/24,fd42:42:42::3/64 [Peer] PublicKey = <server public key> Endpoint = <server public IP>:443 AllowedIPs = 0.0.0.0/0,::/0
On the server:
[Peer] PublicKey = <client 2 public key> AllowedIPs = 10.66.66.3/32,fd42:42:42::3/128
Note that the clients won’t have the other clients as peer since they don’t have valid initial endpoints (= a public IP address and open/forwarded port).
Tips and tricks
Verifying your connection
Generate a public key from a private key
If you need to get the public key from a private key, you can pipe the private key to
wg pubkey like:
wg genkey | wg pubkey
To get a pair in two files:
wg genkey | tee privatekey | wg pubkey > publickey
Or in your terminal output:
private_key=$(wg genkey) public_key=$(echo $private_key | wg pubkey) echo "private key: $private_key" echo "public key: $public_key"
IPv4, IP6, dual stack
Here, we use a dual stack VPN, and the peers connect via IPv4.
I prefer the endpoints to be IPv4 since sometimes I am on IPv4-only network but you could connect to your server via IPv6.
The privates addresses could also be IPv4 only or IPv6 only, but dual stack is the best!
Changing the client’s DNS resolvers
A little tip if you wan to change your client’s DNS resolvers upon connection. There are many reason to do this:
- With the new routes, your local network won’t be accessible. So if the DNS servers pushed by your DHCP server are in the local network, you’re screwed. (Or you add the correct route with PostUp on the client)
- You want to use a private/self-hosted DNS server
- You want to use a specific DNS server on a platform where you can’t without a VPN, like Android
As for me, I currently put DNS everywhere. It’s especially useful on my Android phone where I don’t have an ad blocker.
To specify DNS servers, add the DNS option to the client’s
[Interface] ... DNS = 126.96.36.199,188.8.131.52
Bypassing blocked ports and filtered connections
WireGuard uses UDP. A well-known way to bypass blocked ports with OpenVPN is to use TCP on the port 443 to simulate HTTPS, but it’s slower.
On both OpenVPN and WireGuard, I usually connect to the port 53 via UDP, since DNS is never blocked (unless your network does DPI…).
It’s been a long post, so let’s see how our configuration files look by now.
Peer 1 (server)
[Interface] Address = 10.66.66.1/24,fd42:42:42::1/64 PostUp = iptables -t nat -A POSTROUTING -o ens3 -j MASQUERADE; ip6tables -t nat -A POSTROUTING -o ens3 -j MASQUERADE PostDown = iptables -t nat -D POSTROUTING -o ens3 -j MASQUERADE; ip6tables -t nat -D POSTROUTING -o ens3 -j MASQUERADE ListenPort = 443 PrivateKey = <server private key> [Peer] PublicKey = <client 1 public key> AllowedIPs = 10.66.66.2/32, fd42:42:42::2/128 [Peer] PublicKey = <client 2 public key> AllowedIPs = 10.66.66.3/32, fd42:42:42::3/128
Peer 2 (client 1)
[Interface] PrivateKey = <client 1 private key> Address = 10.66.66.2/24,fd42:42:42::2/64 DNS = 184.108.40.206,220.127.116.11 [Peer] PublicKey = <server public key> Endpoint = <server public IP>:443 AllowedIPs = 0.0.0.0/0,::/0
Peer 3 (client 2)
[Interface] PrivateKey = <client 1 private key> Address = 10.66.66.3/24,fd42:42:42::3/64 DNS = 18.104.22.168,22.214.171.124 [Peer] PublicKey = <server public key> Endpoint = <server public IP>:443 AllowedIPs = 0.0.0.0/0,::/0
WireGuard is super awesome and easy to setup.
Thanks to this, I can connect safely (encryption) from nearly anywhere (port 53), get IPv6 connection (dual-stack) while blocking ads, and having great speeds!
WireGuard is still being actively developed and has received lots of support and donations. I have been using it for months to connect servers to each other (blog post incoming), and I never had any issue.