OpenVPN Configuration with Easy-RSA#
Note
This guide is work-in-progress, and will be updated accordinlgy. Please take this status into consideration.
This guide shows how to configure OpenVPN clients to login using a Nitrokey Pro 2 or a Nitrokey Storage 2. For software key management we will be using Easy-RSA, a utility that has been evolving alongside OpenVPN.
To sign the certificates, we will use a Nitrokey HSM 2 set up as Certificate Authority, however this guide does not cover the set up of the CA itself (it is clear and well documented here).
We will use Easy-RSA, because it seems to provide some flexibility, and allows key management via external PKIs. We will use it on the server to issue the signing request, and repeat the same process on the client. The Certificate Signing Requests will be signed by the CA on the Nitorkey HSM, and re-transmitted to the server and the client.
Prerequisites#
In the following documentation we will require 3 different machines as following:
OpenVPN server (v. 2.5) on Debian 10 (EC2 virtual machine - AWS)
OpenVPN client (v. 2.4.9) on Fedora 30 (local machine)
The Certificate Authority will be accessible from a standalone machine with Fedora 30 (local machine)
To interact with the devices we will require OpenSC 0.20 installed on the client and CA machine (the local machines). You can follow the instructions to set it up in this link (*Unix).
To download the dependencies on Fedora machines we can this instruction:
su -c 'dnf install readline-devel openssl-devel libxslt docbook-style-xsl pcsc-lite-devel automake autoconf libtool gcc zlib-devel'
For Debian Linux, more recent OpenSC packages are available here.
We will use the following Nitrokeys for physical key management:
An authentication key using the Nitrokey Pro 2 (pdf)
A Certificate Authority (CA) using the Nitrokey HSM 2 (pdf)
As a reminder, to build a Certificate Authority on Nitrokey HSM 2, you may follow the instructions available in the documentation.
Alternatively you may set up your own CA on a on a separate machine, or use the OpenVPN tutorial which also relies on Easy-RSA. The last 2 options rely on software solutions for key management.
Server side#
1. Install OpenVPN#
First we need to enable IP Forwarding by editing
/etc/sysctl.conf
file$ editor /etc/sysctl.confUncomment or edit accordingly the following line
net.ipv4.ip_forward=1Close after saving it, and enter this command
$ sysctl -pOnce IP forwarding is done, we will need to download the latest release of OpenvPN for our Debian 10 server, according to these instructions:
Change to root and download the GPG key that signed the package
$ sudo -s # wget -O - https://swupdate.openvpn.net/repos/repo-public.gpg | apt-key add -Add the URL of the adequate OpenVPN packages to the
sources.list
file# echo "deb http://build.openvpn.net/debian/openvpn/release/2.5 buster main" > /etc/apt/sources.list.d/openvpn-aptrepo.list # exitWe downloaded OpenVPN 2.5 as “password prompt” requires at least OpenVPN version 2.4.8 to login.
Next we download OpenVPN
$ sudo apt install openvpnIf you want to check the version, it possible by calling
--version
and print the following:$ sudo openvpn --version OpenVPN 2.5_beta3 x86_64-pc-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [PKCS11] [MH/PKTINFO] [AEAD] built on Sep 1 2020 library versions: OpenSSL 1.1.1d 10 Sep 2019, LZO 2.10 Originally developed by James Yonan Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net> Compile time defines: enable_async_push=no enable_comp_stub=no enable_crypto_ofb_cfb=yes enable_debug=yes enable_def_auth=yes enable_dependency_tracking=no \ enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown enable_fast_install=needless enable_fragment=yes enable_iproute2=yes \ enable_libtool_lock=yes enable_lz4=yes enable_lzo=yes enable_maintainer_mode=no enable_management=yes enable_multihome=yes enable_pam_dlopen=no enable_pedantic=no \ enable_pf=yes enable_pkcs11=yes enable_plugin_auth_pam=yes enable_plugin_down_root=yes enable_plugins=yes enable_port_share=yes enable_selinux=no \ enable_shared=yes enable_shared_with_static_runtimes=no enable_silent_rules=no enable_small=no enable_static=yes enable_strict=no enable_strict_options=no \ enable_systemd=yes enable_werror=no enable_win32_dll=yes enable_x509_alt_username=yes with_aix_soname=aix with_crypto_library=openssl with_gnu_ld=yes \ with_mem_check=no with_sysroot=no
2. Install Easy-RSA#
To build the PKI, we will download the latest version of Easy-RSA on the server and client machines. To get the latest release, go to the Releases page on the official EasyRSA GitHub project, copy the download link for the file ending in
.tgz
, and then paste it into the following command:
Download the latest release
$ cd ~ $ wget -P ~/ https://github.com/OpenVPN/easy-rsa/releases/download/v3.0.7/EasyRSA-3.0.7.tgzExtract the tarball
$ cd ~ $ tar xvf EasyRSA-3.0.7.tgz $ mv EasyRSA-3.0.7/ easyrsa/ # rename folder
3. Create a PKI for OpenVPN server#
Before you can create your OpenVPN server’s private key and certificate, you need to create a local Public Key Infrastructure directory on your OpenVPN server. You will use this directory to manage the server and clients’ certificate requests, instead of making them directly on your CA server.
To build a PKI directory on your OpenVPN server, you’ll need to populate a file called
vars
with some default values.
Create a
vars
file$ touch ~/easyrsa/vars $ cd easyrsa/ $ editor varsOnce the file is opened, paste in the following two lines
set_var EASYRSA_ALGO "ec" set_var EASYRSA_DIGEST "sha512"These are the only two lines that you need in this
vars
file on your OpenVPN server since it will not be used as a Certificate Authority. They will ensure that your private keys and certificate requests are configured to use Elliptic Curve Cryptography (ECC) to generate keys, and secure signatures for your clients and OpenVPN server.In regards to the choice of the cryptographic algorithms, I follow the model in this tutorial, and you can customize these according to your specific needs.
Initialize the PKI
Once you have populated the
vars
file you can proceed with creating the PKI directory. To do so, run the easyrsa script with the init-pki option:$ ./easyrsa init-pkiAfter you’ve initialized your PKI on the OpenVPN server, you are ready to move on to the next step, which is creating an OpenVPN server certificate request and private key.
4. Create server.req
and server.key
#
Now that your OpenVPN server has all the prerequisites installed, the next step is to generate a key pair composed of a private key (to keep secret), and a Certificate Signing Request (
.csr
) on your OpenVPN server.In general terms, on systems where we generate a key and request, these files are left unencrypted by using the
nopass
argument, since servers usually need to start up without any password input. This generates an unencrypted key, so mind protect its access and file permissions carefully.Tip
Configuration notes from OpenVPN:
The server, and each client, must have their own cert and key file. The server and all clients will use the same CA file.
Server certificate should have the following:
keyUsage: digitalSignature, keyEncipherment
extendedKeyUsage: serverAuth
Create the signing request for the server
Navigate to the
~/easyrsa
directory on your OpenVPN Server as your non-root user, and enter the following commands:$ cd easyrsa/ $ ./easyrsa gen-req server nopassThis will create a private key for the server and a certificate request file called
server.req
.Once you have a signed certificate, you’ll transfer it back to the OpenVPN server.
Copy the key to the OpenVPN server directory
$ sudo cp /home/admin/EasyRSA/pki/private/server.key /etc/openvpn/server/After completing these steps, you have successfully created a private key for your OpenVPN server. You have also generated a Certificate Signing Request for the OpenVPN server.
Tip
File extensions for certificate signing requests
The file extension that is adopted by the CA and HSM tutorial indicates the creation of a
.csr
file, however Easy-RSA creates certificate signing requests with a.req
extension.We will use interchangeably both extensions, while making sure that we transfer the right files to the Certificate Authority, and generate a final certificate with a
.crt
extension.In the next section of this guide, we will sign a
.req
file with our CA on deployed on the HSM 2 device. For this purpose, I will use a dedicated machine to sign the requests.
5. Sign and retrieve server.crt
#
The following instructions require the transfer of the
server.req
(orserver.csr
) file to the CA system.The transfer itself is not security sensitive, though it is wise to verify if the received file matches the sender’s copy, if the transport is untrusted.
In order to go through these steps, I will extensively rely on these instructions, to sign the certificate signing requests, once we generated them with Easy-RSA.
Sign the
server.req
fileOn the local machine dedicated to access the HSM, we will use the tools provided by Opensc 0.20 in order to sign the
.req
file, and send it back to the OpenVPN server. We assume we have transferred the file from the server machine to the CA machine.First we start by plugging the HSM Nitrokey, and enter this instruction for listing the keys available.
Query the list of available devices
$ p11tool --list-all **(Required step)** If this is the first time you sign a certificate with the CA, you might want to retrieve the URI of the CA’s private key from the HSM, and include it in the config file.
The key’s URI should be in this format:
pkcs11:model=PKCS%2315%20emulated;manufacturer=www.CardContact.de;serial=DENK0104068;token=SmartCard-HSM%20%28UserPIN%29%00%00%00%00%00%00%00%00%00;id=%E0%16%1C%C8%B6%F5%D6%6A%C6%83%5E%CD%EC%B6%23%FC%05%06%A6%75;object=root;type=privateCreate
openvpn/
directory undercertificate-authority/
$ mkdir/opt/certificate-authority/ $ cd /opt/certificate-authority/Sign the
server.req
$ openssl ca -config sign_server_csrs.ini -engine pkcs11 -keyform engine -days 375 -notext -md sha512 -create_serial -in server.req -out /home/user/pki/issued/server.crtRetrieve the
server.crt
file to the server machine
Transfer the signed certificates to the server
From the CA machine, copy the files
server.crt
andchain.crt
to the OpenVPN server. In this example we will use thescp
command as following:$ scp openvpn/{server.crt,chain.crt} admin@your_openvpnserver_ip:/tmpPlace the certificates on the server’s directory
$ mv /tmp/{server.crt,chain.crt} /etc/openvpn/serverWarning
CA Certificate and
chain.crt
In the above, the CA returns the signed sever certificate, and includes the CA certificate
CA.crt
which is thechain.crt
file. This can be done over an insecure channel, though the client is encouraged to confirm if the receivedchain.crt
is valid, if the transport is untrusted.It is possible to rename the file
chain.crt
file toCA.crt
on the target machine, however we will usechain.crt
in the next instructions.
6. Configure the OpenVPN server#
A connection that uses TLS requires multiple certificates and keys for authentication. Now that we issued and signed those, we can place them in the right directories. The breakdown of the certificates and keys that must be located at the root directory are the following:
OpenVPN server - The root certificate file (CA.crt or chain.crt in our setup) - Server certificate - Server key - Diffie Hellman Parameters (optional)On your OpenVPN server, now you can create the configuration file
server.conf
with your favorite text editor. The file can be configured according to your needs, while we make sure to change the server certificate and key sections according the names you chose for the your the files we signed:# OpenVPN Server Certificate - CA, server key and certificate ca chain.crt cert server.crt key server.keyHere is the configuration file we can use for testing these instructions:
port 1194 proto udp dev tun ca ca.crt cert server.crt key server.key # This file should be kept secret dh dh.pem server 10.8.0.0 255.255.255.0 push "redirect-gateway def1 bypass-dhcp" push "dhcp-option DNS 208.67.222.222" push "dhcp-option DNS 208.67.220.220" keepalive 10 120 tls-auth ta.key 0 # This file is secret cipher AES-256-CBC user nobody group nogroup persist-key persist-tun status /var/log/openvpn/openvpn-status.log log /var/log/openvpn/openvpn.log log-append /var/log/openvpn/openvpn.log verb 3 explicit-exit-notify 1 tls-version-min 1.2 # Lower boundary for TLS version tls-version-max 1.2 # Higher boundary for TLS versionTo test if the configuration functions properly, we can use this command:
$ sudo openvpn --server --config server.conf
7. Start the OpenVPN service on the server#
Enable the OpenVPN service by adding it to systemctl, and start it using these commands:
$ sudo systemctl -f enable openvpn@server $ sudo systemctl start openvpn@serverTo Double check if the OpenVPN service is active use this command:
$ sudo systemctl status openvpn@serverThe OpenVPN should be running at this point.
Client side configuration#
1. Install OpenVPN and Easy-RSA#
Install the software
We can use directly
dnf install
to install OpenVPN 2.4.9 and Easy-RSA 3.0.7$ sudo dnf install openvpn easy-rsaThen we create as non-root a directory for Easy RSA called
Easy-RSA
$ mkdir ~/easyrsaAnd link it to the Easy RSA package we just installed
$ ln -s /usr/share/easy-rsa/3/* ~/easyrsa/
2. Create a PKI for the OpenVPN client#
In the same manner we created a PKI on the OpenVPN server, we will create a PKI using Easy-RSA on the client side.
3. Create a client.req
and client.key
#
In the same manner we issued the key pair on the sever, we generate a key pair for the client which will be composed of the
client.req
file and theclient.key
file. The latter must be kept secret on the client machine.
4. Sign client.req
and issue the client.crt
file#
To transfer the
client.req
file to the CA machine, we will use the same method as we did for theserver.req
file.Once transferred, on the CA machine we sign the certificate signing request file with this command
$ openssl ca -config sign_server_csrs.ini -engine pkcs11 -keyform engine -days 375 -notext -md sha512 -create_serial -in client.req -out /home/user/pki/issued/client.crt
5. Import client.crt
on the Nitrokey from the CA machine#
After creating the
client.crt
file, we plug the Nitrokey Pro 2 device in the CA machine, and import the.crt
to the Pro 2 device using this command:$ pkcs15-init --store-certificate client.crt --id 3You can see if the key is effectively stored on the Nitrokey using this command:
$ pkcs15-tool -cOr alternatively
$ pkcs11-tool --list-objectsFore more commands you can refer to the OpenSC wiki.
6. Retrieve the chain.crt
file from the CA machine#
While we keep the
client.crt
stored on the nitrokey Pro 2 device, we must retrieve thechain.crt
file on the client machine, and store it in the adequate directory. We may usescp
as in the method explained in the server section of this guide.
7. Configure the client to interact with the Nitrokey#
Now back on the client machine, we will plug the Nitrokey Pro and use it to establish the VPN connection with the server. In general terms, a connection that uses TLS requires multiple certificates and keys for authentication:
OpenVPN client - The root certificate file (`chain.crt`) - Client certificate - Client keyFor this guide we can the following
client.conf
file, and add the required options to it accordingly:client dev tun proto udp remote <server> 1194 resolv-retry infinite nobind user nobody group nobody persist-key persist-tun ca ca.crt remote-cert-tls server cipher AES-256-CBC verb 3 redirect-gateway def1 tls-version-min 1.2 # Lower boundary for TLS version tls-version-max 1.2 # Higher boundary for TLS version
Determine the correct object
Each PKCS#11 provider can support multiple devices. In order to view the available object list you can use the following command:
$ openvpn --show-pkcs11-ids /usr/lib64/pkcs11/opensc-pkcs11.so The following objects are available for use. Each object shown below may be used as parameter to --pkcs11-id option please remember to use single quote mark. Certificate DN: CN=client Serial: E53DA75C5B8F1518F520BCEF0128C09F Serialized id: pkcs11:model=pkcs11:model=PKCS%NNNN%20emulated;token=User%20PIN%20%28OpenPGP%20card%29;manufacturer=ZeitControl;serial=000NNNNNN;id=%03Each certificate/private key pair have unique
Serialized id
string. The serialized id string of the requested certificate should be specified, in the configuration file. We can do this by adding thepkcs11-id
option using single quote marks.pkcs11-id 'pkcs11:model=pkcs11:model=PKCS%NNNN%20emulated;token=User%20PIN%20%28OpenPGP%20card%29;manufacturer=ZeitControl;serial=000NNNNNN;id=%03'Add retrieved Serialized ID to the configuration file
Using your favorite text editor, open the server.conf file, and add the following lines, while taking care to insert your own
Serialized id
:pkcs11-providers /usr/lib64/pkcs11/opensc-pkcs11.so pkcs11-id 'pkcs11:model=pkcs11:model=PKCS%NNNN%20emulated;token=User%20PIN%20%28OpenPGP%20card%29;manufacturer=ZeitControl;serial=000NNNNNN;id=%03'For additional settings related to OpenVPN authentication, you may also add few lines to handle key maganagement, although it is optional.
Note
Click to view the code
# nitrokey config pkcs11-providers /usr/lib64/pkcs11/opensc-pkcs11.so pkcs11-id 'pkcs11:model=pkcs11:model=PKCS%NNNN%20emulated;token=User%20PIN%20%28OpenPGP%20card%29;manufacturer=ZeitControl;serial=000NNNNNN;id=%03' # pkcs11-pin-cache 300 # daemon # auth-retry nointeract # management-hold # management-signal # management 127.0.0.1 8888 # management-query-passwords pkcs11-cert-private 1 # Prompt for PINOptional step
If you need to test the configuration, with and without the token on the Nitrokey, you may add lines to the same
client.conf
and comment/uncomment the relevant lines according to your needs:Note
Click to view the code
# non_nitrokey login # cert client.crt # key client.key # tls-auth ta.key 1Configure the OpenVPN client
The final configuration file
client.conf
should look like this one:client dev tun proto udp remote <server> 1194 resolv-retry infinite nobind user nobody group nobody persist-key persist-tun ca ca.crt remote-cert-tls server cipher AES-256-CBC verb 3 redirect-gateway def1 tls-version-min 1.2 # Lower boundary for TLS version tls-version-max 1.2 # Higher boundary for TLS version # nitrokey login pkcs11-providers /usr/lib64/pkcs11/opensc-pkcs11.so pkcs11-id 'pkcs11:model=pkcs11:model=PKCS%NNNN%20emulated;token=User%20PIN%20%28OpenPGP%20card%29;manufacturer=ZeitControl;serial=000NNNNNN;id=%03' # pkcs11-pin-cache 300 # daemon # auth-retry nointeract # management-hold # management-signal # management 127.0.0.1 8888 # management-query-passwords pkcs11-cert-private 1 # Prompt for PIN # OR # non_nitrokey login # cert client.crt # key client.key # tls-auth ta.key 1Configure OpenVPN
In order to establish a handshake, you must configure OpenSSL included in OpenVPN.
Create the directory
ssl
inC:\Program Files\OpenVPN
and create fileopenssl.cnf
with the following content :openssl_conf = default_conf
[ default_conf ] ssl_conf = ssl_sect
[ ssl_sect ] system_default = ssl_default_sect
[ ssl_default_sect ] SignatureAlgorithms = RSA+SHA512:ECDSA+SHA512:RSA+SHA384:ECDSA+SHA384:RSA+SHA256:ECDSA+SHA256 MaxProtocol = TLSv1.2 MinProtocol = TLSv1.2
With this modification, you will not have error as reported here, here and here
Known issues
There are some known issues related to OpenVPN login with OpenSC. Please consult these issues here.
8. Start the OpenVPN client#
Start the OpenVPN service on the client
Enable the OpenVPN service, and start it using these commands:
$ sudo systemctl -f enable openvpn-server@server.service $ sudo systemctl start openvpn-server@server.serviceTo double check if the OpenVPN service is active use this command:
$ sudo systemctl status openvpn-server@server.serviceEnter your User PIN
When executing OpenVPN client, Nitrokey’s PIN needs to be entered:
$ sudo openvpn --client --config client.conf Fri Sep 11 17:42:01 2020 OpenVPN 2.4.9 x86_64-redhat-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [PKCS11] [MH/PKTINFO] [AEAD] built on Apr 24 2020 Fri Sep 11 17:42:01 2020 library versions: OpenSSL 1.1.1g FIPS 21 Apr 2020, LZO 2.08 Fri Sep 11 17:42:01 2020 PKCS#11: Adding PKCS#11 provider '/usr/lib64/pkcs11/opensc-pkcs11.so' Enter User PIN (OpenPGP card) token Password: ******In some reported cases it does not prompt for a PIN on the terminal. One workaround would be to use to use this command to login with the PIN:
$ telnet 8888 password 'User PIN (OpenPGP card) token' <PIN>Alternatively, you could recompile OpenVPN client with systemd support disabled, and it will prompt you for the PIN as expected.
Another option, would be to login to your OpenVPN instance with the Viscosity client which provides a better user experience especially for entering the PIN.