Setup a VPN with cjdns

In this tutorial I will outline a simple setup for a VPN using cjdns. Cjdns will allow you to setup a secure mesh vpn which uses IPv6 internally.

Requirements

For this tutorial, I have used two client machines, both running Funtoo. A FreeBSD 11 server is used as a global connection point.

You are ofcourse able to use any other OS or distro supported by cjdns, but you may have to update some steps to work on your environment in that case.

Installation of the server

Dependencies

Before you can begin, we need some dependencies. There's only two of those, and they are available via pkg to make it even easier. Install them as follows:

pkg install gmake node

Compiling

Next up is getting the cjdns sources and compile these, as cjdns is not available as a prebuilt package:

mkdir -p ~/.local/src
cd $_
git clone https://github.com/cjdelisle/cjdns.git cjdns
cd $_
./do

To make the compiled binary available system-wide so we can use it with a system service, copy it to /usr/local/bin and rehash to make it available as a direct command:

cp cjdroute /usr/local/bin/.
hash -r

Configuring

Cjdns provides a flag to generate the initial configuration. This will provide you with some sane defaults where only a couple of small changes are needed to make it work properly. Generate these defaults with --genconf:

(umask 177 && cjdroute --genconf > /usr/local/etc/cjdroute.conf)

The umask will make all following commands write files using 600 permissions. This makes sure the config file is not readable by people who shouldn't be able to read it. Be sure to check wether the owner of the file is root!

Now you can start actually configuring the node to allow incoming connections. You have to find the authorizedPasswords array in the cjdroute.conf file and remove the contents of it. Then you can add your own machines in it. This guide follows the assumption of two clients, so the config for two clients will be shown here. You can add more clients if you wish, ofcourse.

"authorizedPasswords":
[
    {"password": "aeQu6pa4Vuecai3iebah7ogeiShaeDaepha6Mae1yooThoF0oa0Eetha9oox", "user": "client_1"},
    {"password": "aiweequuthohkahx4tahLohPiezee9OhweiShoNeephe0iekai2jo9Toorah", "user": "client_2"},
]

If you need to generate a password, you can make use of the tool pwgen, available at your local package manager. You can then generate new passwords by running pwgen 60 -1. Change the 60 around if you want passwords of a different size.

Adding a startup service

rcinit has deceptively easy scripts to make applications available as services. This in turn allows you to enable a service at startup. This way you can make sure cjdns starts whenever the server boots. You can copy the following contents directly into /usr/local/etc/rc.d/cjdroute:

#! /bin/sh

# PROVIDE: cjdroute
# KEYWORD: shutdown

#
# Add the following lines to /etc/rc.conf to enable cjdroute:
#
#cjdroute_enable="YES"

. /etc/rc.subr

name="cjdroute"
rcvar="cjdroute_enable"

load_rc_config $name

: ${cjdroute_config:=/usr/local/etc/cjdroute.conf}

command="/usr/local/bin/cjdroute"
command_args=" < ${cjdroute_config}"

run_rc_command "$1"

Afterwards, you must enable the service in /etc/rc.conf.local like follows:

echo 'cjdroute_enable="YES"' >> /etc/rc.conf.local

Installation of the clients

Dependencies

The dependencies are still on gmake and node, so simply install those on your clients. This guide assumes using Funtoo for the clients, so installation would go as follows:

emerge gmake nodejs

Compiling

Compilation is the same as for the server, so check back there for more information if you have already forgotten.

Configuring

Generating the base configuration is again done using cjdroute --genconf, just like on the server. On Funtoo, config files generally reside in /etc instead of /usr/local/etc, so you should set the filepath you write the configuration to accordingly:

cjdroute --genconf > /etc/cjdroute.conf

Setting up the connections differs as well, as the clients are going to make an outbound connection to the server, which is configured to accept inbound connections.

You should still clean the authorizedPasswords array, as it comes with a default entry that is uncommented.

Now you can setup outbound connections on the clients. You set these up in the connectTo block of cjdroute.conf. For this example, the IP 192.168.1.1 is used to denote the server IP. Unsurprisingly, you should change this to your server's actual IP. You can find the publicKey value at the top of your server's cjdroute.conf file.

On client 1, put the following in your cjdroute.conf:

"connectTo":
{
    "192.168.1.1:9416":
    {
        "login": "client_1",
        "password": "aeQu6pa4Vuecai3iebah7ogeiShaeDaepha6Mae1yooThoF0oa0Eetha9oox",
        "publicKey": "thisIsJustForAnExampleDoNotUseThisInYourConfFile_1.k"
    }
}

On client 2:

"connectTo":
{
    "192.168.1.1:9416":
    {
        "login": "client_2",
        "password": "aiweequuthohkahx4tahLohPiezee9OhweiShoNeephe0iekai2jo9Toorah",
        "publicKey": "thisIsJustForAnExampleDoNotUseThisInYourConfFile_1.k"
    }
}

That is all for configuring the nodes.

Adding a startup service

You probably want cjdroute to run at system startup so you can immediatly use your VPN. For openrc based systems, such as Funtoo, cjdns comes with a ready to use service script. To make this available to your system, copy it over to the right directory:

cp ~/.local/src/cjdns/contrib/openrc/cjdns /etc/init.d/cjdroute

Now add the service to system startup and start the service:

rc-update add cjdroute default
rc-service cjdroute start

That should be sufficient to get cjdns up and running for an encrypted VPN. You can find the IPs of each of your systems at the top of your cjdroute.conf files, in the ipv6 attribute.