Skip to main content

OpenVPN in a VM running in an OpenContrail subnet

This technical deep-dive blog from Yves Gwenael discusses in detail how you can set a VPN to access your VMs using OpenVPN running in an OpenContrail subnet. Today, OpenStack provides VPNaaS, however OpenContrail has not yet implemented this feature. As an interim solution, Yves lays out how OpenVPN can be used in conjunction with OpneContrail to create VPnaaS.

Please visit the following link to read further:  http://dev.cloudwatt.com/en/blog/openvpn-in-a-vm-running-in-an-opencontrail-subnet.html

OpenStack provides VPNaaS, however it is not yet implemented with OpenContrail, so you will not be able to use it yet on an OpenContrail infrastructure.

OpenContrail does not allow (for security reasons) traffic from IPs and MAC address which aren’t configured ports in the network, so a VPN in “bridging” mode will not work until the Allowed address pairs plugin is released (it has however just been merged on the “master” trunk recently), and neither would a VPN in routing mode (seeOpenVPN bridging vs. routing) work until the Extra routes plugin supports OpenContrail.

So how can we set a VPN to access our VMs without waiting for the mentioned features and plugins to be available (sure they will be soon however) for OpenContrail? There is a solution by using OpenVPN in routing mode and by masquerading the VPN clients. This is what we intend to do in this tutorial.

Before we start, many people ask why we configure OpenVPN on UDP instead of TCP? If you ask yourself the same question, Well you must know that the VPN’s UDP packets when “encapsulating” a TCP packet to transfer, the TCP protocol will be properly handled “after” de-encapsulation on the other side! There is no reason for encapsulating a UDP packet in a TCP packet (which would be the case with a TCP tunnel), and even less reasons for encapsulating a TCP packet in another TCP packet. Please read Why TCP Over TCP Is A Bad Idea for detailed drawbacks of using TCP when tunneling.

In this Tutorial we will describe a fully manual procedure for “those who want to know” :-), full server configuration and client configuration example files, and aDeployment via Heat for the ones who are in a hurry 🙂

Requirements:

We will not explain how to spawn a VM, because if you read this, we consider that you have already gone through our Getting started documentation, and you already have an OpenStack Virtual network with some VMs running. Here We will explain how to set up an OpenVPN so that you can access all the VMs which are inside your OpenStack private network.

So first, simply spawn a VM running in your private network, and give it a floating IP. This documentation is how to configure OpenVPN on either a CentOS/RHEL or Ubuntu VM.

Allow SSH and OpenVPN Trafic towards your OpenVPN VM

List your security groups:

neutron security-group-list

Note the ID of the security group you assigned to the VM you will use as an OpenVPN server, and allow the following rules:

neutron security-group-rule-create --direction ingress --remote-ip-prefix 0.0.0.0/0 --protocol udp --port-range-min 1194 --port-range-max 1194 # Allow OpenVPN
 neutron security-group-rule-create --direction ingress --remote-ip-prefix 0.0.0.0/0 --protocol tcp --port-range-min 22 --port-range-max 22 # Allow SSH

Install openvpn and easy-rsa

Log onto your VM, and su to root:

sudo su -

And install OpenVPN:

On Centos/RHEL:

yum install openvpn easy-rsa

On Ubuntu:

apt-get install openvpn
 # on Ubuntu > 12.04 easy-rsa has been stripped out from openvpn
 apt-get install easy-rsa # only on Ubuntu > 12.04

Server configuration

Once openvpn is installed, go in the openvpn directory:

cd /etc/openvpn

Copy the easy-rsa scripts

On Centos/RHEL:

 cp -r /usr/share/easy-rsa/2.0 /etc/openvpn/easy-rsa
 cd /etc/openvpn/easy-rsa
 ln -s openssl-1.0.0.cnf openssl.cnf

