Creating VPNs with OpenVPN

Introduction

A VPN is a set of tools which allow networks at different locations to be securely connected, using a public network as the transport layer. A VPN produces a virtual “dedicated circuit” over the internet and use cryptography to secure it.

A SSL VPN uses industry standard SSL and runs in user space, simplifying administration and implementation. The SSL VPN is just a web application that tries to give users the services they need without a full VPN implementation.

OpenVPN is an opensource SSL VPN solution targeted for SMEs and enterprises. Wide range of configurations exists for openvpn including site-to-site VPNs, remote access, Wi-Fi security etc. It uses the tun interface for virtual linking of the two end points of the VPN. A tun interface is a virtual network adapter that looks like point-to-point network hardware to the OS. But since its virtual, the data is actually pushed to the user space program which can then open a tun device like a file and read and write IP packets from and to it.

NOTE – A tap interface is similar to the tun except that it emulates ethernet rather than point-to-point.

In a user space VPN the IP packets from a tuntap adapter are encrypted and then encapsulated into UDP and sent over the internet. At the destination the remote host deencapsulates the IP packet, decrypts and authenticates before pumping it into a tun/tap virtual adapter.

Configuring OpenVPN

There are various ways to configure OpenVPN and you can choose the one matching your requirement. In this article we will see a few types of configurations which are typically used.

To start, install openvpn as follows. The server setup in this article has always used a linux system and various clients have been used for testing like windows-xp with openvpn-gui, fedora and ubuntu with network-manager openvpn plugin, debian, centos and fedora with openvpn client through command line.

We will start with installing openvpn from the distributions repository.

On CentOS 5.3 I had activated the Fedora EPEL repository for OpenVPN package to make sure that I use a 2.1 version build.

$ sudo yum install openvpn

The configuration files are kept in the /etc/openvpn directory. The init script installed with the package takes care of registering the openvpn with the chkconfig system, so that it can be stared and stopped using the service command. While starting the openvpn service, the init script takes care of loading all the configuration files from /etc/openvpn directory with .conf extension. If for a particular configuration some initialization is to be done then a script can be created in /etc/openvpn directory by the same name as the configuration file with .sh extension. So, for example our configuration file is called /etc/openvpn/server.conf a script /etc/openvpn/server.sh can be created which will be executed before loading the configuration from server.conf file.

We will use the following directory structure for our setup so let us create it:

$ sudo mkdir /etc/openvpn/keys

Regardless of the configuration chosen, there are few options which are used in all types of configurations. Although some of these are default settings, still it is a good idea to specify them in the configuration file. In the following example, the static key configuration is the most basic configuration, so I will describe all basic configurations there and from there on for other configurations just the additions and removals will be specified. Initially we are only allowing the client and server to ping each other. After reviewing all
popular configurations we will have a look at how to provide the clients access to the private network behind the VPN and also to configure a proxy to let the clients surf the net securely.

NOTE – On the CentOS Server SELINUX was enabled so the server set-up described works on the SELINUX enabled boxes. At places I have shown the output displaying the SELINUX contexts.

Static Key Configuration

The static key configuration of openvpn is the most basic one and only allows one client to connect to one server. This simple setup can be used by you if you want to setup a VPN connectivity between your laptop or home computer and one of your servers on the internet somewhere. This is the quickest configuration to setup. However, we need to make sure that after the key is generated at one end, it is securely transferred to the other end before initiating the connection. This is only a one time job, so the pain is
worth the simplicity.

To start we will generate the static key as

root@vpn.unixclinic.net # cd /etc/openvpn/keys
root@vpn.unixclinic.net # /usr/sbin/openvpn --genkey --secret static.key
root@vpn.unixclinic.net # ls -lZ
-rw------- root root user_u:object_r:openvpn_etc_rw_t static.key

Now let us configure the server part:

root@vpn.unixclinic.net # vi /etc/openvpn/static-server.conf
port 1194
proto udp
dev tun
# The keep alive directive is particularly important if you are using UDP
# through a stateful firewall like Netfilter. Because UDP is connectionless
# any stateful firewall will forget about the connection if packets are not
# going through it at regular intervals.
keepalive 10 60
ping-timer-rem
persist-tun
persist-key
# Enable compression (use only if compiled with lzo support)
comp-lzo
# Short log of active connections and internal routing table.
# Recreated every minute.
status openvpn-status.log
log-append openvpn.log
# Verbosity level in the log (0=silent, 3 or 4=normal, 9=maximum for
debugging)
verb 3
### Change following for different configurations
# For Static key server configuration
ifconfig 10.8.0.1 10.8.0.2
secret /etc/openvpn/keys/static.key

