Rapid OpenVPN Certificate & Configuration Deployment

Overview

On a number of recent occasions, I have needed to quickly configure and deploy OpenVPN. These situations have included simulated attacks, in which the opportunity to deploy OpenVPN as an egress or C2 channel presented itself, or during reconnaissance activities in which it is advantageous to disguise your real IP address for attribution purposes. In these cases, it is desirable to be able to rapidly deploy OpenVPN without compromising on security, and where the situation may not warrant or benefit from the overheads of constructing a full PKI. OpenVPN itself is a very simple tool to configure; the more convoluted part is the generation of digital certificates which is made relatively straightforward through the easy-rsa set of scripts. However, I was in need of a quick way to configure OpenVPN as below:

  • Mutual TLS authentication using separate CAs.
  • Generation of DH parameters.
  • Usage of OpenVPN's HMAC authentication.
  • Inclusion of the required keyUsage/extendedKeyUsage/nsCertType attributes and values.
  • Non-attributable CNs.
  • Ideally in a one-click style tool which doesn't need the OpenSSL file structure.

I therefore chose to repurpose my Certerator code to build a quick OpenVPN configuration and certificate generator. Where possible, it uses pyOpenSSL (python's OpenSSL bindings). When run, it performs the following actions:

  1. If server_ca.pem does not exist, generate a new certification authority for the OpenVPN server. If it does exist, use whatever is there.
  2. If client_ca.pem does not exist, generate a new certification authority for the OpenVPN client. If it does exist, use whatever is there.
  3. If server_cert.pem does not exist, generate a new certificate for the OpenVPN server and sign it with the Server CA.
  4. If client_cert.pem does not exist, generate a new certificate for the OpenVPN client and sign it with the Client CA.
  5. If ta.key does not exist, execute openvpn --genkey --secret ta.key to generate a shared secret.
  6. If dh4096.pem does not exist, execute openssl genrsa -out dh4096.pem 4096 to generate custom 4096 bit DH parameters (change DH_PARAM_SIZE if you want a differently sized key). I have included my generated 4096 bit DH parameters for you to use.
  7. Generate a configuration file for the OpenVPN server and the OpenVPN client, referencing the required certificates and configuration files.
  8. Create two tar.gz files, one to deploy on the server and one to deploy on the client, which contain the required files.

If a second signed certificate was desired, you need only change the hardcoded CN (if appropriate), remove client_cert.pem and client_key.pem and re-run the script. It will pick up the fact that those certificates are missing and generate new ones. One benefit of this is that it (mostly) does not use the OpenSSL command line interface directly, meaning that there is no need to set up the OpenSSL file structure that is usually necessary. It does use the OpenSSL binary to generate DH params and uses OpenVPN to generate the HMAC key (ta.key), but everything else is generated through python.

Example

$ ./generate.py

.mMMMMMm. MMm M WW W WW RRRRR
mMMMMMMMMMMM. MM MM W W W R R
/MMMM- -MM. MM MM W W W R R
/MMM. _ \/ ^ M M M M W W W W RRRR
|M. aRRr /W| M M M M W W W W R R
\/ .. ^^^ wWWW| M M M W W R R
/WW\. .wWWWW/ M M M W W R R
|WWWWWWWWWWW/
.WWWWWW. Server/Client Cert Generator (for OpenVPN)
stuart.morgan@mwrinfosecurity.com | @ukstufus

Generating new Server CA.....done
Written PEM CA certificate to server_ca.pem
Written PEM CA key to server_ca.key
Server Fingerprint: CB:3E:D9:CF:AF:AC:FF:D0:03:DA:D9:75:29:22:8A:7E:93:21:66:6F

Generating new Client CA.....done
Written PEM CA certificate to client_ca.pem
Written PEM CA key to client_ca.key
Client Fingerprint: 3B:8A:09:BF:E1:95:43:C3:79:77:BE:8A:8F:E2:72:90:43:5D:0C:A2

Generating new server certificate.....done
Written PEM certificate to server_cert.pem
Written private key to server_cert.key
SHA1 server Cert Fingerprint: 1F:6B:73:73:18:F5:06:95:3D:62:AD:A4:8F:38:68:58:02:97:02:60

Generating new client certificate.....done
Written PEM certificate to client_cert.pem
Written private key to client_cert.key
SHA1 client Cert Fingerprint: 9C:5B:99:4A:30:60:0B:95:1F:3E:A4:8A:96:4E:96:F2:8F:C3:D1:B4

Generating new HMAC key...

Reusing dh4096.pem


On the OpenVPN Server:
ca client_ca.pem
cert server_cert.pem
key server_cert.key

On the OpenVPN Client:
ca server_ca.pem
cert client_cert.pem
key client_cert.key

Example configs written to example.server.conf and example.client.conf

Server configuration and related files are in example.server.tar.gz
Client configuration and related files are in example.client.tar.gz

Transfer example.server.tar.gz to the server machine:

[stufus@server ~/openvpn]$ tar zxvf example.server.tar.gz
x client_ca.pem
x ta.key
x dh4096.pem
x server_cert.pem
x server_cert.key
x example.server.conf
[stufus@server ~/openvpn]$ cat example.server.conf
port 1194
proto udp
dev tun
ca client_ca.pem
cert server_cert.pem
key server_cert.key
dh dh4096.pem
server 10.255.255.0 255.255.255.0
topology net30
ifconfig-pool-persist ipp.txt
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 8.8.8.8"
push "dhcp-option DNS 8.8.4.4"
keepalive 10 120
tls-auth ta.key 0
cipher AES-128-CBC
comp-lzo
persist-key
persist-tun
#user openvpn
#group openvpn
status openvpn-status.log

Transfer example.client.tar.gz to the client machine:

[stufus@client ~/openvpn]$ tar zxvf example.client.tar.gz
x server_ca.pem
x ta.key
x client_cert.pem
x client_cert.key
x example.client.conf
[stufus@client ~/openvpn]$ cat example.client.conf
client
dev tun
proto udp
remote SERVER 1194
resolv-retry infinite
nobind
#user openvpn
#group openvpn
persist-key
persist-tun
ca server_ca.pem
cert client_cert.pem
key client_cert.key
remote-cert-tls server
tls-auth ta.key 1
cipher AES-128-CBC
comp-lzo
#For HTTP proxy uncomment the below
#http-proxy-retry
#http-proxy HTTPPROXYSERVER HTTPPROXYPORT
#http-proxy-option AGENT Mozilla/5.0+(Windows;+U;+Windows+NT+5.0;+en-GB;+rv:1.7.6)+Gecko/20050226+Firefox/1.0.1
#or create a 2 line text file, username on first line, pass on second, call it userpass.txt
#http-proxy HTTPPROXYSERVER HTTPPROXYPORT userpass.txt_file basic
#http-proxy HTTPPROXYSERVER HTTPPROXYPORT userpass.txt_file ntlm
#socks-proxy SERVER PORT

Now start the server, update the client config file with the server IP address and connect to the server. Either edit the file using vi/pico/nano/ee etc or update it inline:

[stufus@client ~/openvpn]$ sed -I "" 's/SERVER/192.168.0.100/' example.client.conf
[stufus@client ~/openvpn]$ sudo openvpn example.client.conf
Tue Apr 19 14:24:09 2016 OpenVPN 2.3.10 amd64-portbld-freebsd10.2 [SSL (OpenSSL)] [LZO] [MH] [IPv6] built on Feb 2 2016
Tue Apr 19 14:24:09 2016 library versions: OpenSSL 1.0.2g 1 Mar 2016, LZO 2.09
Tue Apr 19 14:24:09 2016 Control Channel Authentication: using 'ta.key' as a OpenVPN static key file
Tue Apr 19 14:24:09 2016 UDPv4 link local: [undef]
Tue Apr 19 14:24:09 2016 UDPv4 link remote: [AF_INET]192.168.0.100:1194
Tue Apr 19 14:24:09 2016 [Server Cert] Peer Connection Initiated with [AF_INET]192.168.0.100:1194
Tue Apr 19 14:24:11 2016 TUN/TAP device /dev/tun1 opened
Tue Apr 19 14:24:11 2016 do_ifconfig, tt->ipv6=0, tt->did_ifconfig_ipv6_setup=0
Tue Apr 19 14:24:11 2016 /sbin/ifconfig tun1 10.255.255.6 10.255.255.5 mtu 1500 netmask 255.255.255.255 up
...snip...

If a second signed client certificate is required, the first need only be removed and a new one generated:

[stufus@client ~/openvpn]$ rm client_cert.pem && rm client_cert.key
[stufus@client ~/openvpn]$ ./generate.py
...snip...
Reusing server_ca.pem as the Server
Server Fingerprint: CB:3E:D9:CF:AF:AC:FF:D0:03:DA:D9:75:29:22:8A:7E:93:21:66:6F

Reusing client_ca.pem as the Client
Client Fingerprint: 3B:8A:09:BF:E1:95:43:C3:79:77:BE:8A:8F:E2:72:90:43:5D:0C:A2

Reusing server_cert.pem as the server certificate
SHA1 server Cert Fingerprint: 1F:6B:73:73:18:F5:06:95:3D:62:AD:A4:8F:38:68:58:02:97:02:60

Generating new client certificate.....done
Written PEM certificate to client_cert.pem
Written private key to client_cert.key
SHA1 client Cert Fingerprint: DB:70:46:52:AE:9B:99:1C:37:55:58:3F:F6:B6:3A:81:5F:13:A4:5D

Reusing ta.key

Reusing dh4096.pem
...snip...

However the two client certificates would have the same serial number and same CN field; for most practical purposes, it would be sensible to change the serial number and CN manually, which requires modification of the script.

Limitations

  • This tool is not a replacement for a fully maintainable public key infrastructure. I built it simply for speed and convenience and released it in the hope that others find it helpful.
  • It makes several assumptions about the OpenVPN configuration specifics. For example, it assumes that UDP is the desired protocol and does not automatically include options such as port-share (which may be helpful if TCP mode is used instead).
  • It is not intelligent; for example, if you removed client_ca.* but left client_cert.* present and re-ran the script, it would generate a new client CA but would not generate a new client certificate and sign it.

Like so many tools, this started as a quick-and-dirty tool to automate an otherwise tedious task.

Further Work

I am not planning to add a configuration file, revocation support (e.g. CRL) or any of the other niceties to this because tools such as OpenSSL itself, easy-rsa, tinyCA etc have been specifically designed for this purpose. This project was simply to meet a need, namely to be able to go from nothing to a secure, functioning OpenVPN installation with a minimum of configuration or effort. Although this is built for OpenVPN, the certificates are valid, normal digital certificates and could be used for any mutual TLS authentication scenario. For example, it could be used to generate the required certificates for Apache, nginx etc to facilitate client certificate authentication. The fact that this project is open source will not make any meaningful difference to the cryptographic security because the keys are all generated locally.

Location

The tool can be found at https://github.com/stufus/openvpn-rapid-config. Alternatively the repository can be cloned as follows:

git clone https://github.com/stufus/openvpn-rapid-config.git