On Ubuntu > 12.04:

 cp -r /usr/share/easy-rsa /etc/openvpn/
 cd /etc/openvpn/easy-rsa
 ln -s openssl-1.0.0.cnf openssl.cnf

On Ubuntu 12.04:

 cp -r /usr/share/doc/openvpn/examples/easy-rsa/2.0 /etc/openvpn/easy-rsa
 cd /etc/openvpn/easy-rsa
 ln -s openssl-1.0.0.cnf openssl.cnf

Generate the certificates:

PKI configuration

Edit the vars file in your /etc/openvpn/easy-rsa directory and adapt all the export KEY_* variables to your liking (especially: KEY_SIZE,KEY_COUNTRYKEY_PROVINCEKEY_CITYKEY_ORGKEY_EMAILKEY_OU), and then source this file:

source ./vars

Note: If you logged out/in, you will have to run the above command again before running any script in the /etc/openvpn/easy-rsa directory.

Initialize the PKI

./clean-all

(Never run this command again unless you want to destroy all certificates in the keys /etc/openvpn/easy-rsa/keys directory an recreate new ones.)

Generate the Diffie Hellman

./build-dh
Note: this can take a long time. The bigger the KEY_SIZE (set in the vars file) the longer it takes. If KEY_SIZE is set to 2048 bits, it takes several minutes, but depending on the size (flavor) of your VM, if it has only one CPU and 1 Gb of RAM, a 4096 bits Diffie Hellman generation time can take more than an hour.

Create your own CA:

We create the ca.crt and ca.key files in the keys subdirectory:

./build-ca

Create your server’s certificate:

./build-key-server My_Server_Name

Hit the “enter” key when prompted for a password because the server needs a password-less certificate.

Hit the “y” key when prompted to Sign the certificate, and when prompted to commit.

Now in the keys subdirectory we have generated the My_Server_Name.crtMy_Server_Name.csr and My_Server_Name.key files.

Create a secret shared key

To Harden OpenVPN Security, in order to avoid DoS attacks or port flooding on the OpenVPN UDP port (and other nasty things) we create a secret shared key:

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

Create a certificate revocation list:

In order to create a certificate revocation list, we create a certificate which we immediately revoke:

 ./build-key revoked
 ./revoke-full revoked

When revoking a certificate, the revoke-full script checks if the certificate is still valid, so the following error message simply means the revocation worked successfully:

error 23 at 0 depth lookup:certificate revoked

Now in the keys subdirectory we have generated the revoked certificate keys and a crl.pem revocation list.

Edit the server configuration file

Copy the server sample configuration file

On Centos/RHEL:
 cp /usr/share/doc/openvpn-2.3.2/sample/sample-config-files/server.conf /etc/openvpn/
 cd /etc/openvpn
On Ubuntu:
 cp /usr/share/doc/openvpn/examples/sample-config-files/server.conf.gz /etc/openvpn/
 cd /etc/openvpn
 gunzip server.conf.gz

Then edit the server.conf file with your favorite editor to perform the changes explained in the next section. NOTE: lines starting with # are comments, and lines starting with ; are not activated (just suggestions). Removing the ; activates the setting.

Point to the server certificates

Replace:

 ca ca.crt
 cert server.crt
 key server.key # This file should be kept secret

by:

 ca /etc/openvpn/easy-rsa/keys/My_Server_Name.crt
 cert /etc/openvpn/easy-rsa/keys/My_Server_Name.crt
 key /etc/openvpn/easy-rsa/keys/My_Server_Name.key # This file should be kept secret

Use the Diffie Hellman we generated

Replace:

dh dh1024.pem

by:

dh /etc/openvpn/easy-rsa/keys/dh2048.pem

(The above example is if you have set KEY_SIZE=2048 in your vars file, you may have in /etc/openvpn/easy-rsa/keys/ e.g.: dh1024.pem or dh2048.pemor dh4096.pem etc… depending on the KEY_SIZE)

Use the secret shared key