At the client side the configuration file will look like below. As you can see that there is only one line which is majorly different and that is the ifconfig line, where the IP addresses are reversed. Also there is a configuration option remote at the client side. The purpose of this option is to specify the openvpn server to which this client has to connect to. The remote option can take both the IP address and resolvable hostname. Please note that you need to find a pre-existing secure channel for the static key to be
transferred to the client from the server.

root@client # vi /etc/openvpn/static-client.conf
port 1194
proto udp
dev tun
# The keep alive directive is particularly important if you are using UDP
# through a stateful firewall like Netfilter. Because UDP is connectionless
# any stateful firewall will forget about the connection if packets are not
# going through it at regular intervals.
keepalive 10 60
ping-timer-rem
persist-tun
persist-key
# Enable compression (use only if compiled with lzo support)
comp-lzo
# Short log of active connections and internal routing table.
# Recreated every minute.
status openvpn-status.log
log-append openvpn.log
# Verbosity level in the log (0=silent, 3 or 4=normal, 9=maximum for
debugging)
verb 3
# Remote OpenVPN server to connect to
remote vpn.unixclinic.net
### Change following for different configurations
# For Static key client configuration
ifconfig 10.8.0.2 10.8.0.1
secret /etc/openvpn/keys/static.key

Now start the openvpn server at both the ends and tail the log files at both ends for any messages.

root@vpn.unixclinic.net # tail -50 /etc/openvpn/openvpn.log
Thu Jun 18 03:16:04 2009 OpenVPN 2.1_rc15 x86_64-redhat-linux-gnu [SSL] [LZO2] [EPOLL] built on Nov 30 2008
Thu Jun 18 03:16:04 2009 NOTE: the current --script-security setting may allow this configuration to call user-defined scripts
Thu Jun 18 03:16:04 2009 Static Encrypt: Cipher 'BF-CBC' initialized with 128 bit key
Thu Jun 18 03:16:04 2009 Static Encrypt: Using 160 bit message hash 'SHA1' for HMAC authentication
Thu Jun 18 03:16:04 2009 Static Decrypt: Cipher 'BF-CBC' initialized with 128 bit key
Thu Jun 18 03:16:04 2009 Static Decrypt: Using 160 bit message hash 'SHA1' for HMAC authentication
Thu Jun 18 03:16:04 2009 LZO compression initialized 
Thu Jun 18 03:16:04 2009 TUN/TAP device tun0 opened
Thu Jun 18 03:16:04 2009 TUN/TAP TX queue length set to 100
Thu Jun 18 03:16:04 2009 /sbin/ip link set dev tun0 up mtu 1500
Thu Jun 18 03:16:04 2009 /sbin/ip addr add dev tun0 local 10.8.0.1 peer 10.8.0.2
Thu Jun 18 03:16:04 2009 Data Channel MTU parms [ L:1545 D:1450 EF:45 EB:135 ET:0 EL:0 AF:3/1 ]
Thu Jun 18 03:16:04 2009 Local Options hash (VER=V4): '4b91e501'
Thu Jun 18 03:16:04 2009 Expected Remote Options hash (VER=V4): '48593abd'
Thu Jun 18 03:16:04 2009 Socket Buffers: R=[124928->131072] S=[124928->131072]
Thu Jun 18 03:16:04 2009 UDPv4 link local (bound): [undef]:1194
Thu Jun 18 03:16:04 2009 UDPv4 link remote: [undef]
Thu Jun 18 03:16:09 2009 Peer Connection Initiated with 116.87.186.181:1194
Thu Jun 18 03:16:10 2009 Initialization Sequence Completed
Thu Jun 18 03:18:46 2009 write UDPv4 [EHOSTUNREACH]: Operation not permitted (code=1)

