Tunnels are laid, sites are connected. Now, it's your time to roam! Become the road warrior with Wireguard.
Connecting your remote locations with your home network has never been so easy!
For quite a long time I've been running 2 sites, my home lab running multiple docker containers, VMs and many more devices that utilized LAN. Then for heavy duty stuff I pay for dedicated servers hosted at commercial datacenters, these run ElasticSearch clusters, Grafana/Prometheus stack and this website for example.
Whenever I needed to connect my "on-premise" stuff with remote infrastructure, I've always done it in a dirty way via SSH tunnels, this most of the time worked fine thanks to combination of autossh running in Docker, but it involved intermediate step of setting up the tunnel, whenever I wanted to connect some service between the sites, which at times could be cumbersome. I always wanted to have a proper site to site connection, but I've never put effort into setting it up, so here we are!
I had prior experience with OpenSSL, but it just seemed too complex for this task. In the past I've also run tinc VPN which I really liked, but I felt like trying something new, so with a bit of searching around I stumbled upon Wireguard.
Some of the things I like about Wireguard
- Runs in Linux kernel, instead of userspace. This gives it the speed performance over OpenVPN or any other VPN that runs in userspace.
- Very simple configuration, you will see later down ;)
- Uses simple network interface, no need to play with taps and tuns!
Now without further ado, let's jump to mine setup.
What I need to achieve is to be able to call my home site network(
192.168.0.0/16) from my remote site. For example Dedicated server in remote site needs to be able to call a service on my home network directly via it's IP, let's say
192.168.1.200. Here is what I've done to make it work.
First off, we have to install Wireguard kernel module and the tools. I run most of my infrastructure under Debian Stable and at the time of writing this article Wireguard is not in the stable repository therefore I will have to use Backports. Luckily the process is pretty straightforward. If you use anything else refer to: Link
- Add following line into your
echo 'deb http://deb.debian.org/debian buster-backports main' >> /etc/apt/sources.list
- Afterwards update the list of available packages from the new repository you have added:
- Install Wireguard:
apt-get -t buster-backports install "wireguard" -y
Now that we have installed all the dependencies let's set this up!
The setup is fairly easy and this is the beauty of using Wireguard, however there are few things that we will cover, as it was not that clear to me from the documentation because I didn't read it thoroughly and I struggled little bit because of it.
As you can see in the diagram I show above, we will have 2 Wireguard peers. But before we proceed to exact configuration steps, we should specify baseline of our setup.
Peers should be able to handle traffic for the VPN network itself, but also for my LAN network. Therefore we can say that allowed networks should be:
10.170.1.0/24, 192.168.0.0/16. We will get to why this is necessary to specify later when we go through the configuration file.
As we have sorted out what we want to setup, we can now move to the specific configuration steps. I usually like to keep configuration files together therefore I've created following folder structure on my Peer 1 VM.
root@vpn:/data/containers/conf/vpn.m8.sk# tree . ├── certs │ ├── hertzner-box │ │ ├── hertzner.key │ │ └── hertzner.pub │ └── vpn.m8.sk │ ├── vpn.m8.sk.key │ └── vpn.m8.sk.pub ├── peers │ └── wg0_iphone_lubos.conf └── wg0.conf 5 directories, 8 files
Peer 1 - Home Site (vpn.m8.sk)
This peer is located in my home site and this is the configuration I decided to go with:
First off every single Wireguard peer needs to have its set of keys, this consist of a public and private key that is used for encryption and decryption of transmitted/received data and it is also used for the peer authentication.
Assuming we are in the folder
certs/vpn.m8.sk we can run following command to generate private and public key for my Peer 1
wg genkey | tee vpn.m8.sk.key | wg pubkey > vpn.m8.sk.pub
This will create 2 files that will have private and public key in it respectively:
root@vpn:/data/containers/conf/vpn.m8.sk# cat *.pub *key mdv9qFDGcfG5ec2Opk/fNhz84albmcdS2dlKoOeSEQg= yNdDrdkpcIdTyI11fqPDEY2lMk09I+kYbRKjA9yG320=
We repeat the same command, but this time we change to folder
certs/hertzner-box and we also adjust the output file names to
In my case I have only 2 peers, therefore I don't need any additional key pairs, however, if you would have more peers you just repeat the commands, you can also run the commands on the any of the peers, or even your machine, if you have
Now that we got our key pair we can create our initial configuration, which should look like this:
[Interface] PrivateKey = yNdDrdkpcIdTyI11fqPDEY2lMk09I+kYbRKjA9yG320= ListenPort = 51871 [Peer] PublicKey = FqB69gnqdHtsST7R3QKVMZGNfUTFGFltqrKL343xdy5= AllowedIPs = 10.170.1.2/32, 192.168.0.0/16 Endpoint = xxx.xxx.xxx.xxx:51871
I store this main configuration inside a file called
wg0.conf which is in the base of my main configuration folder in this case
The file follows INI configuration format and consists of two sections:
The interface section contains configuration properties for your VPN interface:
PrivateKeyproperty takes the value of your private key that is stored in
ListenPortproperty is the UDP port where you wish to receive VPN traffic.
If you follow guides on the Internet some of them also include configuration properties like:
These properties are used by the
wg-quick tool, but are not valid for the
The peer section contains configuration properties for your VPN peers, i.e the VPN clients that will connect to this VPN instance. For every single peer(client) that should connect to this specific VPN instance you have to set the
Peer section holds following configuration properties:
PublicKeyholds the value of public key of the peer, in this case that would be
AllowedIPsin the context of Peer section acts as a sort of ACL, i.e it tells our VPN instance what packets it should accept. In this case packets coming for
10.170.1.2/32, 192.168.0.0/16are accepted by the instance.
Endpointproperty is access interface of your peer, i.e IP and a port combination where VPN instance can send the traffic to.
AllowedIPs property has also a second use, when sending the data from the VPN instance to it's peers, in this case the property is used as a sort of routing table, telling our VPN instance that the peer will accept the traffic for networks
10.170.1.2/32, 192.168.0.0/16 This is very crucial to understand when setting up Wireguard as you might run into issues, when
AllowedIPs are not set properly.
For more examples I recommend reading the official documentation: Link. This was the part I skipped over initially when reading the documentation and it cost me some trial and error until I fixed it.
Now that we have our Wireguard configuration ready, we can move to setup of our network device that will be used by Wireguard. As promised by Wireguard, this is very simple and all you need is the
First off, you want to create a new network interface:
ip link add dev wg0 type wireguard
Then you need to assign it an IP address, we decided to use the range
10.170.1.0/24, therefore I will just use the first IP in the range for the Peer 1
ip address add dev wg0 10.170.1.1/24
Once we assign the IP, it's time to load our Wireguard configuration that we have prepared in the last step. Assuming we are in the configuration folder we setup earlier we run following command:
wg setconf wg0 wg0.conf
With all this in place we are now ready to bring up the
wg0 interface up on Peer 1. You can do that via following command:
ip link set up dev wg0
What surprised me was the status of the interface after I brought it up, however this is normal:
root@vpn:/data/containers/conf/vpn.m8.sk# ip link show wg0 3: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/none
We can now move on to the setup of Peer 2!
Peer 2 - Remote Site (hertzner-box)
I won't go into much details, as most of the details have been covered when we were preparing Peer 1. For Peer 2 we only need to prepare the configuration file and setup the network interface. Key pair doesn't need to be generated as we have already did that on Peer 1, therefore we just need to copy over the values.
Configuration for our Peer 2 should look like this:
[Interface] PrivateKey = cCPxAr8vKhQtR4fFIgZF/j3cHXcW2oD8XytOHqwFlXo= ListenPort = 51871 [Peer] PublicKey = IYFNx4xijfW0805yqfR4NIOz5JrmkPFkB5Y02y6W2AE= AllowedIPs = 10.170.1.1/32, 192.168.0.0/16 Endpoint = xxx.xxx.xxx.xxx:51871
I will now go briefly over the differences:
PrivateKeyshould contain the value of
PublicKeyshould contain the value of
As you can see the roles now switched and our home site acts as a Peer of remote site VPN instance. Make sure you don't mix up the keys in this configurations as then the authentication won't work.
Once you are done with the configuration file for the Peer 2 VPN instance we can move onto setting up the network interface.
We will repeat the same commands that we have used for Peer 1
ip link add dev wg0 type wireguard ip address add dev wg0 10.170.1.2/24 wg setconf wg0 wg0.conf ip link set up dev wg0
Please note that I have assigned different IP to the Peer 2! In this case second IP from our range, which is
Testing the VPN tunnel
With all the steps done on all of your Peers we can test the tunnel connectivity. I have no firewalls in place, therefore I just used simple
ping from both sites.
As you can see in the picture both sites respond to the ping test! This confirms that we have successfully connected remote and local site. However, we are not done yet! Remember, we wanted to access my LAN network from the Remote site! so let's try that now:
This doesn't work out of the box and we get to why in the next section.
Routing from VPN network to LAN network.
In order to route traffic between our VPN network(
10.170.1.0/24) and our LAN network(
192.168.1.0/16) we need to setup few things:
- All remote peers need to have a static route to the LAN.
- Our Wireguard peer in LAN has to act as a router for routing traffic between VPN network and LAN network.
Setting up a static route is very simple on all the remote peers run following command:
ip route add 192.168.0.0/16 dev wg0
This will make sure that all the traffic for the
192.168.0.0/16 gets routed through
In order to properly connect two networks together we will have to setup NAT on our VPN instance that is hosted in home site.
Add following iptables rules to enable the NAT:
iptables -A FORWARD -i wg0 -j ACCEPT iptables -t nat -A POSTROUTING -o enp1s0 -j MASQUERADE
wg0 is our VPN network interface,
enp1s0 is your main network interface that can reach your LAN.
Once that is set, you will need to tell the Linux kernel to forward ip packets:
sysctl -w net.ipv4.ip_forward=1
And with this in place we are done!
Now that we have managed to setup everything we can test our setup!
As you can see I am able to ping my LAN network from the remote site, I can even reach my IoT network.
Please note that all the interface configurations that we setup are temporal, meaning that they will go away after you reboot your server. If you would like to see how to set this up permanently please read this article of mine: Persistent routing for your site to site Wireguard VPN
Also the sites are connected one way, e.g I am not able to call VPN network from inside my LAN network, this is something I want to setup in the future.
I've got a bonus!! I did brief tests with iperf3 to test out the network performance. My remote site has full 1gbit connection to the outside. My home site has 300/30Mbit.
TCP without VPN
I managed to push 229Mbit/s from my remote site to my home site without VPN.
TCP with VPN.
Now the interesting part, tests via VPN. I hit whooping 200Mbit