Verbose VPN server installation using OpenVPN and OpenSSL



VPN (virtual private network) is a private, encrypted network that traverses a public network (such Internet). This is a way of giving remote users access to local network resources as if they were themselves local.

OpenVPN is a open-source VPN software that allows creating encrypted tunnels between computers. The secure authentication and encryption functions relies on OpenSSL library that implements the basic cryptographic functions.

This post shows how to setup a VPN server with certificate-based authentication using OpenVPN and OpenSSL. Also, in tutorial will be shown how to connect to this VPN server from Linux and Windows machines.

Configure OpenSSL

SSL is based on public-key cryptography where each party has two key: one public and one private. Private key is used to sign data digitally then you send it, and to decrypt data that have been sent to you. Public key is used to verify data that have been signed and sent to you, and to encrypt data that you will send.

In SSL's parlance, a certificate contains the public key, some information about the owner of the certificate (such name, organization, e-mail etc.), a hash to ensure that the certificate has not been tampered and the certificate ID of third party who certifies (signs) this information.

This third party is usually Certificate Authority (CA), which attests that the public key contained in the certificate, belongs to the person, organization, server or other entity noted in the certificate.

In essence, a CA is responsible for verifying the identity of a requesting entity before issuing a certificate. After the identity verification, CA signs the certificate using its private key, which is used to verify the certificate. A CA's certificates and certificate revocation lists (CRLs – containing certificates that have been revoked) are distributed.

In this article, will be set up a own CA, because in contrast to commercial third party CA providers, a own CA is free, and ideal for services not offered to the public. The CA does not need to be on the same system as the OpenVPN server. It is safer to have it in a less accessible place.

First step is to establish a OpenVPN PKI (public key infrastructure). The PKI consists of:

CA private key
Is the root of trust. Should be kept secret and it is used to sign other certificates.

CA certificate
Used by everyone to check CA signatures. It is public, contains the CA public key and is signed by CA private key

Server private key
Should be kept secret on VPN server.

Server certificate
It is public and is signed by CA.

Client private key (for each client)
Secret key that should be kept on VPN clients.

Client certificate
Public accesible and it is signed by CA.

CRL (certificate revocation list) allows compromised certificates to be selectively rejected without requiring that the entire PKI be rebuilt. If a one of private keys is compromised, it can be disabled by adding its certificate to a CRL.

It is time to proceed to practice – make sure that OpenSSL is compiled from source and if it isn't, reinstall it:

emerge -pv openssl
echo "dev-libs/openssl -bindist" >> /etc/portage/package.use
emerge -av openssl

Create default directory in /root from that CA will read and write to:

mkdir vpnCA

Create directory for server and clients issued certificates:

mkdir vpnCA/certs

Create directory for issued certificate revocation lists (CRLs):

mkdir vpnCA/crls

Create the DB index file – an ASCII file with a line for every certificate issued. OpenSSL uses this internally to keep track of things:

touch vpnCA/index.txt

Create directories for private keys (CAkey – for CA private key, Pkeys – for server and clients private keys):

mkdir vpnCA/CAkey vpnCA/Pkeys
chmod 700 vpnCA/CAkey vpnCA/Pkeys

Create directory for requests that will be sent to the CA for being accepted:

mkdir vpnCA/reqs

Initialize file that will be used to keep track of the next serial number that will be assigned to a certificate when it is issued:

echo "01" > vpnCA/serial

A configuration file for CA will be made separately from the system OpenSSL configuration file. Create a file named openssl.cnf in home directory and fit it with following contents:

# Define the way the CA acts when using the ca command

# to sign certificates
[ ca ]

# Tells the ca command to look for a section named [ CA_vpn ]
# which has actual attributes used by ca command
default_ca                              = CA_vpn

# Directives for ca command are in this section

# You can change the name of this section by changing the
# value of the default_ca attribute in the [ ca ] section

[ CA_vpn ]
# The default directory that CA reads from and writes to
dir                                     = ./vpnCA

# Directory where server & clients issued certs are kept
certs                                   = $dir/certs

# Directory where issued cert revocation lists are kept
crl_dir                                 = $dir/crls

