These notes are with respect to FreeNAS 11 and OpenVPN 2.4. This post was a great reference for most of this: https://www.ixsystems.com/community/threads/step-by-step-to-install-openvpn-inside-a-jail-in-freenas-11-1-u1.61681/. Much of the below is based on the post, captured below to have that and the Google Authenticator configuration in one place.
Let’s begin by adding a jail. Within Jails, click Add Jail. Here are the settings for a currently working config: DHCP Autoconfigure IPv4 unchecked, VNET checked, Berkeley Packet Filter checked, IPv4 statically defined, Autoconfigure IPv6 unchecked, Auto-start checked, mount_devfs checked, mount_fedscfs checked, allow_set_hostname checked, allow_raw_sockets checked, all other jail allow_* properties unchecked, Network interface vnet0:bridge0, vnet_default_interface auto, within Custom Properties allow_tun checked. The allow_tun in particular is important for OpenVPN.
To install OpenVPN, we want to get to a shell on the FreeNAS system (ssh works too). # determine jail ID for the newly created jail
$ jls
# shell into the jail
$ jexec /bin/sh
# install dependencies
$ pkg install libqrencode openvpn libpam-google-authenticator
Before configuring OpenVPN, we will setup the certificate authority (CA) and certificates. $ mkdir /usr/local/etc/openvpn /usr/local/etc/openvpn/keys
$ cp /usr/local/share/examples/openvpn/sample-config-files/server.conf /usr/local/etc/openvpn/openvpn.conf
$ cp -r /usr/local/share/easy-rsa /usr/local/etc/openvpn/easy-rsa
$ cd /usr/local/etc/openvpn/easy-rsa
Edit /usr/local/etc/openvpn/easy-rsa/vars
set_var EASYRSA_REQ_COUNTRY
set_var EASYRSA_REQ_PROVINCE
set_var EASYRSA_REQ_CITY
set_var EASYRSA_REQ_ORG
set_var EASYRSA_REQ_EMAIL
set_var EASYRSA_REQ_OU
set_var EASYRSA_KEY_SIZE
$ ./easyrsa.real init-pki
$ ./easyrsa.real build-ca
$ ./easyrsa.real build-server-full openvpn-server nopass
$ ./easyrsa.real gen-dh
$ openvpn --genkey --secret ta.key
$ cp pki/dh.pem pki/ca.crt pki/issued/openvpn-server.crt pki/private/openvpn-server.key /usr/local/etc/openvpn/keys/
$ cp ta.key /usr/local/etc/openvpn/keys/
# generate client cert for each username
$ ./easyrsa.real build-client-full username
$ cp pki/issued/username.crt pki/private/username.key /usr/local/etc/openvpn/keys/
Configure PAM for Google Authenticator. Edit /etc/pam.d/openvpn
auth required /usr/local/lib/pam_google_authenticator.so secret=/home/${USER}/.google_authenticator
We need to add and configure user accounts for Google Authenticator. $ adduser -s /usr/sbin/nologin username
$ chsh -s /bin/sh username
$ su username
$ google-authenticator -t -l "OpenVPN home:$USER" -Q utf8 -r3 -R30 -w5 -f -d
$ chsh -s /usr/sbin/nologin username
Configure OpenVPN with Google Authenticator plugin. Note, we are also configuring it to listen on TCP 443 with port-share to 4443, where we will run a web server. The TCP 443 and port share are necessary to enable connections from remote networks where they may only be allowing TCP 443 outbound and doing packet inspection.
Edit /usr/local/etc/openvpn/openvpn.conf
local the.listen.ip.address
port 443
proto tcp
port-share 127.0.0.1 4443
dev tun
ca /usr/local/etc/openvpn/keys/ca.crt
cert /usr/local/etc/openvpn/keys/openvpn-server.crt
key /usr/local/etc/openvpn/keys/openvpn-server.key
dh /usr/local/etc/openvpn/keys/dh.pem
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist ipp.txt
push "route 192.168.1.0 255.255.255.0"
keepalive 10 120
tls-auth /usr/local/etc/openvpn/keys/ta.key
remote-cert-tls client
cipher AES-256-CBC
user nobody
group nobody
persist-key
persist tun
status openvpn-status.log
verb 3
plugin /usr/local/lib/openvpn/plugins/openvpn-plugin-auth-pam.so openvpn
Edit NAT config in /usr/local/etc/ipfw.rules
#!/bin/sh
EPAIR=$(/sbin/ifconfig -l | tr " " "\n" | /usr/bin/grep epair)
ipfw -q -f flush
ipfw -q nat 1 config if ${EPAIR}
ipfw -q add nat 1 all from 10.8.0.0/24 to any out via ${EPAIR}
ipfw -q add nat 1 all from any to any in via ${EPAIR}
TUN=$(/sbin/ifconfig -l | tr " " "\n" | /usr/bin/grep tun)
ifconfig ${TUN} name tun0
Enable in /etc/rc.conf
openvpn_enable="YES"
openvpn_if="tun"
openvpn_configfile="/usr/local/etc/openvpn/openvpn.conf"
openvpn_dir="/usr/local/etc/openvpn/"
cloned_interfaces="tun"
gateway_enable="YES"
firewall_enable="YES"
firewall_script="/usr/local/etc/ipfw.rules"
I will not go into details here (there is plenty of info available online if you Google it), though in the same jail we want to setup a web server (Apache 2.4 is what I used) listening on TCP 4443 for SSL/HTTPS traffic. This is what the OpenVPN port share will send non-openvpn clients to. You can get a free certificate from Let’s Encrypt and automate it’s renewal. Once configured, add it to startup in /etc/rc.conf
apache24_enable="YES"
Configure OpenVPN client $ cp /usr/local/share/examples/openvpn/sample-config-files/client.conf /usr/local/etc/openvpn/username.conf
Edit /usr/local/etc/openvpn/username.conf
client
dev tun
proto tcp
remote your.fully.qualified.domain 443
resolv-retry infinite
nobind
persist-key
persist-tun
ca ca.crt
cert username.crt
key username.key
remote-cert-tls server
tls-auth ta.key 1
cipher AES-256-CBC
# Send all client traffic through the VPN
redirect-gateway autolocal
# Google Authenticator
ns-cert-type server
auth-user-pass
Restart the jail and give it a test. You should be able to request the 443 destination in a web browser and see the web site listening on TCP 4443’s content. This confirms connectivity to OpenVPN and port share is working. When testing the OpenVPN client, it will ask for the certificate password and then the password, where the Google Authenticator code value is entered as the password.