Warning: in_array() expects parameter 2 to be array, string given in /virtual/theblogs.bestsolution.at/httpd/htdocs/wp-content/plugins/google-one/google-plus-one.php on line 344

Warning: in_array() expects parameter 2 to be array, string given in /virtual/theblogs.bestsolution.at/httpd/htdocs/wp-content/plugins/google-one/google-plus-one.php on line 346

OpenVPN: extended verification of X.509 client certificates

Warning: in_array() expects parameter 2 to be array, string given in /virtual/theblogs.bestsolution.at/httpd/htdocs/wp-content/plugins/google-one/google-plus-one.php on line 344

Warning: in_array() expects parameter 2 to be array, string given in /virtual/theblogs.bestsolution.at/httpd/htdocs/wp-content/plugins/google-one/google-plus-one.php on line 346

X.509 certificates have become a key part for secure authentication in corporate environments and unsurprisingly, OpenVPN[1] allows to use X.509 certificates for identifying authorized clients.

Now, the default mode for OpenVPN is to verify the presented client certificates against a certificate authority (CA) and if that test succeeds, the clients are allowed in. That however is more than basic, because if your PKI is used for more than only OpenVPN authorization, you will have to find a way to “discriminate” certificates, otherwise any certificates issued by this CA would be eligible to connect to your VPN.

One of the ways to deal with this, is to have a dedicated “OpenVPN sub CA”, chained to your main CA. All certificates must then be issued by this dedicated OpenVPN CA and if you do this, you are good to go.

Another way is to configure OpenVPN to separate between authorized and non-authorized X.509 client certificates and this is the way, I am going to describe below.

Unfortunately how to do this in OpenVPN is not really prominently documented, so I decided to write this little blog post.


The essential part is to use the tls-verify directive on the server side. tls-verify allows to to specify an external hook that is invoked whenever a certificate is presented to OpenVPN.

If this hook has an exit code of 0, the certificate is regarded as good, otherwise it is rejected. See the OpenVPN man page for further information.

A sample, yet perfectly working hook script named verify-cn can be found in the sample-scripts directory of your OpenVPN distribution (typically somewhere in /usr/share/doc/openvpn/).

Using the script is simple:

  1. copy it to a reasonable place:
    cp /usr/share/doc/openvpn/examples/sample-scripts/verify-cn /usr/bin/
  2. create a list with authorized CN entries
    $ cat /etc/openvpn/authorized_users
  3. add this to your OpenVPN configuration
    tls-verify /usr/bin/verify-cn /etc/openvpn/authorized_users

The entries in the /etc/openvpn/authorized_users have to be the CN fields of the authorized client certificates.

Even though the hook script works just fine, it is not suitable for larger deployments, because of the huge overhead it comes with: A new process has to be spawned for each incoming connection and each hook process has to parse the list of authorized_users again and again (and that even in an inefficient way). So this leaves much place for improvement, but this is beyond the scope of this article.


Now, at least for us, authorization based on the CN is still not really practicable for many reasons. Much more interesting would be to use the emailAddress field of the certificate.

This can easily be achieved by modifying the sample verify-cn to investigate the emailAddress field instead like below (lacking most comments):

# modified version of verify-cn from https://github.com/OpenVPN/openvpn/blob/master/sample/sample-scripts/verify-cn
# all credits to go the OpenVPN authors
die "usage: verify-emailAddress cnfile certificate_depth subject" if (@ARGV != 3);

($authorizedFile, $depth, $x509) = @ARGV;

if ($depth == 0) {
  # BEWARE: OpenVPN UPPERCASES the passed in certificate fields!!
  if ($x509 =~ / EMAILADDRESS=([^,]+)/) {
    $xField = $1;

    open(FH, '<', $authorizedFile) or exit 1; # can't open, nobody authenticates!
    while (defined($line = )) {
      if ($line !~ /^[[:space:]]*(#|$)/o) {
        if ($line eq $xField) {
          exit 0;
  exit 1;
exit 0;

If you want to use this hook script, you need to do the following:
store it as /usr/bin/verify-emailAddress and change your OpenVPN configuration like this:

  1. store the hook script it a reasonable place
    eg. /usr/bin/verify-emailAddress
  2. create a list with authorized emailAddress entries
    $ cat /etc/openvpn/authorized_users
  3. add this to your OpenVPN configuration
    tls-verify /usr/bin/verify-emailAddress /etc/openvpn/authorized_users


Beware however, that this may only be half of the story. If (and only if) you expect OpenVPN to do further authorization checks (like additionally asking for a user specific password), then you also have to tell OpenVPN to use the wanted X.509 field as the username.

The field extracted as the username can be controlled by the x509-username-field directive. So if you want OpenVPN to switch from CN to emailAddress, you need to instruct it explicitly:

  • tell OpenVPN to use emailAddress instead of CN
    x509-username-field emailAddress

[1] http://openvpn.net/

Spread the love

Leave a Reply

3 Comment threads
1 Thread replies
Most reacted comment
Hottest comment thread
2 Comment authors
NAS Synology : certificats StartSSL et OpenVPN V2 | FunnyPlaceudojgradejoao Recent comment authors
newest oldest most voted
Notify of

hi, great post by the way.
i’m having a problem when i add the tbs-verify line to the server.conf and reload the openvpn service it does not start.
“Options error: the –tls-verify directive should have at most 1 parameter. To pass a list of arguments as one of the parameters, try enclosing them in double quotes (“”).”


i tested your email script but it’s not working.
can you check it?

Post Navigation