# DB index file
database                                = $dir/index.txt

# Place copies of each issued cert in same certs dir
new_certs_dir                           = $certs

# Name of file that contains the CA certificate
certificate                             = $dir/CA_cert.pem

# The serial number which the CA is currently at
serial                                  = $dir/serial

# The file name of the current cert revocation list
crl                                     = $dir/crls/crl.pem

# Name of file that contains CA private key
private_key                             = $dir/CAkey/CA_key.pem

# A private random number file
RANDFILE                                = $dir/CAkey/.rand

# The name of a section that contains directives
# for the ca command when it signs a cert
x509_extensions                         = vpn_cert_ext

# The default nr of days a signed cert will be valid
default_days                            = 3650

# The default nr of days before the next CRL
default_crl_days                        = 365

# The hash algorithm to use. Possible are: md5, mdc2 & sha1
default_md                              = sha1

# Name of another section that defines which fields are
# mandatory or which must match the CA certificate
policy                                  = vpn_policy

# This section is referenced by the x509_extensions attribute
# in the [ CA_vpn ] section
[ vpn_cert_ext ]

# Indicate that the new-signed certificate cannot be used for
# signing/revoking other certificates
# This rule is for clients and server certificates
basicConstraints                        = CA:FALSE

# This section is referenced by the policy attribute in
# the [ CA_vpn ] section
# In the "vpn_policy" policy, all fields listed as:
# match   - must be present in the certificate request and must
# contain the exact same contents as that field in the
# CA's distingueshed name
# supplied - must be present in the certificate request
# optional - are allowed in the certificate request,
# but not required to be there
[ vpn_policy ]
organizationName                        = match
organizationalUnitName                  = supplied
commonName                              = supplied
emailAddress                            = supplied

# Define the section for the req command which
# creates and processes certificate requests in
# PKCS #10 (Public Key Cryptography Standard No. 10)
# format, creates self signed certificates for use as
# root CA certs, etc.
[ req ]

# The default key size in bits. Value is used when req is
# invoked with the -new option (new certificate request)
default_bits                            = 2048

# Name of section that defines the prompts used when asking
# the user for information needed to generate a cert
# The referenced section also gives default values (if none
# are entered) and constraints on allowed values
distinguished_name                      = req_vpn_dn

# Name of section that contains a list of extensions to add
# to certificates generated when the req command is
# invoked with the -x509 option
x509_extensions                         = vpn_CA_cert_ext

# This section is referenced by the distingueshed_name
# attribute in the [ req ] section
# Here are defined the prompts when asking the user for
# information needed to generate a cert
# It also gives default values (if the user doesn't enter
# any) and constraints on allowed values
[ req_vpn_dn ]
0.organizationName                      = Organization Name (eg. company)
0.organizationName_default              = SysAdmin.MD
organizationalUnitName                  = Organizational Unit Name
commonName                              = Common Name (eg. Linux machine)
commonName_max                          = 64
emailAddress                            = Email Address
emailAddress_max                        = 30

# This section is referenced by the x509_extensions attribute
# in the [ req ] section
# It contains directives used by the req command when
# it requests certs
[ vpn_CA_cert_ext ]

# This certificate can be used to sign or revoke other certs
# This rule is for CA certificates
basicConstraints                        = CA:TRUE

 

Generate certificates and keys for VPN server, Linux and Windows clients

Create CA certificate & key:

openssl req -new -x509 -nodes -keyout vpnCA/CAkey/CA_key.pem -out vpnCA/CA_cert.pem -days 3650 -config ./openssl.cnf

Below is a description for each parameter:
-new with -x509
Denotes that a new keypair to be created (selfsigned CA certificate with CA private key)

-nodes
The CA private key is unencrypted (without a passphrase)

-keyout
Indicate where the CA private key will be saved

-out
Indicate where the CA certificate will go

-config
Tells OpenSSL to use above config file rather than the default config

-days
Specify the default number of days a CA certificate will be valid (10 years)

The following output should be displayed:

Organization Name (eg, company) [SysAdmin.MD]:
Organizational Unit Name []:ca.sysadmin.md
Common Name (e.g., Linux machine) []:CA Server
Email Address []:nospam@sysadmin.md

