How to set up a peer-to-peer fully-meshed VPN between servers

If you have deployed multiple servers on DigitalOcean or Vultr, you probably know that every server can be provided with a private IP address that is associated with the private network of the datacenter. This is great, because it allows to deploy distributed services (like databases) that communicate with each other with a latency that is close to zero.

The “issue” is that the private network is shared: that means that in the same subnet there are hundreds and possibly thousands of other servers. So at some point you’ll want to set up a Virtual Private Network (VPN) between your servers, so that they’ll be able to contact each other with custom private IP addresses (like 10.0.1.x) through an encrypted network.

Why you should use vpncloud.rs

One of the most common solutions for setting up a VPN is OpenVPN, but that’s not what I’m going to cover today. At BotFactory we have more than 10 servers hosted on DigitalOcean, and we wanted a relatively easy way to encrypt the traffic between our servers without spending hours and hours configuring the network. We wanted something that automatically discovers new and removed servers. That’s why we’ve chosen vpncloud.rs. Here are some of the highlights of vpncloud.rs:

  • it allows to bind the VPN to a custom interface, so you can create a network like 10.0.x.x/8 with all your hosts
  • it creates a fully-meshed peer-to-peer VPN network. That means that each of the hosts needs the IP address of just one another server (inside the VPN) to automatically discover all the nodes
  • the tunnel is of course encrypted, and AES-256 is supported
  • automatic reconnection when a connection is lost or a peer becomes temporarily unavailable
  • great performance
  • a pre-built .deb package is available
  • it’s open source
  • it’s incredibly easy to configure, you’ll be surprised
  • it’s stable. We’ve been running vpncloud.rs on 10 servers for almost a year, and we never had a crash or any issue. It just works.

I’m now going to explain how to configure a basic mesh P2P VPN network with vpncloud.rs.

Let’s go

First, you’ll need to install vpncloud.rs on each of the servers you’ll want to include in the virtual network.

I’m assuming the servers are running Ubuntu, but the following steps should be the same for all the Debian-based Linux distributions. If you use a different type of Linux distribution, follow the steps in the readme for compiling.

Run these commands:

wget https://github.com/dswd/vpncloud.rs/releases/download/v0.8.1/vpncloud_0.8.1_amd64.deb
sudo dpkg -i vpncloud_0.8.1_amd64.deb

A systemd service will be automatically created, together with a sample configuration file.

Configuration

The configuration file looks like this and is placed in /etc/vpncloud/example.net.disabled.

The first thing you should do is to create a copy of the sample file.

sudo cp /etc/vpncloud/example.net.disabled /etc/vpncloud/myvpn.net
sudo nano /etc/vpncloud/myvpn.net

Now let’s take a look at the content of the configuration file. The changes I’m describing here should be made on all the servers where you installed the VPN.

As I previously said, vpncloud.rs creates a full mesh, but to do so it doesn’t need the IP addresses of all the servers. In my configuration, I have configured 3 peers, which are the IP addresses of the 3 database servers (they should never be offline at the same time). Every VPN instance is able to use those peers to create connections with all the other hosts in the virtual network.

Peers

The following is a sample configuration for peers .

peers:
- privateip1:3210
- privateip2:3210

Although it is possible to put the public IP addresses of the servers in the configuration, it is recommended that you use the internal private network IP addresses (provided by DigitalOcean for eth1, for example), for better performance and lower latency. You can also use hostnames, or even put “fake” hostnames that you can then map with the private IPs in the /etc/hosts file.

Also, make sure that the port 3210 is open on the internal network on all the servers. If you need a simple firewall that allows you to open a port only on one interface, take a look at FireHOL. It’s great!

Shared key

Next, here’s a sample configuration for the shared_key option. The same key should be set on all the servers.

shared_key: "nT4gAGSP!S9!2Rjb9%h*gdVN*8NszP"

(*don’t use this key*)

This is the key that will be used to derive the symmetric key to be used with AES-256 for encrypting the VPN traffic. The advantage of using a shared key is that you don’t need to transfer complex keys between servers. It’s like a password, and it should be a string of ASCII characters. In my case, the shared key is a random string of 30 ASCII characters.

Encryption

By default, vpncloud.rs will use ChaCha20 as the encryption algorithm. On DigitalOcean servers, AES-256 is supported by the CPUs so you can set the encryption/decryption algorithm to AES-256 for better performance.

crypto: aes256

Virtual interface

Last, you should specify the ifup and ifdown options, which specify the commands that should be run by vpncloud.rs when bringing up (and down) the network interface for the VPN.

ifup: "ifconfig $IFNAME 10.0.1.1/8 mtu 1400"
ifdown: "ifconfig $IFNAME down"

According to the IP address (in CIDR notation) that you specify in the ifup command, the operating system will automatically understand that you’re going to create a network whose hosts are, in this case, in the subnet 10.x.x.x .

In my case I “grouped” the servers by changing the third octet of the address, to achieve a configuration like this:

  • 10.0.1.x reserved to database servers
  • 10.0.2.x reserved to web servers

Run boy run

After applying the above configuration on all the servers, you can start the VPN service.

sudo service vpncloud@myvpn start

where myvpn should be replaced with the name of the configuration file (excluding the .net extension).

Check for errors with sudo service vpncloud@myvpn status and then try to ping all the servers: on each server, ping 10.0.1.1 , 10.0.1.2 , etc., depending on you configuration. They should all work with a latency close to zero.

Finally, don’t forget to enable the service, so that it automatically runs at system startup.

sudo systemctl enable vpncloud@myvpn

Naming hosts

To make the hosts of the VPN more easily recognizable, you can map the IP addresses to hostnames, adding something like the following lines to the /etc/hosts file on every server.

10.0.1.1 db1.vpn
10.0.1.2 db2.vpn

Wasn’t that easy?

If you found this article useful, please clap and share :)

If you liked vpncloud.rs as I did, star the repository on GitHub, and if you are able to find a bug, contribute by creating an issue or by sending a pull request (you’ll need to understand a bit of the Rust language to do that. I don’t).

Also, take a look at the vpncloud.rs wiki on GitHub if you’re interested in learning other aspects of the VPN.