root@client # tail -50 /etc/openvpn/openvpn.log
Thu Jun 18 15:16:08 2009 OpenVPN 2.1_rc15 x86_64-redhat-linux-gnu [SSL] [LZO2] [EPOLL] built on Nov 30 2008
Thu Jun 18 15:16:08 2009 NOTE: the current --script-security setting may allow this configuration to call user-defined scripts
Thu Jun 18 15:16:08 2009 Static Encrypt: Cipher 'BF-CBC' initialized with 128 bit key
Thu Jun 18 15:16:08 2009 Static Encrypt: Using 160 bit message hash 'SHA1' for HMAC authentication
Thu Jun 18 15:16:08 2009 Static Decrypt: Cipher 'BF-CBC' initialized with 128 bit key
Thu Jun 18 15:16:08 2009 Static Decrypt: Using 160 bit message hash 'SHA1' for HMAC authentication
Thu Jun 18 15:16:08 2009 LZO compression initialized
Thu Jun 18 15:16:08 2009 TUN/TAP device tun0 opened
Thu Jun 18 15:16:08 2009 TUN/TAP TX queue length set to 100
Thu Jun 18 15:16:08 2009 /sbin/ip link set dev tun0 up mtu 1500
Thu Jun 18 15:16:08 2009 /sbin/ip addr add dev tun0 local 10.8.0.2 peer 10.8.0.1
Thu Jun 18 15:16:08 2009 Data Channel MTU parms [ L:1545 D:1450 EF:45 EB:135 ET:0 EL:0 AF:3/1 ]
Thu Jun 18 15:16:08 2009 Local Options hash (VER=V4): '48593abd'
Thu Jun 18 15:16:08 2009 Expected Remote Options hash (VER=V4): '4b91e501'
Thu Jun 18 15:16:08 2009 Socket Buffers: R=[124928->131072] S=[124928->131072]
Thu Jun 18 15:16:08 2009 UDPv4 link local (bound): [undef]:1194
Thu Jun 18 15:16:08 2009 UDPv4 link remote: 173.45.227.64:1194
Thu Jun 18 15:16:14 2009 Peer Connection Initiated with 173.45.227.64:1194
Thu Jun 18 15:16:14 2009 Initialization Sequence Completed

Now you can ping the client from server and server from client.

root@vpn.unixclinic.net # ping -c2 10.8.0.2
PING 10.8.0.2 (10.8.0.2) 56(84) bytes of data.
64 bytes from 10.8.0.2: icmp_seq=1 ttl=64 time=256 ms
64 bytes from 10.8.0.2: icmp_seq=2 ttl=64 time=256 ms

root@client # ping -c2 10.8.0.1
PING 10.8.0.1 (10.8.0.1) 56(84) bytes of data.
64 bytes from 10.8.0.1: icmp_seq=1 ttl=64 time=256 ms
64 bytes from 10.8.0.1: icmp_seq=2 ttl=64 time=255 ms

This completes our static key based setup. Let us now move on to certificate based authentication.

Certificate Based Authentication

The static key based setup we saw above will limit us to one client connecting to one server and also does not provide enough security. OpenVPN’s support for PKI (Public Key Infrastructure) allows multiple clients to connect to the open vpn server securely. The number of clients is only limited by the available bandwidth and the hardware resources available with the openvpn server.

If you are setting the OpenVPN for your corporate use then you may want to use a comercial CA for issuance of certificates. However, if you want to avoid that additional cost, you can create your own CA for your internal use.

For those who just want to setup a quick PKI for use by OpenVPN only or for laboratory testing of the concept before rolling out the production setup openvpn bundles a set of scripts and tools called easy-rsa. You will get easy-rsa from any OpenVPN version downloaded from the main website. If you have installed the packaged openvpn from your distribution, you should find this typically at /usr/share/doc/openvpn/examples/easy-rsa in Debian and /usr/share/openvpn/easy-rsa in CentOS. Please feel free to copy this directory to a convenient location of your choice. I typically put this in /opt/openvpneasy-rsa-2.0 directory and put that in my path, so I will assume that is the case here:

root@vpn.unixclinic.net # mkdir -p /opt/easy-rsa/keys

If you have a CentOS server then copy as follows:

root@vpn.unixclinic.net # cp -r /usr/share/openvpn/easy-rsa/2.0/* /opt/easy-rsa/

On a Debian server copy the easy-rsa as follows:

root@vpn.unixclinic.net # cp -r /usr/share/doc/openvpn/examples/easy-rsa/2.0/* /opt/easy-rsa/

Next put the directory in your path for easier working with easy-rsa.

root@vpn.unixclinic.net # export PATH=$PATH:/opt/easy-rsa

The file /opt/easy-rsa/vars contains the required environment variables for generating the keys. This file is very-well commented and hence I would not be describing this here. Most of the variable can be left to their default values. Make sure you change the EASY_RSA variable to the directory where the easy-rsa tools are copied. The default value may not suit our requirements:

export EASY_RSA="/opt/easy-rsa"

You may want to change the following variables. Following, are the values for these variables which are used for the purpose of this article:

export KEY_COUNTRY="IN"
export KEY_PROVINCE="UA"
export KEY_CITY="Nainital"
export KEY_ORG="Unixclinic"
export KEY_EMAIL="casupport@unixclinic.net"

NOTE – By default the root CA key and certificates expiry is set to 3650 days (10 years). Practically the certificates should expire typically after one year or two years. So I additionally changed the KEY_EXPIRE to 365 days.

Now let us start generating the necessary keys and certificates. To start with we need to source the variables specified in vars file and make sure that existing keys directory is cleaned. Pay extra attention to the ‘.’ in the following command. This ‘.’ will make sure that the environment variables will be setup in the current shell.

root@vpn.unixclinic.net # . vars
NOTE: If you run ./clean-all, I will be doing a rm -rf on /opt/easy-rsa/keys
root@vpn.unixclinic.net # clean-all

Next we need to build the Diffie-Hellman (DH) parameters. It took me less than a min to generate a 1024 bit long safe prime on my server. Might take longer on your machine. I have snipped the output.

root@vpn.unixclinic.net # build-dh
Generating DH parameters, 1024 bit long safe prime, generator 2
This is going to take a long time
................................................+

.... [snipped..]

Next we will generate the root CA certificate and key:

root@vpn.unixclinic.net # build-ca
Generating a 1024 bit RSA private key
.++++++
..++++++
writing new private key to 'ca.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [IN]:
State or Province Name (full name) [UA]:
Locality Name (eg, city) [Nainital]:
Organization Name (eg, company) [Unixclinic]:
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) [Unixclinic CA]:unixclinic.net
Name []:Unixclinic CA
Email Address [casupport@unixclinic.net]:

Now that our CA has been setup, we need to create a certificate for our VPN server. This can be done as follows:

root@vpn.unixclinic.net # build-key-server vpn.unixclinic.net
Generating a 1024 bit RSA private key
....++++++
..++++++
writing new private key to 'vpn.unixclinic.net.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [IN]:
State or Province Name (full name) [UA]:
Locality Name (eg, city) [Nainital]:
Organization Name (eg, company) [Unixclinic]:
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) [vpn.unixclinic.net]:
Name []:OpenVPN Server
Email Address [casupport@unixclinic.net]:vpnsupport@unixclinic.net

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:password
An optional company name []:Unixclinic
Using configuration from /opt/easy-rsa/openssl.cnf
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
countryName           :PRINTABLE:'IN'
stateOrProvinceName   :PRINTABLE:'UA'
localityName          :PRINTABLE:'Nainital'
organizationName      :PRINTABLE:'Unixclinic'
commonName            :PRINTABLE:'vpn.unixclinic.net'
name                  :PRINTABLE:'OpenVPN Server'
emailAddress          :IA5STRING:'vpnsupport@unixclinic.net'
Certificate is to be certified until Jun 22 04:24:13 2010 GMT (365 days)
Sign the certificate? [y/n]:y

1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

Now we will have to copy the server certificate to the /etc/openvpn/keys directory and make sure that the private key has the most restrictive permissions:

root@vpn.unixclinic.net # cp -v /opt/easy-rsa/keys/vpn.unixclinic.net.{crt,key} /opt/easy-rsa/keys/dh1024.pem /etc/openvpn/keys/
'/opt/easy-rsa/keys/vpn.unixclinic.net.crt' -> '/etc/openvpn/keys/vpn.unixclinic.net.crt'
'/opt/easy-rsa/keys/vpn.unixclinic.net.key' -> '/etc/openvpn/keys/vpn.unixclinic.net.key'
'/opt/easy-rsa/keys/dh1024.pem' -> '/etc/openvpn/keys/dh1024.pem'

root@vpn.unixclinic.net # cp -v /opt/easy-rsa/keys/ca.crt /etc/openvpn/keys/

'/opt/easy-rsa/keys/ca.crt' -> '/etc/openvpn/keys/ca.crt'

root@vpn.unixclinic.net # chmod 0600 /etc/openvpn/keys/vpn.unixclinic.net.key

Now let us configure the server configuration file, I will call this keyauth-server.conf. Copy the contents till the line which states “Change following for different configurations”. Add the following lines to the configuration:

##### PKI authentication

dh /etc/openvpn/keys/dh1024.pem

ca /etc/openvpn/keys/ca.crt
cert /etc/openvpn/keys/vpn.unixclinic.net.crt

# The following file should be kept very secret.
key /etc/openvpn/keys/vpn.unixclinic.net.key

# Specifies the range of IP addresses allocated by server to client.
# The server itself will take 10.8.0.1 as its IP address.
server 10.8.0.0 255.255.255.0

# Makes sure that if available the client always gets the previous IP address.
# The record of IP addresses allocated to client is in ipp.txt file.
ifconfig-pool-persist ipp.txt

# Maximum number of clients which can connect, default is 100.
max-clients 10

Now we need to generate the client certificate as follows:

root@vpn.unixclinic.net # ./build-key ajitabh
Generating a 1024 bit RSA private key
...++++++
........++++++
writing new private key to 'ajitabh.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [IN]:
Stapte or Province Name (full name) [UA]:
Locality Name (eg, city) [Nainital]:
Organization Name (eg, company) [Unixclinic]:
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) [ajitabh]:
Name []:Ajitabh Pandey
Email Address [casupport@unixclinic.net]:ajitabhp@unixclinic.net

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:password
An optional company name []:   
Using configuration from /opt/easy-rsa/openssl.cnf
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
countryName           :PRINTABLE:'IN'
stateOrProvinceName   :PRINTABLE:'UA'
localityName          :PRINTABLE:'Nainital'
organizationName      :PRINTABLE:'Unixclinic'
commonName            :PRINTABLE:'ajitabh'
name                  :PRINTABLE:'Ajitabh Pandey'
emailAddress          :IA5STRING:'ajitabhp@unixclinic.net'
Certificate is to be certified until Jun 23 04:38:27 2010 GMT (365 days)
Sign the certificate? [y/n]:y

1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

While creating the client certificate, pay special attention to the CN (Common Name), it should be unique as well as easily identifiable just in case you decide to do the client specific configuration later on. Also try not to put blank space in the CN. After the client key is created transfer the .crt, .key and ca.crt files securely to the client using some pre-exsiting secure channel. I typically consider SCP/SFTP to be pretty safe. You can use USB thumbdrives to transfer the client certificate. Please note that the OpenVPN server does not need to know anything about the client certificates. So feel free to generate the client certificates on a seperate machine but make sure that they are signed by the same CA using which the server’s keys are signed. I used the same machine to generate the client certificate.

If you are using OpenVPN client then add the following in the configuration for PKI authentication:

## PKI Authentication
# Tell the OpenVPN that we are client.
# This will be use full to pull configuration settings from server later on.
client
ca /etc/openvpn/keys/ca.crt
cert /etc/openvpn/keys/ajitabh.crt
key /etc/openvpn/keys/ajitabh.key

Start the OpenVPN server and client and they should connect. Check out which IP address has been allocated by the server to the client before pinging. The server does not allocate the IP addresses in a sequence.

Using IPTABLES For Simple Internal Network Access

In the multi-client setup often you may require to provide protected network access to the VPN clients connecting to the server. There are two ways of doing this:

  • Configure the default router of your organisation to send all traffic destined for VPN addresses (10.8.0.0/24 in our case) to the VPN server.
  • Masquerade all the VPN traffic to originate from the VPN server internal IP address (not the 10.8.0.1)

In most of the case my clients have chosen to go for masquerading as they host their server in external data centre and do not have control of the router. This can be done as follows:

root@vpn.unixclinic.net # iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

I am assuming that the VPN server is connected to the internal network through eth0 interface

Further one of my clients posed the requirement of forcing all port 80 traffic coming from the VPN clients to go through their squid proxy. This can be achieved by following simple iptables rule:

root@vpn.unixclinic.net # iptables -t nat -A PREROUTING -i tun+ -p tcp --dport 80 -j REDIRECT --to-port 3128

Moving On

In this article we have seen two of the very commonly used configurations for OpenVPN. I have used OpenVPN with various types of configurations such as authentications using a MySQL database, freeradius backends, pam etc. If time permits I will try to cover these configurations in some future article.

References

OpenVPN Documentation
OpenVPN FAQ
OpenVPN Articles
The user-space VPN and OpenVPN

This entry was posted in FLOSS, Published Articles, Security and tagged , , , . Bookmark the permalink.

Leave a Reply