To view CA private key issue:

openssl rsa -noout -text -in vpnCA/CAkey/CA_key.pem | more

To view CA certificate:

openssl x509 -noout -text -in vpnCA/CA_cert.pem | more

Generate a server private key and request for server certificate. Two files will be created:
ServerKey.pem - server private key
ServerRequest.pem - request for server certificate to be sent to the CA for being accepted

openssl req -new -nodes -keyout vpnCA/Pkeys/ServerKey.pem -out vpnCA/reqs/ServerRequest.pem -config ./openssl.cnf

The following output should be displayed:

Organization Name (eg, company) [SysAdmin.MD]:
Organizational Unit Name []:vpn.sysadmin.md
Common Name (e.g., Linux machine) []:VPN Server
Email Address []:root@vpn.sysadmin.md

To view server private key issue:

openssl rsa -noout -text -in vpnCA/Pkeys/ServerKey.pem | more

To view request for server certificate:

openssl req -noout -text -in vpnCA/reqs/ServerRequest.pem | more

Next, issuing the server certificate:

openssl ca -in vpnCA/reqs/ServerRequest.pem -batch -out vpnCA/certs/ServerCert.pem -config ./openssl.cnf

View server certificate:

openssl x509 -noout -text -in vpnCA/certs/ServerCert.pem | more

Generate a private key and request for certificate for Linux VPN client.

openssl req -new -nodes -keyout vpnCA/Pkeys/LinuxClientKey.pem -out vpnCA/reqs/LinuxClientRequest.pem -config ./openssl.cnf

Two files will be created:
LinuxClientKey.pem - Linux VPN client private key.
LinuxClientRequest.pem - request for Linux VPN client certificate to be sent to the CA for being accepted

Above command will have the following output:

Organization Name (eg, company) [SysAdmin.MD]:
Organizational Unit Name []:linux.sysadmin.md
Common Name (e.g., Linux machine) []:Linux VPN client
Email Address []:root@linux.sysadmin.md

To view Linux VPN client private key issue:

openssl rsa -noout -text -in vpnCA/Pkeys/LinuxClientKey.pem | more

View request for Linux VPN client certificate:

openssl req -noout -text -in vpnCA/reqs/LinuxClientRequest.pem | more

Issuing the Linux VPN client certificate:

openssl ca -in vpnCA/reqs/LinuxClientRequest.pem -batch -out vpnCA/certs/LinuxClientCert.pem -config ./openssl.cnf

View Linux VPN client certificate:

openssl x509 -noout -text -in vpnCA/certs/LinuxClientCert.pem | more

Generate a private key and request for certificate for Windows VPN client:

openssl req -new -nodes -keyout vpnCA/Pkeys/WindowsClientKey.pem -out vpnCA/reqs/WindowsClientRequest.pem -config ./openssl.cnf

Two files will be created:
WindowsClientKey.pem - Windows VPN client private key
WindowsClientRequest.pem - request for Windows VPN client certificate to be sent to the CA for being accepted

Organization Name (eg, company) [SysAdmin.MD]:
Organizational Unit Name []:windows.sysadmin.md
Common Name (e.g., Linux machine) []:Windows VPN client
Email Address []:root@windows.sysadmin.md

View Windows VPN client private key:

openssl rsa -noout -text -in vpnCA/Pkeys/WindowsClientKey.pem | more

View request for Windows VPN client certificate:

openssl req -noout -text -in vpnCA/reqs/WindowsClientRequest.pem | more

Issuing the Windows VPN client certificate:

openssl ca -in vpnCA/reqs/WindowsClientRequest.pem -batch -out vpnCA/certs/WindowsClientCert.pem -config ./openssl.cnf

View Windows VPN client certificate:

openssl x509 -noout -text -in vpnCA/certs/WindowsClientCert.pem | more

To revoke a certificate use -revoke flag. For example:

openssl ca -config openssl.cnf -revoke vpnCA/certs/WindowsClientCert.pem

You can verify all new-created certificates with following command:

openssl verify -CAfile vpnCA/CA_cert.pem -purpose any vpnCA/certs/*Cert.pem

Generate CRL's:

openssl ca -gencrl -out vpnCA/crls/crl.pem -config ./openssl.cnf

To view CRL use following command:

openssl crl -noout -text -in vpnCA/crls/crl.pem

Install and configure VPN server

In video I will install VPN server on host 172.16.50.63.OpenVPN will create a tun/tap interface, so CONFIG_TUN need to be enabled in the kernel.

cd /usr/src/linux
make menuconfig

Select:

Device Drivers -> Network Device Support
[*] Universal TUN/TAP device driver support

Recompile the kernel and reboot

Install OpenVPN:

emerge -av openvpn

Generate Diffie Hellman parameters, 2048 bit long, required for OpenVPN (this will take some time):

cd /etc/openvpn/
openssl dhparam -out dh2048.pem 2048

Create HMAC authentication key file used by OpenVPN to protect against DoS attacks:

openvpn --genkey --secret tls-auth.key

Copy CA, server certificates, server private key and CRL from CA server (172.16.50.4) via secured channel (over sftp):

sftp 172.16.50.4
sftp> get vpnCA/CA_cert.pem
sftp> !mkdir certs
sftp> get vpnCA/certs/ServerCert.pem certs/
sftp> !mkdir Pkeys
sftp> !chmod 400 Pkeys
sftp> get vpnCA/Pkeys/ServerKey.pem Pkeys/
sftp> !chmod 400 Pkeys/ServerKey.pem
sftp> !mkdir crls
sftp> get vpnCA/crls/crl.pem crls/

Configure OpenVPN >open openvpn.conf:

# Use UDP protocol for communicating with remote host
proto           udp
# In server mode, OpenVPN will listen on a single port
# for incoming client connections
mode            server

# Enable TLS and assume server role during TLS handshake
tls-server

# Use TAP virtual network device
dev             tap0

# UDP port number for server and clients
port            5060

# Use fast LZO compression
comp-lzo

# Ping clients over the UDP if no packets have been sent for
#  at least 20 sec
# Restart connection after 120 sec of inactivity
keepalive       20      120

# Set output verbosity to 3. Level 3 is recommended
verb            3

# UID of the OpenVPN process after initialization
user            nobody

# GID of the OpenVPN process after initialization
group           nobody

# Don't close and reopen TAP device on SIGUSR1 resets
persist-tun

# Solves the "re-read keys" problem by persisting keys across
# SIGUSR1 resets
persist-key

# Each client will "see" the other clients which are
# currently connected
client-to-client

# File containing Diffie Hellman parameters

dh              /etc/openvpn/dh2048.pem

# HMAC authentication key file
tls-auth        /etc/openvpn/tls-auth.key

# CA file
ca              /etc/openvpn/CA_cert.pem

# Server signed certificate
cert            /etc/openvpn/certs/ServerCert.pem

# Server private key
key             /etc/openvpn/Pkeys/ServerKey.pem

# Certificate revocation lists
crl-verify      /etc/openvpn/crls/crl.pem

# Set up an OpenVPN server which will allocate addresses

# to clients out of the network 10.9.8.0/24
server 10.9.8.0 255.255.255.0

Run OpenVPN with verbosity 5 to verify config file:

openvpn --config ./openvpn.conf --verb 5

If all it's OK, start OpenVPN and add it to runlevel default:

cd
/etc/init.d/openvpn start
rc-update add openvpn default

Allow routing of all client traffic through the VPN:

echo "1" > /proc/sys/net/ipv4/ip_forward

Open /etc/sysctl.conf and add:

net.ipv4.ip_forward = 1

Configure iptables:

iptables -t nat -A POSTROUTING -s 10.9.8.0/24 -o eth0 -j MASQUERADE
echo "iptables -t nat -A POSTROUTING -s 10.9.8.0/24 -o eth0 -j MASQUERADE">> /etc/conf.d/local.start

Connecting to VPN server from Linux host

In video Linux host will be 172.16.50.193. As in server case, we will need OpenVPN installed, so CONFIG_TUN also need to be enabled in the kernel.

cd /usr/src/linux
make menuconfig
Device Drivers -> Network Device Support
[*] Universal TUN/TAP device driver support

Recompile the kernel and reboot.

Install OpenVPN:

emerge -av openvpn

Copy CA, client certificates, client private key and CRL from CA server (172.16.50.4) via secured channel (over sftp):

cd /etc/openvpn/
sftp 172.16.50.4
sftp> get vpnCA/CA_cert.pem
sftp> !mkdir certs
sftp> get vpnCA/certs/LinuxClientCert.pem certs/
sftp> !mkdir Pkeys
sftp> !chmod 400 Pkeys
sftp> get vpnCA/Pkeys/LinuxClientKey.pem Pkeys/
sftp> !chmod 400 Pkeys/LinuxClientKey.pem
sftp> !mkdir crls
sftp> get vpnCA/crls/crl.pem crls/

Copy HMAC authentication key file from VPN server via secured channel(over sftp):

sftp 172.16.50.63
sftp> get /etc/openvpn/tls-auth.key

Configure OpenVPN for Linux client:

vi ./openvpn.conf
# Use UDP protocol for communicating with remote host
proto           udp

# Configuration of OpenVPN's client mode
client

# Client will try to connect to a server at 172.16.50.63:5060
remote  172.16.50.63 5060

# Enable TLS and assume client role during TLS handshake
tls-client

# Use TAP virtual network device
dev             tap0

# Most clients don't need to bind to a specific local
# port number
nobind

# Use fast LZO compression
comp-lzo

# Set output verbosity to 3. Level 3 is recommended
verb            3

# UID of the OpenVPN process after initialization
user            nobody

# GID of the OpenVPN process after initialization
group           nobody

# Don't close and reopen TAP device on SIGUSR1 resets
persist-tun

# Solves the "re-read keys" problem by persisting keys
# across SIGUSR1 resets
persist-key

# HMAC authentication key file
tls-auth        /etc/openvpn/tls-auth.key

# CA file
ca              /etc/openvpn/CA_cert.pem

# Linux client signed certificate
cert            /etc/openvpn/certs/LinuxClientCert.pem

# Linux client private key
key             /etc/openvpn/Pkeys/LinuxClientKey.pem

# Certificate revocation lists
crl-verify      /etc/openvpn/crls/crl.pem

# Automatically execute routing commands to cause

# all ougoing IP traffic to be redirected over the VPN
redirect-gateway def1

Run OpenVPN with verbosity 5 to verify config file:

openvpn --config ./openvpn.conf --verb 5

If all it's OK, start OpenVPN and add it to runlevel default:

cd
/etc/init.d/openvpn start
rc-update add openvpn default

Connecting to VPN server from Windows host

Download and install:OpenVPN Installer for Windows (http://openvpn.net/download.html)SFTP client to use it to copy private data (keys and certificates) from CA via secured channel. I recommend WinSCP (http://winscp.net/eng/download.php)

Using WinSCP copy CA, client certificate, client private key and CRL from CA server via secured channel(over sftp).Place all copied items in directory C:\Program Files\OpenVPN\config

Copy HMAC authentication key file from VPN server via secured channel(over sftp) place it in directory C:\Program Files\OpenVPN\config

Configure OpenVPN for Windows client. In the C:\Program Files\OpenVPN\config directory create a file named openvpn.ovpn with following contents:

# Use UDP protocol for communicating with remote host
proto		udp

# Configuration of OpenVPN's client mode
client

# Client will try to connect to a server at 172.16.50.63:5060
remote	172.16.50.63	5060

# Enable TLS and assume client role during TLS handshake
tls-client

# Use TAP virtual network device
dev		tap0

# Most clients don't need to bind to a specific local port number
nobind

# Use fast LZO compression
comp-lzo

# Set output verbosity to 3. Level 3 is recommended
verb		3

# Don't close and reopen TAP device on SIGUSR1 resets
persist-tun

# Solves the "re-read keys" problem by persisting keys across SIGUSR1 resets
persist-key

# HMAC authentication key file
tls-auth	"C:\\Program Files\\OpenVPN\\config\\tls-auth.key"

# CA file
ca		"C:\\Program Files\\OpenVPN\\config\\CA_cert.pem"

# Windows client signed certificate
cert		"C:\\Program Files\\OpenVPN\\config\\certs\\WindowsClientCert.pem"

# Windows client private key
key		"C:\\Program Files\\OpenVPN\\config\\Pkeys\\WindowsClientKey.pem"

# Certificate revocation lists
crl-verify	"C:\\Program Files\\OpenVPN\\config\\crls\\crl.pem"

# Automatically execute routing commands to cause
# all ougoing IP traffic to be redirected over the VPN
redirect-gateway def1

After that go to Start->Run and type:

cmd

This will open Windows Command Processor. Run openvpn with verbosity 5 to verify config file:

openvpn --config "C:\Program Files\OpenVPN\config\openvpn.ovpn" --verb 5

If all it's OK, configure OpenVPN to start as service. Go to Start->Run and type:

services.msc

This will open System Services window. In list find OpenVPN Service and double-click on it. Set startup type as Automatic and start OpenVPN as service by pressing the Start button.

Testing VPN Connectivity

You can test the VPN connectivity by sending a simple ping from one host to the other. In this case, the ping goes to the Linux client 10.9.8.2 from server 172.16.50.63 (vpn ip 10.9.8.1).So, on VPN server ping client 10.9.8.2:

ping 10.9.8.2

In another screen capture packets between hosts 10.9.8.1 and 10.9.8.2 via tcpdump on tap0 interface. This will show unencrypted traffic on OpenVPN's virtual TUN/TAP device between hosts 10.9.8.1 and 10.9.8.2:

tcpdump -i tap0 -lenx host 10.9.8.1 and 10.9.8.2

The output shows unencrypted icmp packets that traverces tap0 interface:

07:08:07.121670 00:ff:4b:d6:63:0d > 00:ff:2e:41:cf:71, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto ICMP (1), length 84) 10.9.8.1 > 10.9.8.2: ICMP echo request, id 1071, seq 1124, length 64
0x0000:  4500 0054 0000 4000 4001 1695 0a09 0801
0x0010:  0a09 0802 0800 5004 042f 0464 a743 f346
0x0020:  18db 0100 0809 0a0b 0c0d 0e0f 1011 1213
0x0030:  1415 1617 1819
07:08:07.229353 00:ff:2e:41:cf:71 > 00:ff:4b:d6:63:0d, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 40706, offset 0, flags [none], proto ICMP (1), length 84) 10.9.8.2 > 10.9.8.1: ICMP echo reply, id 1071, seq 1124, length 64
0x0000:  4500 0054 9f02 0000 4001 b792 0a09 0802
0x0010:  0a09 0801 0000 5804 042f 0464 a743 f346
0x0020:  18db 0100 0809 0a0b 0c0d 0e0f 1011 1213
0x0030:  1415 1617 1819

To capture packets that traverses Internet interface eth0 (on VPN server), having source/destination UDP port 5060 the following command should be issued:

tcpdump -i eth0 -lenx udp port 5060

The output shows that packets that goes to Internet are encrypted:

07:14:46.722871 00:13:8f:cd:d4:3e > 00:0c:29:1a:ed:70, ethertype IPv4 (0x0800), length 175: 172.16.50.193.32773 > 172.16.50.63.5060: SIP, length: 133
0x0000:  4500 00a1 0000 4000 4011 7d2b ac10 32c1
0x0010:  ac10 323f 8005 13c4 008d 5a71 3142 2aba
0x0020:  fafa 9a5d 0914 8359 5567 b273 b34b 85af
0x0030:  898e dc96 718f
07:14:47.328285 00:0c:29:1a:ed:70 > 00:13:8f:cd:d4:3e, ethertype IPv4 (0x0800), length 175: 172.16.50.63.5060 > 172.16.50.193.32773: SIP, length: 133
0x0000:  4500 00a1 0000 4000 4011 7d2b ac10 323f
0x0010:  ac10 32c1 13c4 8005 008d dddb 31b0 8400
0x0020:  ff8c 2e7a 8f1a 9756 2699 bb0a e6c4 703b
0x0030:  6603 0708 7919

Server IP on eth0 interface is 172.16.50.63, client IP is 172.16.50.193.