Running OpenVPN client in a FreeNAS Jail
Disclaimer: this post was created on 06-20-2019 for FreeNAS 11.2, it might not work on other platforms or versions.
Lately I have had less free time, which has resulted in the fact that this is the first article in over a year. Also I’ve been using mostly CentOS at my job and FreeBSD got out of the focus. But that doesn’t mean that I abandoned FreeBSD, just that solutions that require less time to set up and maintain got my attention.
So, I wanted to replace FreeBSD with FreeNAS on my home NAS as well as on my parents’. Both systems are replicating over any new data overnight so in case any of them breaks, we still have our data. I knew I wanted use the built-in solution in FreeNAS which only requires an SSH connection to the other NAS. I was also sure, that even though SSH is safe, I don’t want to replicate data over plain internet. This would also require a fix public IP address on both sides which is not easy with home commercial internet connections. A workaround would be to use dynamic DNS like duckdns or dyndns, but it still requires setting up NAT on the router not to mention exposing your FreeNAS to the public internet (in my experience, most ISP-provided routers do not have a firewall or it’s so dumb that it cannot filter based on source addresses).
Long story short, I needed VPN. I have a fix IP ar my home, which makes setup more easy, but this can be also achieved with a cheap VPS running somewhere, acting as a relay.
As I mentioned, you need at least one fix IP address somewhere which will be used by the OpenVPN Server. It can be anything, as OpenVPN can run on different platforms. This guide assumes that an OpenVPN Server is already set up.
The OpenVPN Server
This guide won’t go into details of setting up the OpenVPN Server, but I’ll mention the configuration options that are needed in order to make this connection work.
The easiest and safest authentication method for OpenVPN is to use certificates. If a client is trying to connect, the server will check the clients certificate against its CA. If it’s a match, the client is authenticated. The client can/will also check the server’s certificate in order to make sure that it connects to a valid server. There are a lot of guides out there that explain how to set up a PKI (Private Key Infrastructure) with easyrsa (which is installed along with OpenVPN) or other tools.
The routes of the client FreeNAS’s network need to be configured in the OpenVPN server. You can use this tool to find out the network and subnet mask: http://www.subnet-calculator.com/ Please note, that the location of the openvpn configuration file could be different. Here I’m using Debian. Once you have it, add it to the server’s configuration:
route 192.168.0.0 255.255.255.0
Besides this, the server needs to know over which client this subnet is reachable. This is achieved with client specific configurations in the Client Config Directory (CCD). If there is none yet, define the CCD location as follows:
Inside this directory, there should be a file corresponding to the client’s name (defined in its certificate). The content of the file is:
# cat /etc/openvpn/ccd/client iroute 192.168.0.0 255.255.255.0
You may notice that this is the same subnet defined in the OpenVPN Server configuration file. In the main configuration file, we define that a subnet will be handled on the system by OpenVPN. But because the client will not communicate to the server that this specific subnet is reachable through it, we need to tell the OpenVPN server. This way, once the connection is estbalished, a new route will be added to the operating system’s routing table pointing to the correct client.
The client also needs to be informed about the server’s subnet. Add the subnet you want to push to the client:
push "route 10.0.1.0 255.255.255.0"
Once the client is connected, it will be informed about this route.
Creating a Jail for OpenVPN is mostly straightforward, but there are a few gotchas. Create a new Jail by navigating to Jails > Add and continue with Advanced Jail Creation. Assign a name and IP address to the Jail. It is important to check the box to VNET! The Jail has to use VNET in order to make this work. Open Custom Properties and check allow_tun. Besides this, as we will route traffic through the OpenVPN Jail, we need to tell FreeNAS how that server is reachable. Open Network > Static Routes and then Add. Enter the Destination (which is the network we would like to reach this FreeNAS from, the gateway (which is the IP address of the newly created Jail) and a description.
OpenVPN Setup in the Jail
Once the Jail is running, enter it through the CLI (either by SSH into the NAS or using the Shell on the FreeNAS GUI and then iocage console jailname). Install openvpn
pkg install openvpn
(or install through ports if you prefer that way). After installation, either copy an OpenVPN configuration file that is already in use or set up a new one. Copy a template:
cp /usr/local/share/examples/openvpn/sample-config-files/client.conf /usr/local/etc/openvpn/
Modify the configuration file as needed (most importantly the remote part and the certificates). You can use the following as a template:
client #We are a client dev tun #Using TUN instead of TAP proto udp #Using UDP remote your.doma.in 1194 #This can be an IP address and a different port as well resolv-retry infinite #If we are connecting to a domain name, try to resolve it infinitely nobind #No need to bind persist-key #Reserve states between restarts persist-tun user nobody #Drop privilege group nobody ca /path/to/ca.crt #Path to the Certificate Authority cert /path/to/client.crt #Path to the certificate used by this client, signed by the CA key /path/to/client.key #Path to the key belonging to the certificate remote-cert-tls server #Check the server certificate if the certificates were set up for this
Please note, that this is just a template, you need to modify it to reflect your actual setup. After all files are in place including the configuration and the certificates, you can try to connect to the server.
# openvpn path/to/the/configuration.conf
If the connection is unsuccessful, you will see an explanation in the output.
If the client is successfully connected, check if the routing is done correctly. On the client, the subnet of the server should be there:
root@client:~ # netstat -r4 Routing tables Internet: Destination Gateway Flags Netif Expire default 192.168.0.1 UGS epair0b 10.8.0.0/24 10.8.0.1 UGS tun0 10.8.0.1 link#3 UH tun0 10.8.0.2 link#3 UHS lo0 localhost link#1 UH lo0 10.0.1.0/24 10.8.0.1 UGS tun0
The route 10.0.1.0/24 was pushed from the server. On the server, check if the client’s subnet is present.
root@server:~ # netstat -r4 Kernel IP routing table Destination Gateway Genmask Flags MSS Window irtt Iface default 10.0.1.1 0.0.0.0 UG 0 0 0 eth0 10.0.100.0 0.0.0.0 255.255.255.240 U 0 0 0 eth0 10.8.0.0 0.0.0.0 255.255.255.0 U 0 0 0 tun0 192.168.0.0 10.8.0.2 255.255.255.0 UG 0 0 0 tun0
If both are correct, we should be able to ping the client from the server over the VPN tunnel.
root@server:~# ping -S 10.0.1.2 192.168.0.2 PING 192.168.0.9 (192.168.0.9) 56(84) bytes of data. 64 bytes from 192.168.0.9: icmp_seq=1 ttl=64 time=43.7 ms 64 bytes from 192.168.0.9: icmp_seq=2 ttl=64 time=41.3 ms
-S defines the source IP address, in this case this is the local interface of the server.
If the route was successfully added on the FreeNAS, ping should be also successful to its local IP address.
If you need to reach the FreeNAS from a machine other then the VPN server, you either need to add a static route on the other machine or the same on your default gateway.
This is a fairly easy setup and makes sure that the traffic between you and your FreeNAS installation is secure over an OpenVPN tunnel. It is also possible to use a cheap VPS on DigitalOcean, Vultr or any other provider if you want to reach FreeNAS from a location that does not have a fix IP or is behind CGNAT (Carrier Grade NAT).