Some general stuff to secure openVPN

TLS-Auth

Use a pre-shared key on both, server and client to secure the connection
Create the key

openvpn --genkey --secret /etc/openvpn/ta.key

Activate TLS key on server, edit server.conf

tls-auth ta.key 0
tls-server

Deploy the ta.key to all clients, edit client.conf

tls-auth ta.key 1
tls-client

Diffie Hellman Parameters

Create the dhparam key for the server.

openssl dhparam -out /etc/openvpn/dh2048.pem 2048

Add it to the server.conf

dh dh2048.pem

Do the same for all clients.
Be sure to create own dhparams keys for each client.
Add the same line to the client.conf files as above shown for server.conf

Client Config Directory

You can define a client-config-dir where you can put client specific configurations.
This can be used to assign static IP addresses to clients or such.

Add to your server.conf

client-config-dir ccd
route 10.10.0.252 255.255.255.252

Add the following to the to a file in ccd directory, name that file the same as the CommonName (CN) of the connecting client.
Assuming the certificate of the connecting client has a CN of ovpn_Thor.Himself create the file /etc/openvpn/ccd/ovpn_Thor.Himself and add the following line

ifconfig-push 10.10.0.253 10.10.0.254

This will assign ip 10.10.0.254 to the server interface and 10.10.0.253 to the clients tunnel interface.

So, if a clients connects our openVPN server and has a certificate that has been signed by our CA and a CN of ‘ovpn_Thor.Himself’ the client will get IP 10.10.253 on every connect.

Every traffic to network 10.10.0.252/30 will be routed into the tunnel interface.

Persistent Pool IP

You can also assign static IP addresses out of the dynamic pool
Add the following to your server.conf

ifconfig-pool-persist ipp.txt

And create the file /etc/openvpn/ipp.txt and list the clients and their IP one per line
Omit the ‘,<IPv6>’ in each line if you have an IPv4 only setup.

# Syntax
# <CN>,<IPv4>,<IPv6>
ovpn_Thor.Himself,192.168.192.2,2a03:a:609:f999::1002
ovpn_Sif.Herself,192.168.192.3,2a03:a:609:f999::1003
ovpn_Other.User,192.168.192.4,2a03:a:609:f999::1004

TLS Verify

This is mainly to temporary block users from connecting to our server.
Assuming that someone is a longer time out of office, let’s say on maternity leave, and you don’t want to revoke her certificate from the CA completely you can use something like ‘tls-verify’

You need to write a script to do the actual validation.
I’m using the following script putting it into file /etc/openvpn/openvpn-verify-cn
Be sure to make it executable!

#!/usr/bin/perl

# verify-cn -- a sample OpenVPN tls-verify script
#
# Return 0 if cn matches the common name component of
# X509_NAME_oneline, 1 otherwise.
#
# For example in OpenVPN, you could use the directive:
#
#   tls-verify "./verify-cn Test-Client"
#
# This would cause the connection to be dropped unless
# the client common name is "Test-Client"

die "usage: verify-cn cn certificate_depth X509_NAME_oneline" if (@ARGV != 3);

# Parse out arguments:
#   cn    -- The common name which the client is required to have,
#            taken from the argument to the tls-verify directive
#            in the OpenVPN config file.
#   depth -- The current certificate chain depth.  In a typical
#            bi-level chain, the root certificate will be at level
#            1 and the client certificate will be at level 0.
#            This script will be called separately for each level.
#   x509  -- the X509 subject string as extracted by OpenVPN from
#            the client's provided certificate.
($cnfile, $depth, $x509) = @ARGV;