Uncomment the following line by removing the leading ; and point to the proper key by replacing:

;tls-auth ta.key 0 # This file is secret

by:

tls-auth /etc/openvpn/easy-rsa/keys/ta.key 0

Check against the revocation list to refuse revoked clients

Add the following lines:

# CRL (certificate revocation list) verification
 crl-verify /etc/openvpn/easy-rsa/keys/crl.pem

Configure the server’s logs

Uncomment:

log openvpn.log

You can prepend openvpn.log by a full path to where you want to place the logs. (by default it will be in the same directory as the configuration file). If you do so, you can also do the same to the status openvpn-status.log and ifconfig-pool-persist ipp.txt lines.

Configure the VPN and the routes to the networks to which you want the clients to access

By default, the server sample config file we took creates a VPN on the 10.8.0.0/24 CIDR. This is the IP range used for tunneling and has to be different from both the network you are in and the one you wish to reach. You can leave as-is if your openstack private-network and you own local network is NOT in this CIDR, otherwise change it by modifying the line:

server 10.8.0.0 255.255.255.0
Note: just like for your OpenStack private networks, you need to set an RFC1918 CIDR (Example)

Then, if e.g. your OpenStack private network (the one in which the VM runs and where you want to access) is 172.16.0.0/24, add:

push "route 172.16.0.0 255.255.255.0"

You can push routes to as many networks you have in your openstack infrastructure, as long as you have routers in between them.

If you want to know how to convert 172.16.0.0/24 CIDR notation to 172.16.0.0 255.255.255.0 (ASDRESS and NETMASK) notation, here are nice tools:

They are installable (with apt-get install or yum install depending on your distro).

Configure the masquerading

The masquerading will be activated by external scripts we will have to create.

First, we configure in server.conf the scripts we wish to launch by adding:

 script-security 2 # Required so that openvpn accepts launching external scripts
 route-up /etc/openvpn/route-up.sh # Script launched once the VPN is up and routes established.
 down /etc/openvpn/down.sh # Script launched when openvpn terminates.

Now you can save the changes in server.conf and we have to create the route-up.sh and down.sh scripts

Create the script which activates masquerading when launching the server

 cat > /etc/openvpn/route-up.sh < /etc/openvpn/net.ipv4.conf.all.forwarding.bak
 /sbin/sysctl net.ipv4.conf.all.forwarding=1
 /sbin/iptables-save > /etc/openvpn/iptables.save
 /sbin/iptables -t nat -F
 /sbin/iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -j MASQUERADE
 EOF
 chmod +x /etc/openvpn/route-up.sh

Create the scripts which deactivate masquerading when stopping the server

cat > /etc/openvpn/down.sh <<EOF
 #!/bin/bash
 FORWARDING=$(cat /etc/openvpn/net.ipv4.conf.all.forwarding.bak)
 echo "restoring net.ipv4.conf.all.forwarding=$FORWARDING"
 /sbin/sysctl net.ipv4.conf.all.forwarding=$FORWARDING
 /etc/openvpn/fw.stop
 echo "Restoring iptables"
 /sbin/iptables-restore < /etc/openvpn/iptables.save EOF cat > /etc/openvpn/fw.stop <<EOF
 #!/bin/sh
 echo "Stopping firewall and allowing everyone..."
 /sbin/iptables -F
 /sbin/iptables -X
 /sbin/iptables -t nat -F
 /sbin/iptables -t nat -X
 /sbin/iptables -t mangle -F
 /sbin/iptables -t mangle -X
 /sbin/iptables -P INPUT ACCEPT
 /sbin/iptables -P FORWARD ACCEPT
 /sbin/iptables -P OUTPUT ACCEPT
 EOF
 chmod +x /etc/openvpn/down.sh /etc/openvpn/fw.stop

Launch the server

Simply run the following command:

service openvpn start

