
This article mainly re-uses the OpenStack official documentation. Since the latter has errors in it, I fixed them. It’s fully functionnal under Ubuntu 12.04 distro.
I. Cloud Pipe VPN image template
First run a new empty instance. If you use the Ubuntu Cloud repo image some extra packages are needed. From now we will work inside our fresh Ubuntu instance.
$ sudo apt-get update && sudo apt-get upgade
$ sudo apt-get install openvpn bridge-utils unzip -y
Create the openvpn configuration file called server.conf.template in /etc/openvpn/, with the following content:
port 1194
proto udp
dev tap0
up "/etc/openvpn/up.sh br0"
down "/etc/openvpn/down.sh br0"
script-security 3 system
persist-key
persist-tun
ca ca.crt
cert server.crt
key server.key # This file should be kept secret
dh dh1024.pem
ifconfig-pool-persist ipp.txt
server-bridge VPN_IP DHCP_SUBNET DHCP_LOWER DHCP_UPPER
client-to-client
keepalive 10 120
comp-lzo
max-clients 1
user nobody
group nogroup
persist-key
persist-tun
status openvpn-status.log
verb 3
mute 20
Create the script which bring up the bridge network interface, call it up.sh:
#!/bin/sh
BR=$1
DEV=$2
MTU=$3
/sbin/ifconfig $DEV mtu $MTU promisc up
/sbin/brctl addif $BR $DEV
Create the script which bring down the bridge network interface, call it down.sh:
#!/bin/sh
BR=$1
DEV=$2
/usr/sbin/brctl delif $BR $DEV
/sbin/ifconfig $DEV down
Don’t forget to make executable!
$ sudo chmod +x /etc/openvpn/{up.sh,down.sh}
Modify your network parameters in /etc/network/interfaces
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).
# The loopback network interface
auto lo
iface lo inet loopback
# The primary network interface
auto eth0
iface eth0 inet manual
up ifconfig $IFACE 0.0.0.0 up
down ifconfig $IFACE down
auto br0
iface br0 inet dhcp
bridge_ports eth0
Eventually edit your /etc/rc.local like so:
#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.
####### These lines go at the end of /etc/rc.local #######
. /lib/lsb/init-functions
echo Downloading payload from userdata
wget http://169.254.169.254/latest/user-data -O /tmp/payload.b64
echo Decrypting base64 payload
openssl enc -d -base64 -in /tmp/payload.b64 -out /tmp/payload.zip
mkdir -p /tmp/payload
echo Unzipping payload file
unzip -o /tmp/payload.zip -d /tmp/payload/
# if the autorun.sh script exists, run it
if [ -e /tmp/payload/autorun.sh ]; then
echo Running autorun.sh
cd /tmp/payload
sh /tmp/payload/autorun.sh
if [ ! -e /etc/openvpn/dh1024.pem ]; then
openssl dhparam -out /etc/openvpn/dh1024.pem 1024
fi
chmod 700 /etc/openvpn/server.key
else
echo rc.local : No autorun script to run
fi
exit 0
For those of you who are curious here is the content of the autorun.sh script:
#!/bin/bash
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# This gets zipped and run on the cloudpipe-managed OpenVPN server
export LC_ALL=C
export VPN_IP=`ifconfig | grep 'inet addr:'| grep -v '127.0.0.1' | cut -d: -f2 | awk '{print $1}'`
export BROADCAST=`ifconfig | grep 'inet addr:'| grep -v '127.0.0.1' | cut -d: -f3 | awk '{print $1}'`
export DHCP_MASK=`ifconfig | grep 'inet addr:'| grep -v '127.0.0.1' | cut -d: -f4 | awk '{print $1}'`
export GATEWAY=`netstat -r | grep default | cut -d' ' -f10`
DHCP_LOWER=`echo $BROADCAST | awk -F. '{print $1"."$2"."$3"." $4 - 5 }'`
DHCP_UPPER=`echo $BROADCAST | awk -F. '{print $1"."$2"."$3"." $4 - 1 }'`
# generate a server DH
openssl dhparam -out /etc/openvpn/dh1024.pem 1024
cp crl.pem /etc/openvpn/
cp server.key /etc/openvpn/
cp ca.crt /etc/openvpn/
cp server.crt /etc/openvpn/
# Customize the server.conf.template
cd /etc/openvpn
sed -e s/VPN_IP/$VPN_IP/g server.conf.template > server.conf
sed -i -e s/DHCP_SUBNET/$DHCP_MASK/g server.conf
sed -i -e s/DHCP_LOWER/$DHCP_LOWER/g server.conf
sed -i -e s/DHCP_UPPER/$DHCP_UPPER/g server.conf
sed -i -e s/max-clients\ 1/max-clients\ 10/g server.conf
echo "push \"route 10.0.0.0 255.255.255.0 $GATEWAY\"" >> server.conf
echo "duplicate-cn" >> server.conf
echo "crl-verify /etc/openvpn/crl.pem" >> server.conf
/etc/init.d/openvpn start
Now your instance is ready to be snapshoted and stored in Glance. The following commands will select the id of our Ubuntu template and create a new image based on it.
$ nova list
+--------------------------------------+------------+--------+---------------------+
| ID | Name | Status | Networks |
+--------------------------------------+------------+--------+---------------------+
| 739079ab-0f8e-404a-ae6e-a91f4fe99c94 | cloud-pipe | ACTIVE | vlan1=192.168.22.43 |
+--------------------------------------+------------+--------+---------------------+
$ nova image-create 739079ab-0f8e-404a-ae6e-a91f4fe99c94 cloud-pipe-template
$ nova image-list
+--------------------------------------+---------------------+--------+--------------------------------------+
| ID | Name | Status | Server |
+--------------------------------------+---------------------+--------+--------------------------------------+
| 0bfc8fd3-1590-463b-b178-bce30be5ef7b | cloud-pipe-template | ACTIVE | fb93eda8-4eb8-42f7-b53c-91c6d83cface |
+--------------------------------------+---------------------+--------+--------------------------------------+
Update your image in Glance and make it public (accessible by all tenants):
$ glance update 0bfc8fd3-1590-463b-b178-bce30be5ef7b is_public=true
Updated image 0bfc8fd3-1590-463b-b178-bce30be5ef7b
$ glance show 0bfc8fd3-1590-463b-b178-bce30be5ef7b | grep Public
Public: Yes
II. CloudPipe setup
II.1 Configure OpenStack to use the template
Add some options to your nova.conf like the id of you image, this will tell nova to call the vpn profile when our vpn-image is called:
## cloud-pipe vpn client ##
--vpn_image_id=0bfc8fd3-1590-463b-b178-bce30be5ef7b
--use_project_ca=true
--cnt_vpn_clients=5
Restart all your nova services.
II.2. Create the VPN
You are ready to run your cloud-pipe instance from any tenant. The command line tool is pretty unclear:
$ nova help cloudpipe-create
usage: nova cloudpipe-create <project>
Create a cloudpipe instance for the given project
Positional arguments:
<project> Name of the project.
The CLI suggests to use the Name of the project but that won’t work, the correct syntax is to use the id of the tenant:
$ keystone tenant-list
+----------------------------------+---------+---------+
| id | name | enabled |
+----------------------------------+---------+---------+
| 071ffb95837e4d509cb7153f21c57c4d | stone | True |
| 520b6689e344456cbb074c83f849914a | service | True |
| d1f5d27ccf594cdbb034c8a4123494e9 | admin | True |
| dfb0ef4ab6d94d5b9e9e0006d0ac6706 | demo | True |
+----------------------------------+---------+---------+
$ nova cloudpipe-create d1f5d27ccf594cdbb034c8a4123494e9
Use this command to verify:
$ nova cloudpipe-list
+----------------------------------+------------+-------------+---------------+
| Project Id | Public IP | Public Port | Internal IP |
+----------------------------------+------------+-------------+---------------+
| d1f5d27ccf594cdbb034c8a4123494e9 | 172.17.1.3 | 1000 | 192.168.22.34 |
+----------------------------------+------------+-------------+---------------+
II.3. Under the hood
II.3.1. Security rules
This will run a new instance called <project-id>-vpn. In VLAN networking mode, the second IP in each private network is reserved for the cloudpipe instance. Nova network will automatically create a new security group called <project id>-vpn, assigned this group to the vpn instance and eventually will allow those rules:
ALLOW 1194:1194 from 0.0.0.0/0
ALLOW -1:-1 from 0.0.0.0/0
II.3.2. Credentials
An SSH key has been generated here /var/lib/nova/keys, you can use it to log into the VPN instance. Certificates are stored in /var/lib/nova/CA/projects/<tenant-id>. Basically:
- Server CA file is located in
/var/lib/nova/CA/projects/<tenant-id>/cacert.pem - New client cert are located in
/var/lib/nova/CA/projects/<tenant-id>/newcerts/
II.4. Generate client credentials
Default generated credentials are vpn server credential you must not use them, thus create client credentials. Don’t forget to install the nova-cert package.
$ nova x509-create-cert
Wrote private key to pk.pem
Wrote x509 certificate to cert.pem
Then fetch the server certificate:
$ nova x509-get-root-cert
Wrote x509 root cert to cacert.pem
Client template, which can be find here /usr/lib/python2.7/dist-packages/nova/cloudpipe/client.ovpn.template:
# NOVA user connection
# Edit the following lines to point to your cert files:
cert $certfile
key $keyfile
ca cacert.pem
client
dev tap
proto udp
remote $ip $port
resolv-retry infinite
nobind
# Downgrade privileges after initialization (non-Windows only)
user nobody
group nogroup
comp-lzo
# Set log file verbosity.
verb 2
keepalive 10 120
ping-timer-rem
persist-tun
persist-key
II.4. Troubleshooting
A periodic task will disassociate the fixed ip address for this instance, this task is identified in the log like:
Running periodic task VlanManager._disassociate_stale_fixed_ips from (pid=21578) periodic_tasks /usr/lib/python2.7/dist-packages/nova/manager.py:152
After this the nova cloudpipe-list output should be empty.
However if you re-run the cloud-pipe instance too quickly you will get an error from nova-network:
ERROR nova.rpc.amqp Returning exception Fixed IP address 192.168.22.34 is already in use.
To fix this, you need to update some fields in the nova database:
mysql> USE nova;
mysql> SELECT * FROM fixed_ips WHERE address='192.168.22.34';
+---------------------+---------------------+------------+---------+-----+---------------+------------+-------------+-----------+--------+----------+----------------------+------+
| created_at | updated_at | deleted_at | deleted | id | address | network_id | instance_id | allocated | leased | reserved | virtual_interface_id | host |
+---------------------+---------------------+------------+---------+-----+---------------+------------+-------------+-----------+--------+----------+----------------------+------+
| 2012-05-21 12:06:18 | 2012-06-18 09:26:25 | NULL | 0 | 484 | 192.168.22.34 | 13 | 630 | 0 | 0 | 1 | NULL | NULL |
+---------------------+---------------------+------------+---------+-----+---------------+------------+-------------+-----------+--------+----------+----------------------+------+
mysql> UPDATE fixed_ips SET allocated=0, leased=0, instance_id=NULL WHERE address='192.168.22.34';
mysql> SELECT * FROM fixed_ips WHERE address='192.168.22.34';
+---------------------+---------------------+------------+---------+-----+---------------+------------+-------------+-----------+--------+----------+----------------------+------+
| created_at | updated_at | deleted_at | deleted | id | address | network_id | instance_id | allocated | leased | reserved | virtual_interface_id | host |
+---------------------+---------------------+------------+---------+-----+---------------+------------+-------------+-----------+--------+----------+----------------------+------+
| 2012-05-21 12:06:18 | 2012-06-18 09:26:25 | NULL | 0 | 484 | 192.168.22.34 | 13 | NULL | 0 | 0 | 1 | NULL | NULL |
+---------------------+---------------------+------------+---------+-----+---------------+------------+-------------+-----------+--------+----------+----------------------+------+
II.5. Bonus
See below all the nova.conf options related to the Cloud Pipe VPN:
vpn_ip = <COMPUTE_NODE_IP or PUBLIC_IP>
vpn_start = 1000
vpn_key_suffix = -vpn
vpn_client_template = /usr/lib/python2.7/dist-packages/nova/cloudpipe/client.ovpn.template
credential_vpn_file = nova-vpn.conf
vpn_image_id = IMAGE_ID
cnt_vpn_clients = 5
keys_path = /var/lib/nova/keys
ca_path = /var/lib/nova/CA
Some options can be managed by the nova-manage command:
$ sudo nova-manage vpn change --ip=<ip> --project=<project-id> --port=<port-number>
For an automatic installation I forked the Mirantis repo and made some minor changes. Now the scripts should be compatible with Ubuntu 12.04, I only modified the
cloudpipeconf.shscript according to my tests, so I don’t guarantee that the full project will work for you. Many thanks to Mirantis for the original script. See my fork on Github and the automatic installation script.