my @CNs;
open(IN, "<$cnfile") or die("Unable to open CN file: $!\n");
while (<IN>) {
        chomp;
        next if ($_ =~ /^#/);
        next if ($_ eq '');
        push(@CNs, $_);
}
close(IN);

if ($depth == 0) {
    # If depth is zero, we know that this is the final
    # certificate in the chain (i.e. the client certificate),
    # and the one we are interested in examining.
    # If so, parse out the common name substring in
    # the X509 subject string.

    if ($x509 =~ /\/CN=([^\/]+)/) {
        # Accept the connection if the X509 common name
        # string matches the passed cn argument.
        my $x509_cn = $1;

        foreach my $validCn (@CNs) {
                if ($x509_cn =~ m/^${validCn}$/s) {
                    exit 0;
                }
        }
    }

    # Authentication failed -- Either we could not parse
    # the X509 subject string, or the common name in the
    # subject string didn't match the passed cn argument.
    exit 1;
}

# If depth is nonzero, tell OpenVPN to continue processing
# the certificate chain.
exit 0;

Create the file /etc/openvpn/valid-CNs and add the CommonNames of the certificates you want to enable login for

ovpn_Thor.Himself
ovpn_Sif.Herself
ovpn_Other.User

Now add the next line to your server.conf

tls-verify "/etc/openvpn/openvpn-verify-cn /etc/openvpn/valid-CNs"

At that point in time (after restart of openVPN server for sure!) our server will only accept logins by certificate that are signed by our CA and that are NOT revoked and the common name of the certificate MUST be listed in file /etc/openvpn/valid-CNs

This way you can block access temporarily by simply removing the CN of the user certificate from the valid-CNs file instead of revoking the certificate and deploy a new certificate once the user gets back to work.

Example Server Config

port 1194
proto udp
dev tun0
tls-server
tls-version-min 1.2
tls-auth ta.key 0     # use '1' for client side
ca /etc/openvpn/pki/ca.crt
cert server.crt
key server.key  # This file should be kept secret
dh dh2048.pem
topology subnet
server 192.168.192.0 255.255.255.0
ifconfig-pool-persist ipp.txt
client-config-dir ccd/
push "route 192.168.192.0 255.255.255.0"
push "dhcp-option DNS 192.168.192.1"
keepalive 10 120
auth SHA256
cipher AES-256-CBC
tls-verify "./verify-cn ./allowedClientCNs"
script-security 2
max-clients 10
user nobody
group nobody
persist-key
persist-tun
status openvpn-status.log
verb 3
explicit-exit-notify 1
client-to-client

### enable tun6
server-ipv6 2a03:a:609:f999::/64
proto udp6
tun-ipv6
push tun-ipv6
push "route-ipv6 2a03:a:609:f999::/64"
push "dhcp-option DNS 2a03:a:609:f999::1"
push "dhcp-option DNS 2a03:a:609:f966:53::"

Example Client Config

client
remote vpn.thor.de 1194
proto udp
dev tun
persist-key
persist-tun
resolv-retry infinite
nobind
cipher AES-256-CBC
auth SHA256
key-direction 1
tls-client
tls-version-min 1.2
verb 3


### Deploy client keys by config file ###

<ca>
-----BEGIN CERTIFICATE-----
MIIDNTCCAh2gAwIBAgIJAJsAewXy19vjMA0GCSqGSIb3DQEBCwUAMBYxFDASBgNV
<snipp/>
l0GH1zOpZx0K
-----END CERTIFICATE-----
</ca>
<cert>
-----BEGIN CERTIFICATE-----
MIIDgjCCAmqgAwIBAgIQHSc7Pn37zew1+GFA9PFuTDANBgkqhkiG9w0BAQsFADAW
<snipp/>
OcIk44Umb3BfW+0B7AcSZHMKcMYFzPkczXWVh0FtEsWkaPEiCyc=
-----END CERTIFICATE-----
</cert>
<key>
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCt/M1FD91s9e5T
<snipp/>
mENovB7jphbFGz2pRLL7IwU=
-----END PRIVATE KEY-----
</key>
<tls-auth>
-----BEGIN OpenVPN Static key V1-----
8b95fd6352617c07676fc997a6cbc649
<snipp/>
ee653470371e2d088e44772ffb5a9252
-----END OpenVPN Static key V1-----
</tls-auth>

Leave a Reply