openvpn / hardened fox-it openvpn-nl
Today, we will be evaluating OpenVPN-NL — “[a] hardened version of OpenVPN that includes as many of the security measures required to operate in a classified environment as possible — and whether we can use it as a drop-in replacement for regular OpenVPN.
While OpenVPN allows many insecure configurations, such as turning off encryption, or the use of outdated cryptographic functions in security critical places, the goal of OpenVPN-NL — a fork created and maintained by Fox-IT — is to strip insecure configuration and verify that the distributed version is uncompromised.
We'll be answering the question of whether it's compatible and whether we want to use it.
For Ubuntu Bionic and Xenial, repositories exist. But the Bionic version works just fine on Ubuntu Focal.
OpenVPN | OpenVPN-NL | |
---|---|---|
repo | ubuntu default | fox-it repository |
package | openvpn | openvpn-nl |
version | 2.4.7-1ubuntu2 | 2.4.7-bionicnl1 |
dependencies | lzo2-2, lz4-1, pkcs11, ssl, systemd0, iproute2 | lzo2-2, net-tools, (embedded) Mbed TLS 2.16.2 |
size | 1160 KiB | 1627 KiB |
binary | /usr/sbin/openvpn | /usr/sbin/openvpn-nl |
systemd notify | YES | - |
As you can already see in the above list:
- the versions are similar (2.4.7);
- OpenVPN is linked to OpenSSL while OpenVPN-NL embeds Mbed
TLS. This means that:
- it is not affected by OpenSSL specific security issues,
- but it will be affected by Mbed TLS issues and we'll have to rely on updates from Fox-IT, should such issues arise.
- OpenVPN-NL can be installed alongside OpenVPN, which makes switching between the two convenient;
- it depends on older networking tools (net-tools);
- it does not support
sd_notify
— you'll have to disableType=notify
in your SystemD service files.
On to the hardening bits
The hardening done by Fox-IT appears to consist of the following changes:
- Mbed TLS is used instead of OpenSSL:
- if you assume that OpenSSL is riddled with flaws, then this is a good thing;
- if you assume that any security product, including Mbed TLS will have its flaws, then a drawback is that you get fewer features (no TLS 1.3) and that you have to rely on timely patches from Fox-IT.
- OpenVPN-NL drastically limits the allowed cryptography algorithms — both on the weak and on the strong side of the spectrum — leaving you with really no option but SHA256, RSA and AES-256;
- it enforces a few options that you should have enabled, like
certificate validation, and specifically
remote-cert-tls
to prevent client-to-client man in the middle attacks; - it removes a few options that you should not have enabled, like
no-iv
,client-cert-not-required
or optionalverify-client-cert
; - certificates must be signed with a SHA256 hash, or the certificates will be rejected;
- it delays startup until there is sufficient entropy on the system
(it does so by reading and discarding
min-platform-entropy
bytes from/dev/random
, which strikes me as an odd way to accomplish that) — during testing you can setmin-platform-entropy 0
.
Note that we're only using Linux, so we did not check any Windows build scripts/fixes that may also be done. The included PKCS#11 code — for certificates on hardware tokens — was not checked either at this point.
The available algorithms:
OpenVPN | OpenVPN-NL | |
---|---|---|
--show-digests |
.. lots and lots .. | SHA256 |
--show-tls |
.. anything that OpenSSL supports, for TLS 1.3 and below .. | TLS 1.2 (only) with ciphers: TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384 + TLS-DHE-RSA-WITH-AES-256-GCM-SHA384 + TLS-DHE-RSA-WITH-AES-256-CBC-SHA256 |
--show-ciphers |
.. lots and lots .. | AES-256-CBC + AES-256-GCM |
Notable in the above list is that SHA512 is not allowed, nor are ECDSA ciphers: so no new fancy ed25519 or secp521r1 elliptic curve (EC) ciphers, but only plain old RSA large primes. (The diff between openvpn-2.4.7-1ubuntu2 and Fox-IT bionicnl1 even explicitly states that EC is disabled, except for during the Diffie-Hellman key exchange. No motivation is given.)
So, compatibility with vanilla OpenVPN is available, if you stick to this configuration, somewhat.
Server settings:
mode server
tls-server
Client settings:
client # equals: pull + tls-client
Server and client settings:
local SERVER_IP # [server: remote SERVER_IP]
proto udp
port 1194
nobind # [server: server VPN_NET 255.255.255.0]
dev vpn-DOMAIN # named network devices are nice
dev-type tun
# HMAC auth, first line of defence against brute force
auth SHA256
tls-auth DOMAIN/ta.key 1 # [server: tls-auth DOMAIN/ta.key 0]
key-direction 1 # int as above, allows inline <tls-auth>
# TLS openvpn-nl compatibility config
tls-version-min 1.2
#[not necessary]#tls-version-max 1.2 # MbedTLS has no 1.3
# DH/TLS setup
# - no ECDSA for openvpn-nl
# - no TLS 1.3 for openvpn-nl
tls-cipher TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384
tls-ciphersuites TLS_AES_256_GCM_SHA384 # only for TLS 1.3
ecdh-curve secp384r1
#[only server]#dh none # (EC)DHE, thus no permanent parameters
# TLS certificates
# Note that the certificates must be:
# - SHA-256 signed
# - using RSA 2048 or higher (choose at least 4096), and not Elliptic Curve
# - including "X509v3 Extended Key Usage" (EKU) for Server vs. Client
remote-cert-tls server # [server: remote-cert-tls client] (EKU)
ca DOMAIN/ca.crt # CA to validate the peer certificate against
cert DOMAIN/client-or-server.crt
key DOMAIN/client-or-server.key
#[only server]#crl-verify DOMAIN/crl.pem # check for revoked certs
# Data channel
cipher AES-256-GCM # or AES-256-CBC
ncp-disable # and no cipher negotiation
# Drop privileges; keep tunnel across restarts; keepalives
user openvpn
group nogroup
persist-key
persist-tun
keepalive 15 55 # ping every 15, disconnect after 55
#[only server]#opt-verify # force compatible options
The lack of SystemD notify
support is a minor annoyance. When
editing the SystemD service file, set Type
to simple
and remove
--daemon
from the options. Otherwise you may end up with unmounted
PrivateTmp
mounts and multiple openvpn-nl
daemons (which of course
hold on to the listening socket your new daemon needs, causing strange
client-connect
errors):
# /etc/systemd/system/openvpn@server.service.d/override.conf
[Service]
ExecStart=
# Take the original ExecStart, replace "openvpn" with "openvpn-nl"
# and remove "--daemon ...":
ExecStart=/usr/sbin/openvpn-nl --status /run/openvpn/%i.status 10
--cd /etc/openvpn --script-security 2 --config /etc/openvpn/%i.conf
--writepid /run/openvpn/%i.pid
Type=simple
If you're okay with sticking to SHA256 and RSA for now, then OpenVPN-NL is compatible with vanilla OpenVPN. Do note that hardware acceleration in Mbed TLS is explicitly marked as disabled on the OpenVPN-NL lifecycle page. I'm not sure if this is a security decision, but it may prove to be less performant.
In conclusion: there is no immediate need to use OpenVPN-NL, but it is wise to take their changes to heart. Make sure:
- you validate and trust packages from your software repository;
- all your certificates are SHA256-signed;
remote-cert-tls
is enabled (and your certificates are marked with the correct key usage, e.g. by using a recent easy-rsa to sign your keys);- ciphers are fixed or non-negotiable using
ncp-disable
; auth
,cipher
andtls-cipher
are set to something modern.
But if you stick to the above configuration, then using OpenVPN-NL is fine too..
.. although still I cannot put my finger on how discarding bytes from
/dev/random
would make things more secure.
Notes about RNG, min-platform-entropy and hardware support
About “how discarding bytes from /dev/random
makes things more
secure."
I think the theory is that throwing away some bytes makes things more
secure, because the initially seeded bytes after reboot might be
guessable. And instead of working against the added code — by lowering
min-platform-entropy
— we can instead attempt to get more/better
entropy.
If the rdrand
processor flag is available then this might be a piece
of cake:
$ grep -q '^flags.*\brdrand\b' /proc/cpuinfo && echo has rdrand
has rdrand
If it isn't, and this is a virtual machine, you'll need to (a)
confirm that it's available on the VM host and (b) enable the host
processor in the VM guest (-cpu host
). (If you wanted AES CPU
acceleration, you would have enabled host CPU support already.)
When the processor flag is available, you can start benefitting from host-provided entropy.
$ cat /proc/sys/kernel/random/entropy_avail
701
This old entropy depletes faster than a Coca-Cola bottle with a Mentos
in it, once you start reading from /dev/random
directly.
But, if you install rng-tools, you get a nice /usr/sbin/rngd
that
checks entropy levels and reads from /dev/hwrng
, replenishing the
entropy as needed.
17:32:00.784031 poll([{fd=4, events=POLLOUT}], 1, -1) = 1 ([{fd=4, revents=POLLOUT}])
17:32:00.784162 ioctl(4, RNDADDENTROPY, {entropy_count=512, buf_size=64, buf="\262\310"...}) = 0
$ cat /proc/sys/kernel/random/entropy_avail
3138
Instant replenish! Now you can consider enabling
use-prediction-resistance
if you're using MbedTLS (through
OpenVPN-NL).
Footnotes
See also blog.g3rt.nl openvpn security tips and how to harden OpenVPN in 2020.