OpenVPN: easy-rsa

easy-rsa is a package that's meant to ease the process of becoming a Certificate Authority. I hope that the people and companies that are actual money-making Certificate Authority entities on the internet don't use it, but using OpenVPN pretty much requires you to become your own C.A. - and for that, an easing of the extreme complexity of C.A. issuance is much appreciated. easy-rsa was built specifically for and by OpenVPN, and used to be distributed with it but is now its own project.

I should acknowledge that I thought I knew enough about openssl after my recent foray into self-signed certificates that I could do this all by hand. It's a long road and a lot of pain, and eventually I gave up and came back to this.

Once the package is installed, start with this invocation:

$ make-cadir my_ca

Precisely the same result can be achieved on a Debian-based system by running cp -via /usr/share/easy-rsa/ my_ca. I prefer the latter as it's clearer to me what's happening. Each method is recommended depending on which HOWTO you're following ...

Detour: this led me to an interesting discovery. The diff command takes as input the names of two files. What if instead you have the output of two commands you want to compare? Not stackoverflow but Ask Ubuntu to the rescue: diff <(ls -1 .) <(ls -1 ../otherdir). Good trick.

$ cd my_ca
$ vim vars

The "vars" file is going to be important. And my reading of it suggests that you want to do your work in the folder it's in, because one of the lines in the file is this:

# This variable should point to
# the top level of the easy-rsa
# tree.
export EASY_RSA="`pwd`"

If you're not in this folder, that setting will be wrong.

Values I changed in vars:

export KEY_SIZE=4096 # was 2048

export CA_EXPIRE=1825 # was 3650, 10 years
export KEY_EXPIRE=1825 # was 3650, but will be forced to match CA_EXPIRE anyway

You'll also want to change KEY_COUNTRY, KEY_PROVINCE, KEY_CITY, KEY_ORG, KEY_EMAIL, and KEY_OU - all settings related to you and your organization.

Another piece of information you're going to need is the version number of OpenSSL on your system. As it turns out, finding this out is not obvious. Running openssl --version explains that "--version" isn't a valid command, and gives you a lot of information about "Standard commands" and "Cipher commands." My next thought was to run dpkg -l | grep openssl which is a bit messy, but you can ferret out the version number. Best is just openssl version (note the lack of dashes), which told me I had "OpenSSL 1.0.1k 8 Jan 2015". This is needed because the "my_ca" directory I created contains three different openssl-*.cnf files, where the name variations are versions. It seems that most of the settings in these files (at least the 1.0.0 version that I looked at) are configured to be pulled automatically from the settings you put in the "vars" file. Copy the appropriate version of the file (I used 1.0.0 because there's nothing for 1.0.1) to openssl.cnf. You can edit the new file, but be sure to leave alone information prefaced by "$ENV::" which is their way of dragging in stuff from the vars file ... which is also why you're about to source the vars file to put its values in the local environment. But I didn't find anything that needed changing in the file, so it seems they got everything important into vars.

Now run the following commands:

source ./vars # brings the info in this file into your local Environment
./clean-all # runs 'rm -rf' on the folder you set for key storage
./build-ca # generate your certificate(s)

This still runs through the questions about your location and organization, but the defaults are pulled from "vars" so you can just keep hitting Enter. You'll end up with files "ca.key" and "ca.crt" in the keys/ folder. These together constitute your "Certificate Authority." But this isn't really what we wanted from this exercise: we want TLS keys and certificates that can be used between OpenVPN instances (which the OpenVPN documentation refers to as a "server" and several "clients"). But for this to work, we first need a Certificate Authority we manage that all the instances know about. That's now established.

Build our server certificate: ./build-key-server server. The last parameter, "server", is just any name you choose for the machine we're making the certificate for - it becomes the default "Common Name," the only caveat being that each has to be unique - I think because your new C.A. database (which is the file keys/index.txt if you followed along above) files them by that name. build-key-server runs you through much the same set of queries, but there are more this time. And the last two: "Sign the certificate? [y/n]" and "1 out of 1 certificate requests certified, commit? [y/n]" will both require "y" as an answer rather than just pressing enter to default it.

WARNING: Don't muck up and re-run ./build-key-server server because the first thing it does is empty the existing key file, then it walks you through all the queries, and THEN it tells you to bugger off because its database (keys/index.txt) says a key/cert has already been issued to that name. Wait, what? You did destructive things and THEN failed out? Poor design. So if you have to re-run, change the input name. Or better, do a ./clean-all and start over again.

Likewise, create some client certificates: ./build-key client1 - where again, "client1" is just the name you've chosen. Not sure what the distinction between "build-key" and "build-key-server" is.

This will take a long time
.........+.............................................................+......................... [SNIP]

They weren't kidding: it went on for about 15-20 minutes(?) on a reasonably powered (1 core 1 thread of a lightly loaded 2.8GHz i5 with 1G memory) VirtualBox Linux. I timed it the second time: 54 minutes (does it vary that wildly, or was my guess that badly off?). This command uses the openssl dhparam ... command, and man dhparam isn't helping much: "This command is used to manipulate DH parameter files." Seriously: that's all it says. No wonder I've ended up using easy-rsa if the OpenSSL documentation is this good ... I ran openssl dhparam -in keys/dh<size>.pem -text, which theoretically shows me the parameters in "human readable form," which turns out to consist of a 15x36 block of hexadecimal numbers that's some sort of prime, and another block of encrypted - or at least encoded - text, that's described only as "DH PARAMETERS." So I'm not much the wiser.

At this point, we have all the files we need to create our VPN infrastructure.

A useful table from the OpenVPN HOWTO showing where the various files need to end up:

Filename Needed By Purpose Secret
ca.crt server + all clients Root CA certificate NO
ca.key key signing machine only Root CA key YES
dh{n}.pem server only Diffie Hellman parameters NO
server.crt server only Server Certificate NO
server.key server only Server Key YES
client<N>.crt client<N> only Client<N> Certificate NO
client<N>.key client<N> only Client<N> Key YES

If you're following along, you'll notice that this leaves behind a bunch of *.csr files: they're "Certificate Signing Requests" that are generated as an intermediary step and probably won't be of any further use (although I'm not entirely sure about that).

This is a long and detailed post, and because of that, I'm going to start another one about setting up the server and clients.