Now since you probably guessed it, there is no need to mention you can simply replace ‘start’ by ‘stop’ in the command above to stop the server 🙂

Full server configuration file example

If instead of editing a sample configuration file you prefer creating one from scratch, here is a full server.conf file example (comments removed):

 port 1194
 proto udp
 dev tun
 ca /etc/openvpn/easy-rsa/keys/ca.crt
 cert /etc/openvpn/easy-rsa/keys/My_Server_Name.crt
 key /etc/openvpn/easy-rsa/keys/My_Server_Name.key
 crl-verify /etc/openvpn/easy-rsa/keys/crl.pem
 dh /etc/openvpn/easy-rsa/keys/dh2048.pem
 server 10.8.0.0 255.255.255.0 # Has to be different from your source and target networks.
 ifconfig-pool-persist ipp.txt
 push "route 172.16.0.0 255.255.255.0" # Has to match your target network.
 keepalive 10 120
 tls-auth /etc/openvpn/easy-rsa/keys/ta.key 0
 comp-lzo
 persist-key
 persist-tun
 status /var/log/openvpn-status.log
 log /var/log/openvpn.log
 verb 3
 script-security 2
 route-up /etc/openvpn/route-up.sh
 down /etc/openvpn/down.sh

We did not reduce the OpenVPN’s daemon privileges in the examples above, because if we do so, the down.sh and fw.stop scripts would fail because iptablesand sysctl commands need to be launched as root.

If you want extra security by reducing the privileges, you need to create an “openvpn” user and group (pre-exists on Centos/RHEL when the package is installed, but not on Ubuntu) and then set sudo permission to the “openvpn” user to the fw.stop and down.sh scripts, and make openvpn call another script which will call thedown.sh and fw.stop scripts via a sudo call. This is why you would need an “openvpn” user: because we do not want “nobody/nogroup” to be able to launch these scripts.

So DO NOT uncomment (by removing the leading ;) the ;user nobody and ;group nobody lines of the sample configuration file.

Client configuration

Create a client’s certificate:

Still on the server and in the /etc/openvpn/easy-rsa directory, run the following command:

./build-key My_Client_Name

Hit the “enter” key when prompted for a password if you do not want the client to be prompted for a password.

Hit the “y” key when prompted to Sign the certificate, and when prompted to commit.

Now in the keys subdirectory we have generated the ` My_Client_Name.crtMy_Client_Name.csr and My_Client_Name.key` files.

Edit the client configuration file

Copy the client sample configuration file

On Centos/RHEL:
cp /usr/share/doc/openvpn-2.3.2/sample/sample-config-files/client.conf .
On Ubuntu:
cp /usr/share/doc/openvpn/examples/sample-config-files/client.conf .

Then edit the client.conf file with your favorite editor to perform the changes explained in the next section.

NOTE: just line the server configuration sample file, lines starting with # are comments, and lines starting with ; are not activated (just suggestions). Removing the; activates the setting.

Point to the server’s floating IP

modify:

remote my-server-1 1194

by replacing my-server-1 with your openvpn server’s floating IP.

Point to the client certificates

Replace:

 ca ca.crt
 cert client.crt
 key client.key

by:

 ca keys/My_Client_Name.crt
 cert keys/My_Client_Name.crt
 key keys/My_Client_Name.key

Use the secret shared key

Uncomment the following line by removing the leading ; and point to the proper key by replacing:

;tls-auth ta.key 1

by:

tls-auth keys/ta.key 1

Make sure the other end is using a server certificate to prevent spoofing from people having client certificates

Uncomment the following line by removing the leading ;

;ns-cert-type server

In order to have:

ns-cert-type server

By doing so, connections to a server using client certificates (generated with ./build-key instead off ./build-key-server) will be rejected.

Indeed, anyone having the ca.crt and a pair of keys can create a server configuration file (she/he would just have to build a Diffie Hellman file) and have a functional server. the ns-cert-type is an added security to make sure the issued keys were issued for a server, not a client.

