Skip to content

EasyRSA on Debian for an OpenVPN CA

After asking for useable CA Software, I have finally settled on using EasyRSA. This is what I did to come across the packaging shortcomings of EasyRSA in Debian.

Storing your CA

Be sure that your CA is stored in a secure place. Don't store it online, and make it accessible only to yourself. In my opinion, storing the the CA in a cryptoloop or dm-crypt container file on a small 32 MB USB stick is a good idea. Cryptloop, dm-crypt and LUKS are rather easy today and available in the stock Linux kernel. I commonly use grml-crypt to manage the crypto loopback stuff:

sudo grml-crypt start /media/usb4/cryptoloop $MOUNTPOINT
Gotcha: don't confuse sudo's "password" prompt with grml-crypt's "Enter LUKS passphrase" prompt, the cryptoloop password won't bring you anywhere on the sudo prompt.

Preparing the CA directory

For the CA, create a dedicated directory in the mounted cryptoloop file system. You're originally supposed to copy the entire EasyRSA directory tree in there, but I'd recommend to only link the files from your system's EasyRSA directory to automatically take advantage of distribution updates.

You'll need at least these links:

lrwxrwxrwx 1 mh mh   56 Dec 25 23:23 openssl.cnf -> /usr/share/doc/openvpn/examples/easy-rsa/2.0/openssl.cnf
lrwxrwxrwx 1 mh mh   52 Dec 25 23:22 pkitool -> /usr/share/doc/openvpn/examples/easy-rsa/2.0/pkitool
lrwxrwxrwx 1 mh mh   60 Dec 25 23:23 whichopensslcnf -> /usr/share/doc/openvpn/examples/easy-rsa/2.0/whichopensslcnf
ln -s /usr/share/doc/openvpn/examples/easy-rsa/2.0/openssl.cnf .
ln -s /usr/share/doc/openvpn/examples/easy-rsa/2.0/pkitool .
ln -s /usr/share/doc/openvpn/examples/easy-rsa/2.0/whichopensslcnf .

Configuring your CA

The only thing you cannot link is the vars file which contains your CA's local settings:

export EASY_RSA="$(pwd)"
export OPENSSL="openssl"
export PKCS11TOOL="pkcs11-tool"
export GREP="grep"
export KEY_CONFIG=$($EASY_RSA/whichopensslcnf $EASY_RSA)
export KEY_DIR="$EASY_RSA/keys"

# Issue rm -rf warning
echo NOTE: If you run ./clean-all, I will be doing a rm -rf on $KEY_DIR

export KEY_SIZE=1024
export CA_EXPIRE=3650
export KEY_EXPIRE=3650
export KEY_PROVINCE=""
export KEY_CITY=""
export KEY_ORG=""
export KEY_EMAIL=""

export PKCS11_MODULE_PATH="dummy"
export PKCS11_PIN="dummy"
Comments have been removed from this file. Better copy the file from /usr/share/doc/openvpn/examples/easy-rsa/2.0/vars and edit it to your needs. I added the two PKCS11 variables since you won't otherwise be able to issue certificate requests.

Creating the CA

Next, decide on a shell instance where you will do most operations, and source vars in there.

Next, run /usr/share/doc/openvpn/examples/easy-rsa/2.0/clean-all. If you do this in a directory of an already-in-use EasyRSA CA, you'll need to restore your backup.

To actually initialize the CA, run pkitool --initca --pass. If you do not give the --pass parameter, you'll create a CA that can issue certificates without asking for a passphrase, which might not be a brilliant idea. Choose a reasonably secure pass phrase.

Creating Certificates - the simple and suboptimal way

You can now simply proceed to create an arbitrary number of pairs of "private" keys and associated certificate by simply calling pkitool <clientname> and/or pkitool --server <servername>. Depending on your security policy, you can mandate the private keys to be protected by a passphrase (adding --pass to the command lines), but you'll have the expense of being asked for the passphrase every time you start a new openvpn daemon.

Creating Certificates the Right Way

