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_COUNTRY, KEY_PROVINCE, KEY_CITY, KEY_ORG, KEY_EMAIL, KEY_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.crt, My_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.crt, My_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_id, public_net_id, private_net_id, vpn_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.