See Important Note on possible “Man-in-the-Middle” attack if clients do not verify the certificate of the server they are connecting to for more information.

reduce the client daemon’s privileges

Since the client has not scripts to launch as root, you can change:

 ;user nobody
 ;group nobody

to:

 user nobody
 group nobody

create the client tarball you will distribute

still from the /etc/openvpn/easy-rsa directory, type:

tar -cvjpf vpnaccess.tar.bz2 client.conf, keys/ca.crt keys/My_Client_Name.key keys/My_Client_Name.crt keys/ta.key
Note: You need to generate client keys for each client. If you do not wish to generate multiple client keys, modify the client.conf file and generate a new tarball for each new client, you can simply uncomment ;duplicate-cn (or add duplicate-cn) in the server.conf file on the server side. BUT, if you do so, if you need to revoke a client, you would revoke all the clients… and need to issue new certificates for the clients.

Deploy the Client

Install openvpn on the client machine

On Centos/RHEL:
sudo yum install openvpn
On Ubuntu:
sudo apt-get install openvpn

Install the client configuration tarball

scp cloud@:/etc/openvpn/easy-rsa/vpnaccess.tar.bz2 .
 sudo cp vpnaccess.tar.bz2 /etc/openvpn/
 sudo su -
 cd /etc/openvpn
 tar -xvjpf vpnaccess.tar.bz2

Launch the client

service openvpn start

Full client configuration file example

If instead of editing a sample configuration file you prefer creating one from scratch, here is a full client.conf file example (comments removed):

client
 dev tun
 proto udp
 remote You_Instance_Floating_IP 1194 # Replace You_Instance_Floating_IP
 resolv-retry infinite
 nobind
 user nobody
 group nogroup
 persist-key
 persist-tun
 ca keys/ca.crt
 cert keys/My_Client_Name.crt
 key keys/My_Client_Name.key
 ns-cert-type server
 tls-auth keys/ta.key 1
 comp-lzo
 verb 3

Deployment via Heat

Download this openvpn heat template designed for Ubuntu 14.04.

Run these commands and note carefully what is required to note in the comments beside the command:

 nova image-list # Note the Ubuntu 14.04 image_id
 neutron net-list # Note the public_net_id and private_net_id and private_net_cidr

change image_idpublic_net_idprivate_net_idvpn_cidr in the openvpn.heat template knowing that:

  • vpn_cidr must be different and not overlap with the private_net_cidr noted in the step above.
  • vpn_cidr must also be different and not overlap with the private network your clients will be directly connected to.

i.e.: If you client is your laptop and your laptop is in a 192.168.10.0/24 Network, and your VM is in a 172.16.0.0/12 Network, the vpn_cidr must be in a different and not overlapping network (the default 10.8.0.0/24 provided in the template works perfectly well in this case).

Launch the stack:

heat stack-create openvpn -f openvpn.heat

And wait for the VPN to run.

Once the VPN is up, list your VM instances:

nova list # Note the floating IP.

Install OpenVPN on the client:

On Centos/RHEL:

sudo yum install openvpn

On Ubuntu:

sudo apt-get install openvpn

Retrieve the client configuration file:

scp cloud@floating_ip_noted_previously:~/vpnaccess.tar.bz2 .

(If you get an error message saying this file does not exist, please wait a few minutes “after” the stack to have fully completed, indeed, certain commands (e.g.: the Diffie Hellman generation) take a certain time after the stack completed)

Copy this vpnaccess.tar.bz2 to the /etc/openvpn/ directory of your client:
sudo cp vpnaccess.tar.bz2 /etc/openvpn

Extract the archive and launch the client:

sudo su -
 cd /etc/openvpn
 tar -xvjpf vpnaccess.tar.bz2
 service openvpn start

Check the vpn connection

You should now be able to ping and/or ssh your VMs directly with their internal IPs.