7 June 2016

OpenVPN in FreeNAS jail

Goal

Since I've thrown Windows out of the window and got FreeBSD running on my computer, I don't have any possibility of remotely accessing my FreeNAS server. For this reason, I made myself install and configure OpenVPN in a FreeNAS jail to allow myself to access my home network.

This post is written with the intention of setting up an OpenVPN instance, called opepnvpn_priv. If you would want to have a second instance, for example if you want to tighten up the allowed traffic for a specific set of users, you will want to set up a second openvpn instance, which in this post will be named openvpn_pub. At the end of this post, the additional steps will be written down and explained to have the second instance running, I'll try to not make this to confusing for you guys who only want 1 instance running.

This post is written based on the information which are found on the following 2 links:
OpenVPN howto: https://openvpn.net/howto.html
Guide on FreeNAS forums, written by the user 'Robles': https://forums.freenas.org/index.php?threads/how-to-install-openvpn-inside-a-jail-in-freenas-9-2-1-6-with-access-to-remote-hosts-via-nat.22873/

Approach

So after reading the FreeNAS forums, somebody already did the effort to write a guide on how to setup OpenVPN in a FreeNAS jail, though it isn't up to date anymore. (at some point in the future this article won't be either probably). After some reading and testing, it also seems possible to allow the connecting clients to use the public IP address of the location of the OpenVPN server. Because I'll allow friends to use the VPN server as well so that they can reach my FreeNAS box, I'll be creating 2 instances of OpenVPN. 1 instance will be used by myself, in this guide it will be named openvpn_priv. The other instance, which can be used by friends (and in essence, less trusted) will be connecting to the other instance, which will be named openvpn_pub. The access on the network will be filtered by ipfw on the OpenVPN server.

Network information

propertyvalue
JailnameOpenVPN-ACC
IPv4 address192.168.1.180

LAN subnet: 192.168.1.0/24
LAN gateway + DNS : 192.168.1.1

Subnet for openvpn_priv instance clients: 10.66.66.0/24
Subnet for openvpn_pub instance clients: 10.77.77.0/24

Useful information before getting started

In the OpenVPN howto are quite some important things to read before getting started. They give you an better understanding of how the certificates work, and also some recommendations regarding the subnets to be used are given.

Numbering private subnets

It is also recommended to use an uncommon subnet for the VPN clients so that it will be unlikely that there will be routing conflicts during the VPN session of a client. An example subnet could be: 10.66.66.0/24

Setting up your own Certificate Authority (CA) generating certificates and keys for an OpenVPN server and multiple clients

It is necessary to build your own PKI infrastructure. The PKI consists of:
  • 1 public key and 1 private key for each server.
  • 1 public key and 1 private key for each client that will be connecting.
  • A master Certificate Authority (CA) public and private key which will be used to sign each public keys of the server(s) and client(s).

OpenVPN supports bidirectional authentication based on certificates. Before mutual trust is established, the following actions must have been completed:
  • The client must verify the server certificate.
  • The server must verify the client certificate.

Generating and managing the PKI is done using the easy-rsa command.

Installation

Open a root shell inside the jail and issue the following commands:

pkg update
pkg upgrade
pkg install openvpn


After the installation of openvpn is done, the following file location is shown: /usr/local/etc/rc.d/openvpn. This file is the rc script and contains the instructions to be run during the initialization of the openvpn instance.

Setup PKI infrastructure & generating the CA and certificates

In order to set up the PKI and generate the CA and server and client certificates, it is advised to review the initial settings in the /usr/local/share/easy-rsa/vars. You might want to change the duration of the validity of the generated CA and server/client certificates, which will be 10 years by default. In case you would want to modify this, you would want to do that in this file as shown below:

ee /usr/local/share/easy-rsa/vars


The above configuration will set the generated CA and certificate to be valid for 3 years.

Once you feel comfortable about the settings in your vars file, go on and initialise the PKI:
easyrsa init-pki.