I have put the "private" in quotes since the keys created this way are not really private: The key was created on the box hosting the CA, was stored on the local (crypted) file system and needs to be moved to the target system via a secure channel. Doing so the right way is harder than expected, so it is usually the better way to keep the private key really private by creating it directly on the target system. On the target system, you need OpenVPN installed, and openssl. After the certificate was created, you can remove openssl again.

Creating a private key is part of a key-pair generation process that also leaves a certificate request. The contents of the certificate request is public, and you can safely move it to the CA box and convert it to a certificate by signing the request. You only need to make sure that nobody exchanges your target system's certificate request for her own before you sign it as you might end up certifying a wrong identity.

For the rest of this document we're going to assume that you have ssh access to the target system and have verified the ssh host key, so that you can be reasonably sure to be connected to the right system.

To create a certificate request, you can use this script, which I have called create-easyrsa-cert-req:


set -u


export OPENSSL="openssl"
export PKCS11TOOL="pkcs11-tool"
export GREP="grep"

export KEY_CONFIG="/usr/share/doc/openvpn/examples/easy-rsa/2.0/openssl.cnf"

export KEY_DIR="$TMPDIR/keys.$$"
mkdir -p $KEY_DIR

export KEY_SIZE=1024
export KEY_EXPIRE=3650

export KEY_PROVINCE=""
export KEY_CITY=""
export KEY_ORG="Zugschlus"
export KEY_OU="$(hostname --fqdn)"
export KEY_EMAIL="mh+$(hostname)"

export DEBUG=1
umask 077
if [ "${1:-foo}" != "server" ]; then
  /usr/share/doc/openvpn/examples/easy-rsa/2.0/pkitool --csr $(hostname)
  /usr/share/doc/openvpn/examples/easy-rsa/2.0/pkitool --csr --server $(hostname)

mv $TMPDIR/keys.$$/* .
rm -rf $TMPDIR/keys.$$
When called without parameters, it creates a certificate request for an OpenVPN client certificate, and when you call it with "server" as parameter, it will create a server certificate request.

In any case, it will leave two files in the current directory. They are named after the current host name, and have the extensions .csr and .key. The .key file is your private key. keep it private! The .csr file is the certificate request which you can now move to the keys subdirectory of your CA directory via scp or other means such as an USB stick.

To do the actual signing, invoke pkitool --sign <hostname> and enter the CA passphrase. You can ignore the error message that there was no .key file to chmod. Gotcha, when signing a server certificate, use pkitool --server --sign <hostname>.

Move Certificate to the target host

You can now move the certificate to the target system. Since you're probably going to need the root certificate and the certificate revocation list as well, the .crt file, and ca.crt to the target system. These files are public, so there is no need for a secure channel. If you created the .key file on the CA system (which is not recommended), you need a secure channel to move the .key file to the target system as well.

Revoking Certificates

Since an OpenVPN server's only means of authentication is to check whether the certificate presented by a client is signed by the "right" CA, the only way to revoke VPN access is to revoke the certificate. This is also done on the CA system by calling /usr/share/doc/openvpn/examples/easy-rsa/2.0/revoke-full <client-name>. This creates a crl.pem file which contains a list of all revoked certificates. You need to have a mechanism to distribute that list to all systems that might need it, and it is recommended to have this automated. Remember, if you do not distribute the .crl file, the systems are not going to know about revoked certificates.


No Trackbacks


Display comments as Linear | Threaded

Andrei Prodan on :

"You can now move the certificate to the target system. Since you’re probably going to need the root certificate and the certificate revocation list as well, you can move the .key file (if created on the CA system), the .crt file, and ca.crt to the target system. These files are public, so there is no need for a secure channel. "

Please note that the .key file is secret and can be used to impersonate a client.

Marc 'Zugschlus' Haber on :

Ouch. Thanks for spotting this, it's the result of a misedit. I have fixed the document.

Add Comment

Markdown format allowed
Enclosing asterisks marks text as bold (*word*), underscore are made via _word_.
Standard emoticons like :-) and ;-) are converted to images.
E-Mail addresses will not be displayed and will only be used for E-Mail notifications.
Form options