Information here applies to OpenVPN version 2.3.x. For 2.4.x, more secure options are available. This post will be updated soon to include more on 2.4!
August 3, 2017: Added in #4: note about DH parameters and added the
-check option to the
March 24, 2015: Included
tls-version-min (now #12) and updated #16 to include a note about IANA TLS cipher suite names.
OpenVPN developers tend to prioritize backward compatibility over security. This is not a general bad practise, but the current OpenVPN defaults aren't that well from a security perspective, in my opnion. In this post I hope to help you with 16 practical tips to a more secure OpenVPN setup.
By following the tips in this post it will help minimizing (or even full mitigation of) risks like:
- A man-in-the-middle attack.
- Future decryption of data in case of private key compromise (by enabling forward secrecy).
- Client key compromise allowing an attacker to impersonate a server.
The post may seem very large and overwhelming, I'm aware of that possibility. It just totally depends on what your're trying to protect with your OpenVPN installation. For protecting really secure networks (confidentiality) and the need for data integrity, please read up on all of it. For just bypassing a simple firewall without the specific needs of data encryption, this post is a probably a lot less relevant to you as by default OpenVPN is secure enough for you probably.
Unless mentioned otherwise, all configuration changes apply to both client and server configuration. Most changes require the client to update his configuration as well if changed later. If you're currently running on defaults with a large amount of clients you may want to apply all changes at once and plan the transition in a maintenance window.
In this post I'm assuming you're running OpenVPN version 2.3.x with TLS authentication (certificates, opposed to pre-shared keys).
The 16 items below aren't ordered in a specific way. The first may be as important as the last in your case.
In the client configuration, verify the server certificate subject string. For example:
verify-x509-name 'C=NL, O=Gert van Dijk, CN=xlsvps.gertvandijk.net' subject
Take this measure to prevent a client using his certificate to impersonate a server.
Certificates using the X509v3 format have key usage flags set. Clients should use certificates with the "TLS Web Client Authentication" set and servers should be sending a certificate with "TLS Web Server Authentication" set. Now configure OpenVPN to check for this:
For client configurations, connecting to servers:
remote-cert-eku "TLS Web Server Authentication"
For server configurations, accepting client connections:
remote-cert-eku "TLS Web Client Authentication"
Use an additional shared secret for authenticating the TLS handshake, minimizing (D)DoS attacks. The server and all clients should share the same secret to pass the initial TLS handshake.
Generate the shared secret:
openvpn --genkey --secret tls-auth.key
For client configurations, connecting to servers:
tls-auth /path/to/tls-auth.key 1
For servers, accepting client connections:
tls-auth /path/to/tls-auth.key 0
Alternatively, embed the key and specify the key direction separately:
<tls-auth> ... </tls-auth> key-direction 0
The latter approach appears to be incompatible with OpenVPN-NL.
With pregenerated Diffie-Hellman parameters the TLS session will be enabled (but not limited to) for use with TLS ciphersuites providing forward secrecy. This means that even if a malicious user got hold of the secret keys of any of the peers, he can still not decrypt the encrypted data intercepted. More on that in the last item.
DH parameters are considered public and it's hard to verify that those generated are actually strong. Unfortunately, OpenVPN does not ship with proven strong built-in DH parameters.
Only required on a server.
Generate a 2048 bits DH parameters file:
openssl dhparam 2048 -out dh2048.pem -check
In the configuration:
For all certificates/keys, please use at least 2048 bits (RSA) as it's the minimum considered key size. By default older easy-rsa helper scripts generated all 1024 bits keys. Revoke and reissue all certificates/keys with a size lower than 2048 bits in size. If your situation allows you, use 4096 bits RSA key size.
A list of revoked certificates should be checked against to deny access with such a certificate.
When signing certificates the digital signature of the certificate to be signed is calculated. This hash (digest message) is the actual data being signed by the CA key for trusting it. It is of great importance you don't use MD5, SHA-1 or even weaker digests, because it is considered unsafe and someone could impersonate your server's or client's identity.
To check your certificate (or intermediate CA) signature, do
openssl x509 -in /path/to/cert.crt -text
And it should output lines like these:
Signature Algorithm: sha256WithRSAEncryption
Any algorithm of the SHA-2 family (SHA-256, SHA-384, SHA-512) should be fine.
In typical use of certificates, clients' private keys are everywhere in your organisation and being used on many machines. The risk of them being compromised could be mitigated with revocation, but that won't help if keys have gone stolen unnoticed.
Instead of storing private keys on regular storage, it's a lot better to use a security device like a token or smartcard. Private keys on such devices will never leave it and cryptographic operations are performed on the card itself. This minimizes the risk in a private key compromise significantly.
OpenVPN can talk to PKCS#11 compatible devices. For setting up such a device, have a look at another post: Getting started with the SmartCard-HSM.
Select the right certificate by listing all on the token:
openvpn --show-pkcs11-ids my-pkcs11-middleware.so
my-pkcs11-middleware.so is the path to the manufacturer provided middleware (shared library).
For OpenSC cards using the OpenSC provided PKCS#11 middleware, use:
openvpn --show-pkcs11-ids opensc-pkcs11.so
It will output something like this:
Certificate DN: C=NL, O=Gert van Dijk, CN=Gert van Dijk, description=ePass2003 #2 Q4 2014 VPN Serial: 15 Serialized id: EnterSafe/PKCS\x2315/7528531617051201/ePass2003\x20\x232\x20\x28User\x20PIN\x29/D08DD75984CC577F
Then use it like this in your OpenVPN client configuration:
pkcs11-id 'EnterSafe/PKCS\x2315/7528531617051201/ePass2003\x20\x232\x20\x28User\x20PIN\x29/D08DD75984CC577F' pkcs11-providers opensc-pkcs11.so
During the connection initiation the PKCS#11 middleware will ask for the security token passphrase.
While your connetion might be interrupted and OpenVPN is trying to reconnect, you may be using the default network routes again, bypassing the tunnel. For accessing private networks this might not be a big issue as the network addresses may not be reachable from outside the tunnel, but it may expose information you'd rather keep private like an HTTP request containing cookies.
To tell OpenVPN to keep the device open and to hold traffic until the connection restored, simply set the
It's quite common in OpenVPN usage to have a server to send some client configuration parameters (push) over and the client to apply these (pull).
If you just want to be sure your client configuration is exactly as you configured it, then consider to not include the
This will require you to set up everything explicitly in the client configuration, and it may be severely impacting the ease of OpenVPN client deployments.
In normal configurations you'd want the OpenVPN daemon to configure the tun/tap device for you. (E.g.: configure the interface with the IP address assigned by the server.) However, you might want to consider doing it yourself, just to be sure in avoiding any command injection. Shellshock, anyone? ;-)
From the manpage:
script-security level This directive offers policy-level control over OpenVPN's usage of exter‐ nal programs and scripts. Lower level values are more restrictive, higher values are more permissive. Settings for level: 0 -- Strictly no calling of external programs. 1 -- (Default) Only call built-in executables such as ifconfig, ip, route, or netsh. 2 -- Allow calling of built-in executables and user-defined scripts. 3 -- Allow passwords to be passed to scripts via environmental variables (potentially unsafe).
In order to prevent any form of downgrade attack on the TLS protocol level, set on both clients and server the minimum version. If your clients and servers are modern (2.3.3+), they should support TLSv1.2 just fine, so you can configure it like this:
Note that this will break OpenVPN versions 2.3.2 and earlier, which only expect TLSv1.0 handshake signatures.
All items listed above are general TLS configuration options for your PKI, basically. For further security enhancements one could use OpenVPN-NL, a fork off OpenVPN using PolarSSL instead of OpenSSL as cryptography library and having more secure defaults. The items in below don't apply to OpenVPN-NL, because it already incorporates settings strong enough, or doesn't even offer other. I recommend at least considering using OpenVPN-NL instead of OpenVPN on your server and/or clients. In the meantime, apply the remaining items to a regular OpenVPN installation; they're chosen to be compatible with OpenVPN-NL.
When using OpenVPN-NL, shell commands in this post should use
openvpn-nl rather than
Not required for OpenVPN-NL.
OpenVPN's default encryption algorithm
BF-CBC (Blowfish, block-cipher) with a 128-bit (variable) key size.
While it's certainly not a terrible or 'broken' cipher like RC4 or single-DES, I prefer a more modern and widely used cipher like AES.
Out of all other strong options, I've chosen AES-256-CBC for interoperability with OpenVPN-NL.
To see which other ciphers your version of OpenVPN supports, run
Not required for OpenVPN-NL.
Message authentication is what's referred to as HMAC.
Using a HMAC is to ensure the encrypted data hasn't been altered in transit.
OpenVPN's default setting is
SHA-1 is considered weak since 2005 and Microsoft has announced their deprecation policy for it.
The SHA-2 set of hashing algorithms are considered stronger and one should use those in favour of SHA-1 whenever possible.
Out of the other strong options, I've chosen SHA-256 for interoperability with OpenVPN-NL.
To see which other HMACs are supported by your OpenVPN, run
Not required for OpenVPN-NL.
Limiting the list of TLS ciphers is recommended, because you want to enforce a secure cipher suite for the connection. Basically, you want to strip down the list OpenVPN offers a client to the ones you think are secure. This eliminates downgrade attacks or security issues in client configurations as well as the use of plain RSA key exchange.
From the OpenVPN manpage:
lof allowable TLS ciphers delimited by a colon (":"). If you require a high level of security, you may want to set this parameter manually, to prevent a version rollback attack where a man-in-the-middle attacker tries to force two peers to negotiate to the lowest level of security they both support.
To see which TLS ciphers are supported by your OpenVPN, run
I'd recommend setting a small list of ciphers matching a commonly recommended set, for example:
The list above is basically a combination of the two strongest ciphers with regular OpenVPN (OpenSSL 1.0.1) and the two strongest offered by OpenVPN-NL, included for interoperability reasons. All of these are DHE or ECDHE enabled ciphersuites which means key exchange is done with Diffie-Hellman enabled, providing forward secrecy. ECDHE is preferred, because it is faster by the use of elliptic curve cryptography rather than the much slower plain Diffie-Hellman.
Cipher suite names have to be specified in IANA format, rather than OpenSSL format as you would normally find on the Internet.
A mapping table from IANA to/from OpenSSL cipher suite names is available in the OpenVPN source code
src/openvpn/ssl.c, for currently stable version 2.3.6 that is from line 116.
Thanks to 'sosbar' in the comments to point that out to me.
Is something unclear? Do you have a remark on this post? Did I miss out on an important thing? Please let me know! Use the comments below or send me a tweet.
Do you like the post? Feel free to retweet or share in other ways!