Generate CA

Once the PKI is initialized, the CA can be generated.
easyrsa build-ca

When it asks for the PEM pass to be entered, choose a very strong password. This password is very crucial for your PKI security and should be kept very secret! this password will prevent malicious people to generate a server or client certificate in case your CA would be stolen.


The generated CA will be saved to /usr/local/share/easy-rsa/pki/ca.crt

Generate server certificate and key

It is advised to build the server certificate and key without a password. This will allow the instance to be started unattended. Execute the following command to do so:
easyrsa build-server-full OpenVPN-ACC nopass

When the command asks for the PEM pass phrase, enter the password which you choose for the CA.


The following 2 files will be created:
  • Server certificate (public key): /usr/local/share/easy-rsa/pki/issued/OpenVPN-ACC
  • Server private key: /usr/local/share/easy-rsa/pki/private/OpenVPN-ACC

Generate client certificate and key

Below will be shown how to generate the key for a client. The command should be executed for every single client that should have access to the VPN service. This step should thus be repeated for as many clients that will be connecting to your server. It is advised to generate this certificate and private key with a password, so that the end user must use a password before the certificate can be used to authenticate against the server.
easyrsa build-client-full gertwintop
When the command asks for the PEM pass phrase, enter the password which you choose for the CA.

Generate Diffie-Hellman

openssl dhparam -out /usr/local/share/easy-rsa/pki/dh2048.pem 2048

Generate HMAC

This part is optional, but is strongly advised to do so to help block DoS attacks and UDP port flooding attacks. In case you will be having multiple instances running, this key will help you protect unwanted users from connecting as this file is also used before setting up the connection.

openvpn --genkey --secret /usr/local/share/easy-rsa/pki/ta_priv.key

Create openvpn_priv rc script

Inside the /usr/local/etc/rc.d directory, there is already an openvpn rc script present. In order to make it possible for openvpn to have another instance running (openvpn_priv), execute the following set of commands:
cd /usr/local/etc/rc.d
ln -s openvpn openvpn_priv
When the openvpn_priv instance is started, it will automatically look for the corresponding configfile named 'openvpn_priv.conf' in the /usr/local/etc/openvpn/ directory.

Create config script for server

So in this part, the configuration script for the OpenVPN server will be created and edited.
As 2 instances of OpenVPN will be run (1 will be named openvpn_priv, the other one wille be named openvpn_pub). Each instance of OpenVPN will use it's own pool of IP addresses to assign to connect clients. This will allow us to specify rules in the firewall so allow or deny access for a subnet. The openvpn_priv will have full access to the local LAN, whereas the openvpn_pub will only be allowed to access the FreeNAS shares (no HTTP or SSH access).

In order to get started, create the /usr/local/etc/openvpn directory and copy the example server config script to that folder.
mkdir -p /usr/local/etc/openvpn
cp /usr/local/share/examples/openvpn/sample-config-files/server.conf /usr/local/etc/openvpn/openvpn_priv.conf

Edit config openvpn_priv.conf

Edit the /usr/local/etc/openvpn/openvpn_priv.conf file so that it will suit your environment:
ee /usr/local/etc/openvpn/openvpn_priv.conf

The following fields should be set uncommented and configured conform your environment:

Setting nameinitial value (in sample config)example valuesome extra information regarding this setting
locala.b.c.d192.168.1.180This should be the local LAN ip of the jail in which you installed OpenVPN.
port11941195This should be the port you want the openvpn_priv instance to be listening on.
protoudpudpIn the OpenVPN HOWTO
devtuntun
caca.crt/usr/local/share/easy-rsa/pki/ca.crtThis setting should contain the path of the CA created earlier.
certserver.crt/usr/local/share/easy-rsa/pki/issued/OpenVPN-ACC.crtThis setting should contain the path of the server certificate created earlier
keyserver.key/usr/local/share/easy-rsa/pki/private/OpenVPN-ACC.keyThis setting should contain the path of the server key created earlier
dhdh2048.pem/usr/local/share/easy-rsa/pki/dh2048.pemDiffie Hellman parameters which are used during the exchange of keys between server and client
topologysubnetsubnet
server10.8.0.0 255.255.255.010.66.66.0 255.255.255.0This will be the subnet each client connected to the openvpn_priv instance will get an IP address from.
ifconfig-pool-persistipp.txtipp_priv.txtThis file will maintain the associations of assigned IP addresses and clients.
push"route 192.168.1.10 255.255.255.0""route 192.168.1.0 255.255.255.0"This will advertise the route 192.168.1.0/24 to the connected clients
route192.168.40.128 255.255.255.248192.168.1.180 255.255.255.0 10.66.66.1This will route the traffic from the local LAN back into the VPN subnet
client-to-clientIf this setting is uncommented, clients can see eachother, and not only the server.
tls-authta.key 0/usr/local/share/easy-rsa/pki/ta_priv.key 0This should only be set if the step Generate HMAC was executed
usernodobyThis should be uncommented if the OpenVPN server is set up on a FreeBSD based system so that the daemon privileges are reduced after initialization
groupnobodyThis should be uncommented if the OpenVPN server is set up on a FreeBSD based system so that the daemon privileges are reduced after initialization
statusopenvpn-status.logopenvpn_priv-status.log
log-appendopenvpn.log/var/log/openvpn_priv.log

After all modifications are done, save the file.

Firewall settings

In this part the firewall configuration will be shown. The firewall will contain the set of rules to allow or deny IP packets from the VPN to the local LAN. This can be done based on source IP, destination IP, portnumbers,...

Create the file which will contain the ipfw configuration:
ee /usr/local/etc/ipfw.rules

Please note that copy pasting the text might give some errors, in case you would get errors regarding the ipfw settings you might want to write the following manually.

#!/bin/sh

# General config
EPAIR=$(/sbin/ifconfig -l | tr " " "\n" | /usr/bin/grep epair)
ipfw -q -f flush
ipfw -q nat 1 config if ${EPAIR}

#Config for openvpn_priv instance
ipfw -q add nat 1 all from 10.66.66.0/24 to any out via ${EPAIR}
ipfw -q add nat 1 all from any to any in via ${EPAIR}

# Ensure the tun interface is named "tun0" after each jail restart
TUN=$(/sbin/ifconfig -l | tr " " "\n" | /usr/bin/grep tun)
ifconfig ${TUN} name tun0

/etc/rc.conf configuration

In order to have the openvpn_priv instance start automatically at each restart of the jail (and also FreeNAS box), add the following lines into /etc/rc.conf
openvpn_priv_enable="YES"
openvpn_priv_if="tun"

Also the following lines must be added so that the firewall initialization is done at each restart of the jail:
cloned_interfaces="tun"
gateway_enable="YES"
firewall_enable="YES"
firewall_script="/usr/local/etc/ipfw.rules"

Now restart the jail. This can be done either via the WebUI in FreeNAS, or by opening an ssh session to your FreeNAS box and using the warden command. use the command warden list to see the list of jails on the server.

In my case the following command would stop the OpenVPN-ACC server and start it again:
warden stop OpenVPN-ACC && warden start OpenVPN-ACC

Verifying the configuration

ipfw settings

Verify the ipfw settings by executing the following command:
ipfw list

This should give the following output:

If this doesn't look the same on your end, it is most likely that there is some type in the /usr/local/etc/ipfw.rules file, or the /etc/rc.conf file regarding the settings relevant with ipfw. Review it and execute service ipfw restart && ipfw list to redo the verification.

openvpn_priv settings

Verify the openend sockets inside the jail. This will show us what application is listening on what protocol (UDP or TCP), on what local IP address, and on what portnumber. Depending on the portnumber and protocol defined in /usr/local/etc/openvpn/openvpn_priv.conf the output should look like below:

If the settings don't seem OK, or you would want to use another port for the openvpn_priv instance, edit /usr/local/etc/openvpn/openvpn_priv.conf file, save it and restart the openvpn_priv instance with service openvpn_priv restart

Router portforwarding

The last step necessary to have the openvpn_priv instance being reachable via the internet, is to do portforwarding on the router/firewall on the local LAN end where the server is located.
This setting is very specific on router/firewall you are using, but in this case, keeping the IPs in mind i've been using throughout this post, you would do the following:
Allow port external port 1195, and route the packets to 192.168.1.180 to port 1195.

Client configuration

Software installation

On the client device, you'll need to install the OpenVPN client software.
For windows, the install can be downloaded from the following URL: https://openvpn.net/index.php/download/community-downloads.html
For android, you should find the app easily in the play store.

I'll be explaining the steps on how to get the VPN client working on your Windows machine. The installation itself will not be covered.

So after the installation is completed, the installation folder of OpenVPN can be found on one of the following locations, depending on your Windows bit architecture and the installer. (32-bit vs 64-bit):
C:\Program Files\OpenVPN\
C:\Program Files (x86)\OpenVPN\

Now, it is necessary to copy the generated certificates for this specific client into the config directory inside the OpenVPN installation folder. This should be done via a secure channel, like USB stick, or over the local LAN. It's advised not to send the certificate and keys over an unsecure channel, like mail, skype, or other untrusted connections/platforms.

In this case, we generated a client certificate and key for our client 'gertwintop'. Also the ca.crt and the ta_priv.key should be copied to the client device as well.

The necessary files are still located on the OpenVPN server on the following locations:
  • ca.crt /usr/local/share/easy-rsa/pki/ca.crt
  • gertwintop.crt /usr/local/share/easy-rsa/pki/issued/gertwintop.crt
  • gertwintop.key /usr/local/share/easy-rsa/pki/private/gertwintop.key
  • ta.key /usr/local/share/easy-rsa/pki/ta_priv.key

Client configuration file

The client configuration file should also be located in the C:\Program Files\OpenVPN ( or C:\Program Files (x86)\OpenVPN in case you installed the 32 bit client). Copy the following text in a new textfile and save the file as openvpn_priv.ovpn:

# Specify that we are a client and that we
# will be pulling certain config file directives client

# Use the same settings as used on the server
dev tun
proto udp

# The hostname/IP and port of the server
# replace DNSNAME with your public IP or DNSname
remote DNSNAME 1195

# redirect local traffic to the remote OpenVPN server Also set
# DNS server if redirect-gateway option is set.
# redirect-gateway def1
# dhcp-option DNS 8.8.8.8

# Keep trying indefinitely to resolve the hostname
# of the OpenVPN server
resolv-retry infinite

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

# Try to preserve some state across restarts.
persist-key
persist-tun

# SSL/TLS parms.
ca ca.crt
cert gertwintop.crt
key gertwintop.key

# tls-auth should only be set if Generate HMAC was done
# otherwise comment the tls-auth setting
tls-auth ta_priv.key 1

# Prevent MitM attacks, let client verify the server certificate
remote-cert-tls server

#Enable compression on the VPN link.
comp-lzo

# Set log file verbosity.
verb 3

With the above client configuration, the VPN session allows the client to access the local LAN of the VPN server, the public IP of the client would still be the one it would have without having an active VPN session.

Though, Note the 2 following lines:
# redirect-gateway def1
# dhcp-option DNS 8.8.8.8

If those were uncommented, all internet traffic would be passed via the WAN connection of the VPN server. This will result in a slower performance, but it might give you other opportunities, like bypassing regional limitations.

At this point, you should be able to succesfully connect to your OpenVPN instance. In case you would want a second OpenVPN instance, continu to follow this post!

Setting up a second OpenVPN instance

