OpenVPN has several layers in which the credentials of a connecting client are verified. It is even possible to add a custom layer to the verification process by specifying a tls-verify
script. In this recipe, we will demonstrate how such a script can be used to allow access only for a particular certificate.
Install OpenVPN 2.3 or higher on two computers. Make sure that the computers are connected over a network. Set up the client and server certificates using the Setting up the public and private keys recipe from Chapter 2, Client-server IP-only Networks. For this recipe, the server computer was running Fedora 22 Linux and OpenVPN 2.3.10. The client was running Windows 7 64 bit and OpenVPN 2.3.10. For the client, keep the client configuration file, basic-udp-client.ovpn
, from the Using an ifconfig-pool block recipe, from Chapter 2, Client-server IP-only Networks .
proto udp port 1194 dev tun server 10.200.0.0 255.255.255.0 ca /etc/openvpn/cookbook/ca.crt cert /etc/openvpn/cookbook/server.crt key /etc/openvpn/cookbook/server.key dh /etc/openvpn/cookbook/dh2048.pem tls-auth /etc/openvpn/cookbook/ta.key 0 persist-key persist-tun keepalive 10 60 topology subnet user nobody group nobody # nogroup on some distros daemon log-append /var/log/openvpn.log script-security 2 tls-verify /etc/openvpn/cookbook/example5-4-tls-verify.sh
example5-4-server.conf
.tls-verify
script:#!/bin/bash [ $# -lt 2 ] && exit 1 # if the depth is non-zero , continue processing [ "$1" -ne 0 ] && exit 0 allowed_cns=`sed 's/ /_/g' $0.allowed` for i in $allowed_cns do [ "$2" = "$i" ] && exit 0 done # catch-all exit 1
example5-4-tls-verify.sh
.[root@server]# chmod 755 example5-4-tls-verify.sh
[root@server]# echo "/C=US/O=Cookbook 2.4/CN=client1" > /etc/openvpn/cookbook/example5-4-tls-verify.sh.allowed
Note that this is a one-line command.
[root@server]# openvpn --config example5-4-server.conf
The client should be able to connect normally.
/etc/openvpn/cookbook/example5-4-tls-verify.sh.allowed
file and reconnect. This time the server log will show the following:CN not found in /etc/openvpn/cookbook/example5-4-tls- verify.sh.allowed, denying access ... openvpnclient1:9007 TLS_ERROR: BIO read tls_read_plaintext error: error:140890B2:SSL routines:SSL3_GET_CLIENT_CERTIFICATE:no certificate returned ... openvpnclient1:9007 TLS Error: TLS object -> incoming plaintext read error ... openvpnclient1:9007 TLS Error: TLS handshake failed
This means that the client is denied access by the OpenVPN server.
When a client connects to the OpenVPN server, the tls-verify
script is executed several times to verify the entire certificate chain of the connecting client. In this recipe, we look for the end-user certificate, which is the equivalent of the client1.crt
file. When this end-user certificate is found in the example5-4-tls-verify.sh.allowed
file, the script returns 0
, indicating a successful verification. If it is not found, a message is printed to the OpenVPN log and the script returns 1
. The OpenVPN server then denies the access to this particular client.
In this recipe, we focus only on the end-user certificate using a simple lookup table. Of course, this could also have been achieved in many other ways (for example, by using a client-config-dir
file). With a tls-verify
script, it is also possible to disallow all the certificates from a particular certificate authority (CA).
In more complex setups, where client certificates can be signed by many different CAs, it is sometimes very useful to temporarily refuse access to all the certificates from a particular CA. For example, to deny access to all certificates that are signed with the CA certificate with the subject name "Cookbook 2.4 CA" from Chapter 2, Client-server IP-only Networks, the following script could be used:
#!/bin/bash [ $# -lt 2 ] && exit 1 CA=`echo $2 | sed -n 's/.*/CN=(.*)/.*/1/p'` [ "$CA" = "Cookbook 2.4 CA" ] && exit 1
18.119.19.23