Joining disparate hosts into a VPN with gvpe

Posted by Steve on Wed 20 Nov 2013 at 22:04

The GNU Virtual Private Ethernet software allows you to join multiple distinct hosts into a small private network, via a static set of public/private keys. It is ideally suited to joining a small number of hosts in a secure fashion.

I use gvpe to allow all the virtual machines I run to have a secure link between each other. Imagine you have a typical cluster:

If you assign each host a private address and configure them all to be in a VPN then traffic between them will be encrypted. That means even if your hosting company were to sniff your traffic they wouldn't ever see your database queries over the wire. (Of course if you don't trust your hosting company you've got bigger problems; they could clone your disks, subvert your hardware, or worse.)

I chose gvpe because it is very simple to setup, has a low overhead, and can be configured easily for a small and static list of hosts.

Installation of the software is very simple:

apt-get install gvpe

Once that is done we'll need to configure it. That involves three simple steps:

The first is simple. In our case we're going to configure a VPN between the two hosts one.example.com and two.example.com. So our configuration file /etc/gvpe/gvpe/gvpe.conf will look like this:

enable-udp = yes
udp-port   = 407
ifname     = vpn0

node     = one
hostname = one.example.com

node     = two
hostname = two.example.com

The configuration has some boiler-plate at the top to configure how the VPN will be setup, in our case using UDP traffic over port 407, and the name of the networking devices on each node (vpn0).

Now that we've configured the list of nodes we need to generate the keys:

# gvpectrl --config /etc/gvpe/ --generate

This will poluate the two directores /etc/gvpe/hostkeys, and /etc/gvpe/pubkey with some static keys, which are used for the encryption, and session negotiation. You need to generate the keys on only one node, then copy them to any other nodes - Every node must have identical keys.

In addition to creating keys for all nodes in the mesh we need to ensure that the host key for the local host is present. So we'd run:

# On the first node
root@one:~# ln -s /etc/gvpe/hostkeys/one /etc/gvpe/hostkey

# On the second node.
root@two:~# ln -s /etc/gvpe/hostkeys/two /etc/gvpe/hostkey

(i.e. The keys are named after the node-name, but we need to ensure the key for the current host is located at /etc/gvpe/hostkey.)

The final step is to write a simple shell-script which will be launched when the link(s) are established. Create the file /etc/gvpe/if-up, with contents like so:

#!/bin/sh

#
# Set the MTU + MAC address
#
ip link set $IFNAME address $MAC mtu $MTU up

#
# Add an IP to the 'vpn0' device.
#
ip add add 10.0.0.1 dev $IFNAME

#
# Route the 10.0.0.0-255 range via this device.
#
ip route add 10.0.0.0/8 dev $IFNAME

NOTE: The script here will give the IP address 10.0.0.1 to the node it runs on. To be more dynamic, but still allow /etc/gvpe to be identical on all nodes you could rework the script like so:

#!/bin/sh

#
# Set the MTU + MAC address
#
ip link set $IFNAME address $MAC mtu $MTU up

#
# Add an IP to the 'vpn0' device - give a different IP for each node
#
case $NODENAME in
  one)
     ip add add 10.0.0.1 dev $IFNAME
     ;;
  two)
     ip add add 10.0.0.2 dev $IFNAME
     ;;
esac

#
# Route the 10.0.0.0-255 range via this device.
#
ip route add 10.0.0.0/8 dev $IFNAME

This script works because the nodename of the current host will be passed along with the other variables you see referenced such as $IFNAME, and $MAC.

By the time you've reached this step you'll have the following structure:

/etc/gvpe/
├── gvpe.conf  - The configuration file.
├── hostkey    - The key for this host, linked to from ./hostkeys
├── hostkeys
│   ├── one    - Private keys for the nodes.
│   └── two
├── if-up      - The script to launc the devices and configure the routing.
└── pubkey
    ├── one    - Public keys for the nodes.
    └── two

2 directories, 7 files

The only remaining step is to launch the link:

# On one side.
root@one:~# gvpe --config /etc/gvpe/ -D one

# On another side.
root@two:~# gvpe --config /etc/gvpe/ -D two

The launch command includes the name of the current node, which is why we prefer to name nodes after hostnames. It makes it nice and clear. If all goes well you'll see something like this:

root@one ~ # gvpe --config /etc/gvpe/ -D one
gvpe daemon 2.24 (Nov 15 2011 20:40:14) starting up.
two(udp/x.x.x.x:407): connection established (direct), protocol version 0.1.
..

Repeat the matching command on the other side and you'll have a two-way link, which you can verify via:

root@one ~ # ip -4 addr list dev vpn0
19: vpn0:  mtu 1422 ..
    inet 10.0.0.1/32 scope global vpn0

root@one ~ # ip -4 route list dev vpn0
10.0.0.0/8  scope link

Finally to allow the daemon to be launched on bootup you can update /etc/default/gvpe to read:

START_DAEMON="1"
DAEMON_ARGS="--config /etc/gvpe/ one"

Here I'm using "one" as the node-name to be nice and clear. In my case I name nodes after the local hostnames, so I can instead write ...$(hostname --short)"

At the end of this article you should be able to quickly setup a simple VPN containing 2+ hosts. There are many more options you can experiment with, which are documented via "man gvpe.conf", "man gvpe", and "man gvpectrl".


This article can be found online at the Debian Administration website at the following bookmarkable URL (along with associated comments):

This article is copyright 2013 Steve - please ask for permission to republish or translate.