Generate HMAC

This part is highly recommended in case a second instance is to be configured as this will help you protect users which should be using the openvpn_pub instance to connect to the openvpn_priv instance and vice versa. It should also help to protect from Dos attacks and UDP port flooding attacks.

openvpn --genkey --secret /usr/local/share/easy-rsa/pki/ta_pub.key

create openvpn_pub rc script

Inside the /usr/local/etc/rc.d directory, there is already an openvpn rc script present. In order to make it possible for openvpn to have another instance running (openvpn_pub- execute the following set of commands:
cd /usr/local/etc/rc.d
ln -s openvpn openvpn_pub

When the openvpn_pub instance is started, it will automatically look for the config named 'openvpn_pub.conf' in the /usr/local/etc/openvpn/ directory.

Create config script for server

The easiest way to create a config script for the openvpn_pub instance is to copy the already existing config script of the openvpn_priv instance:
cd /usr/local/etc/openvpn
cp openvpn_priv.conf openvpn_pub.conf

Edit openvpn_pub.conf

Now modify the openvpn_pub.conf file:
ee /usr/local/etc/openvpn/openvpn_pub.conf
Only the following parameters should be modified;

Setting nameinitial value (in openvpn_priv.conf)value to be set in openvpn_pub.confsome extra information regarding this setting
port11951196This should be the port you want the openvpn_pub instance to be listening on. Each instance must have its own unique port number.
server10.66.66.0 255.255.255.010.77.77.0 255.255.255.0This will be the subnet eachclient connected to the openvpn_pub instance will get an IP address from. This should be different then the subnet specified in the config file of the first instance.
ifconfig-pool-persistipp_priv.txtipp_pub.txtThis file will maintain the associations of assigned IP addresses and clients.
route192.168.1.180 255.255.255.0 10.66.66.1192.168.1.180 255.255.255.0 10.77.77.1This will route the traffic from the local LAN back into the VPN subnet
client-to-clientIf this setting is uncommented, clients can see eachother, and not only the server.
tls-auth/usr/local/share/easy-rsa/pki/ta_priv.key 0/usr/local/share/easy-rsa/pki/ta_pub.key 0This should only be set if the step Generate HMAC was executed
statusopenvpn_priv-status.logopenvpn_pub-status.log
log-appendopenvpn_priv.log/var/log/openvpn_pub.log

Save the file after all modifications are done.

Firewall settings

Because we specified a new subnet in the openvpn_pub configuration, the firewall must also be configured so that it will allow specific traffic from the 10.77.77.0/24 subnet to the server. As this instance will be used by friends, and I don't want them to be using internet via my public IP address, I also added some configuration to prevent them from doing that.
ee /usr/local/etc/ipfw.rules

Please note that copy pasting the text might give some errors, in case you would get errors regarding the ipfw settings you might want to write the following manually.

Add the following text so your /usr/local/etc/ipfw.rules file looks like this:

#!/bin/sh

# General config
EPAIR=$(/sbin/ifconfig -l | tr " " "\n" | /usr/bin/grep epair)
ipfw -q -f flush
ipfw -q nat 1 config if ${EPAIR}

#Config for openvpn_priv instance
ipfw -q add nat 1 all from 10.66.66.0/24 to any out via ${EPAIR}
ipfw -q add nat 1 all from any to any in via ${EPAIR}

#Config for openvpn_pub instance
#Prevent access to the FreeNAS webUI
ipfw -q add deny tcp from 10.77.77.0/24 to 192.168.1.10 80
#Prevent SSH access to the FreeNAS server
ipfw -q add deny tcp from 10.77.77.0/24 to 192.168.1.10 22
ipfw -q add nat 1 all from 10.77.77.0/24 to 192.168.1.10 out via ${EPAIR}
ipfw -q add nat 1 all from 192.168.1.10 to 10.77.77.0/24 in via ${EPAIR}

# Ensure the tun interface is named "tun0" after each jail restart
TUN=$(/sbin/ifconfig -l | tr " " "\n" | /usr/bin/grep tun)
ifconfig ${TUN} name tun0

/etc/rc.conf configuration

In order to have the openvpn_pub instance start automatically at each restart of the jail (and also FreeNAS box), add the following lines into /etc/rc.conf
openvpn_pub_enable="YES"
openvpn_pub_if="tun"

Now issue the following commands to start the openvpn_pub instance and to restart the ipfw service so that the settings which are configured in /etc/rc.conf are applied.

service openvpn_pub start
service ipfw restart

Verifying the configuration

Ipfw settings

Verify the ipfw settings by executing the following command ipfw list.
This should give the following output:
If this doesn't look the same on your end, it is most likely that there is some type in the /usr/local/etc/ipfw.rules file, or the /etc/rc.conf file regarding the settings relevant with ipfw. Review it and execute service ipfw restart && ipfw list to redo the verification.

Openvpn_pub settings

Verify the opened sockets inside the jail. This will show us what application is listening on what protocol (UDP or TCP), on what local IP address, and on what portnumber. Depending on the portnumber and protocol specified in /usr/local/etc/openvpn/openvpn_pub.conf the output should look like below:

If the settings don't seem ok, or you would want to use another port for the openvpn_pub instance, edit /usr/local/etc/openvpn/openvpn_pub.conf file, save it and restart the openvpn_pub instance with service openvpn_pub restart

Router portforwarding

Because of the new openvpn instance, which will be listening on port 1196, it should be made available from the internet. Go back into your router settings and forward port 1196 to the internal IP of the jail (192.168.1.180).

Copy the necessary certificates to client

It is assumed that at this point, the OpenVPN client software is already installed on the client device.

Now, it is necessary to copy the generated certificates for this specific client into the config directory inside the OpenVPN installation folder. This should be done via a secure channel, like USB stick, or over the local LAN. It's advised not to send the certificate and keys over an unsecure channel, like mail, skype, or other untrusted connections/platforms.

In this case, we generated a client certificate and key for our client 'gertwintop'. Also the ca.crt and the ta_pub.key should be copied to the client device as well.

The necessary files are still located on the OpenVPN server on the following locations:
  • ca.crt /usr/local/share/easy-rsa/pki/ca.crt
  • gertwintop.crt /usr/local/share/easy-rsa/pki/issued/gertwintop.crt
  • gertwintop.key /usr/local/share/easy-rsa/pki/private/gertwintop.key
  • ta.key /usr/local/share/easy-rsa/pki/ta_pub.key

Client configuration file

The client configuration file should also be located in the C:\Program Files\OpenVPN ( or C:\Program Files (x86)\OpenVPN in case you installed the 32 bit client). Copy the following text in a new textfile and save the file as openvpn_pub.ovpn:

# Specify that we are a client and that we
# will be pulling certain config file directives client

# Use the same settings as used on the server
dev tun
proto udp

# The hostname/IP and port of the server
# replace DNSNAME with your public IP or DNSname
remote DNSNAME 1196

# Keep trying indefinitely to resolve the hostname
# of the OpenVPN server
resolv-retry infinite

# Most clients don't need to bind to a specific local port number
nobind
# Try to preserve some state across restarts.
persist-key
persist-tun

# SSL/TLS parms.
ca ca.crt
cert gertwintop.crt
key gertwintop.key

# tls-auth should only be set if Generate HMAC was done
# otherwise comment the tls-auth setting
tls-auth ta_pub.key 1

# Prevent MitM attacks, let client verify the server certificate
remote-cert-tls server

#Enable compression on the VPN link.
comp-lzo

# Set log file verbosity.
verb 3

With the above client configuration, the VPN session allows the client to access the local LAN of the VPN server, the public IP of the client would still be the one it would have without having an active VPN session.

This should be enough for clients to connect to the openvpn_pub instance.