VPN Solutions on FreeBSD and OpenBSD — OpenVPN approach

Virtual Private Network (VPN) is a required solution in our current interconnected world to protect our own privacy and to interconnect different network through internet. Lot of commercial solutions exists around the world to deploy any kind of VPN, but, can we trust big corportation? In this series, I will show you how to deploy rapidly multiple VPN software on my two favorites operating systems: OpenBSD and FreeBSD.

Before starting, for newbies and new comers in marvelous VPN world, what is a VPN? A VPN is a link between one or many nodes on an pre-existing network. This link (you can call it a tunnel) between each nodes is authenticated and encrypted. This tunnel create a new network where all your communications are “secure” (the flow can’t be read by anyone except the two end-point).

In this article, I will present to you OpenVPN, probably one of the most known and used VPN around the world. This tool comes from Open-Source, is easy to use, easy to configure, easy to deploy and easy to hide too. I will show you how to configure it and create your own personal VPN network in different level of complexity. The first level, in this article, is straight forward, a simple VPN authenticated only with certificate. Here a map to show you an example of the final distributed OpenVPN project.

External client connected with OpenVPN, servers interconnected with tinc

Server Configuration on OpenBSD

Before starting and doing anything, our server need some custom configuration. Firstly, this server will now act as router, need to route network packets from different interfaces and different networks, in ipv4 but also in ipv6 (in a future article). net.inet.ip.forwarding kernel state is really important. If this one is not set to 1 (enabled), all packets from others interfaces or others networks will be not forwarded, and simply discarded. During all this tutorial, if you have some problem to ping or reach a specific ports, ensure this state is well configured.

# enable ipv4 forwarding
sysctl net.inet.ip.forwarding=1
# enable it at boot
echo "net.inet.ip.forwarding=1" >> /etc/sysctl.conf
# ensure this flag is enable (should print 1)
sysctl net.inet.ip.forwarding

Secondly, we can play with some TCP and UDP states in the OpenBSD network stack, increasing buffer space for our UDP/TCP packets.

cat >> /etc/sysctl.conf << EOF
net.inet.udp.recvspace=262144
net.inet.udp.sendspace=262144
net.inet.tcp.recvspace=262144
net.inet.tcp.sendspace=262144
EOF

After this little introduction, we can now install what we need: OpenVPN and Easy-RSA on our 2 OpenBSD servers. In this case, you can install them in multiple way:

  • Using official packages from OpenBSD repository with pkg_add ;
  • Building openvpn and easy-rsa ports ;
  • Using dpb (and create your own repository at the same time).

During this tutorial, I will try to use only official packages, freely available, and doing all we need.

# ensure openvpn and easy-rsa are present in the repository
pkg_info -Q openvpn
pkg_info -Q easy-rsa
# exist? we can install it!
pkg_add openvpn easy-rsa

openvpn package let you initialize your own openvpn directory, by default, I will create it under /etc/openvpn on OpenBSD. This directory will contain all our OpenVPN configuration, but, also all managed certificates and keys for our servers/clients.

# create a directory dedicated to openvpn
mkdir /etc/openvpn
chmod 0700 /etc/openvpn
cd /etc/openvpn

Public Key Infrastructure (PKI) is a technic to store and manage (create, sign, remove or revoke) private, public keys and certificates. easyrsa can generate this easily with the help of init-pkisubcommand. If you want to create your own manually, you can find lot of tutorial on the web.

# initialize a public key infrastructure, in our case, it will
# just create a directory named 'pki' with some files in it
# to manage ssl/tls keys
cd /etc/openvpn
/usr/local/share/easy-rsa/easyrsa init-pki

OpenVPN, using PKI, require a Certificate Authority (CA) to works as expected. A CA is used to trust other managed certificate. This certificate will sign all our generated server or client certificates, ensuring there are correctly managed by our PKI. This file can be generated with easyrsa and build-ca subcommand.

# create a new certificate authority
/usr/local/share/easy-rsa/easyrsa build-ca

OpenVPN gives you the ability to use many different algorithms and methods to add security to your connection. Diffie-Hellman (DH) seed, is used to exchange keys between two entities (in our case, server and client). This file can also be generated by easyrsawith gen-dh subcommand.

/usr/local/share/easy-rsa/easyrsa gen-dh

We have a PKI, a CA, a DH, but we still don’t have our own certificate for our OpenVPN server. This certificate will be used only for our server. Again, easyrsa can create this file with build-server-full subcommand.

/usr/local/share/easy-rsa/easyrsa build-server-full server

At this step, you have all information required to start your own server, if you try it now, you will be prompted for a password. This password protect your server key. Sometime, its pretty useful to have a protection on this kind of file, but, in other side, each time your service will be restarted (even at boot), a password will be required. This security can be removed by using openssl and rsa subcommand.

openssl rsa -in pki/private/server.key \
-out pki/private/server-wop.key

We have a server certificate, we need at least one client certificate for testing if our OpenVPN is working as expected and test our network.

# we will also generate our client certificate
/usr/local/share/easy-rsa/easyrsa build-client-full client

OpenVPN support TLS auth key. This file will be shared with all client and ensure another layer of security based on a shared secret.

cd /etc/openvpn
openvpn --genkey --secret ta.key

OpenVPN can be used in many ways. We can manually start it with a long argument list like any command, or just create configuration file containing our those arguments. The last one seems easier to understand and modify. This configuration will be stored in /etc/openvpn/server.conf.

# we can now create our configuration file
touch /etc/openvpn/server.conf
cat > /etc/openvpn/server.conf < EOF
local ${YOUR_IP_ADDRESS}
port 1194
proto udp
dev tun
ca /etc/openvpn/pki/ca.crt
cert /etc/openvpn/pki/issued/server.crt
key /etc/openvpn/pki/private/server.wop.key
dh /etc/openvpn/pki/dh.pem
server 10.0.1.0 255.255.255.0
; push "route 10.0.2.0 255.255.255.0 10.0.1.1"
ifconfig-pool-persist ipp.txt
client-to-client
keepalive 10 120
tls-auth /etc/openvpn/ta.key 0
cipher AES-256-CBC
max-clients 100
user _openvpn
group _openvpn
persist-key
persist-tun
topology subnet
status openvpn-status.log
verb 3
explicit-exit-notify 1
EOF

For this example, I commented the push route directive. If you have multiple connected network to your server, enabling that could be a nice idea. OpenVPN server will share these routes to OpenVPN client, and your client will be able to talk to other network easily.

OpenBSD use rcctl command to manage internal services. This command will automatically edit for you /etc/rc.conf.local with your options.

# enable openvpn
rcctl set openvpn flags "--config /etc/openvpn/server.conf"

Our OpenBSD service is now active, we can start it manually.

# start our service
rcctl start openvpn

Our OpenVPN service should run now. We can check it by using rcctl again

rcctl check openvpn

If your server doesn’t run… You can start to debug it in standalone mode and try to understand what’s going on!

openvpn --config /etc/openvpn/server.conf

Don’t forget to use other command like ifconfig or netstat to investigate and see if you don’t have made a mistake in your server configuration.

# check network interface
ifconfig -a
# check listening ipv4 sockets
netstat -nla -finet
# check listening ipv4/udp sockets
netstat -nla -finet -pudp
# check routing table
netstat -rn

I always enable OpenBSD Packet Filter firewall at startup, we will add some rules to

  • Allow our OpenVPN to listen on UDP/1194 port
  • Allow all traffic flow in our VPN network
  • NAT our traffic to the outside world
# configure packet filter
cat > /etc/pf.conf << EOF
external="myinterface"
vpn_network="10.0.1.0/24"
# don't apply our rules on lo* interfaces
set skip on lo
# don't apply our rules on tun* interfaces
set skip on tun
# create a NAT rules, all packet from VPN network
# will be translated to the external interface IP address
match out inet from $vpn_network \
to any nat-to ($external) static-port source-hash
# OpenVPN listen on port 1194, we allow it
pass in quick proto udp from any to port 1194
EOF

Our packet filter configuration was modified, but, can be sure about our syntax? The good way before reload our firewall configuration is to test it.

# check packet filter configuration
pfctl -nf /etc/pf.conf

If our firewall configuration is okay… We can now start packet filter with the new configuration.

# start packet filter
pfctl -ef /etc/pf.conf

We have a working OpenVPN service, listening on UDP/1194 port, a working PKI, you can now generate all your server or client certificates based on previous commands and we have a working firewall configuration. Now, the client configuration…

Client Configuration on FreeBSD

This part will show you how to configure a simple OpenVPN client with certificate authentication only. In this article, I will not show you how to connect it to another kind of authentication (e.g. LDAP or PAM).

Installing and configuring OpenVPN as client is straight forward. The first thing to do, is to install OpenVPN, you can do it, like OpenBSD, in different ways:

# install openvpn package
pkg install openvpn

On FreeBSD, OpenVPN package doesn’t create any kind of users or groups, you need to create them manually. To make things easier, you can create them with the same uid/gid present on OpenBSD (577). Just to be clear, _openvpn user and group, starting with an underscore, will not work on some systems (underscore is not allowed in names).

# create default openvpn user and group
pw group add _openvpn -g 577
pw user add _openvpn -u 577 -s /sbin/nologin

Like our server configuration, we need to create our directory, this one will contain OpenVPN client configuration.

# create our configuration directory
mkdir -p /usr/local/etc/openvpn/pki
cd /usr/local/etc/openvpn

OpenVPN can act as server or client only by enable a switch, the configuration file is practically the same than the server configuration.

# create our configuration file
touch /usr/local/etc/openvpn/client.conf
cat > /usr/local/etc/openvpn/client.conf << EOF
client
dev tun
proto udp
remote ${YOUR_IP_ADDRESS}
resolv-retry infinite
nobind
user _openvpn
group _openvpn
persist-key
persist-tun
ca /usr/local/etc/openvpn/pki/ca.crt
cert /usr/local/etc/openvpn/pki/issued/client.crt
key /usr/local/etc/openvpn/pki/private/client.wop.key
remote
tls-auth /usr/local/etc/openvpn/ta.key 1
cipher AES-256-CBC
verb 3
EOF

Our configuration is ready but we need to copy our certificates and our keys from our server. This is not the good way, in practice, our client will give us a Certificate Signing Request (CSR), we will sign it with our Certificate Authority and give it the requested certificate (CRT).

# on our server, create a tarball containing our test certificate
cd /etc/openvpn
tar czvf ~/client-cert.tar.gz ta.key \
pki/ca.crt \
pki/issued/client.crt \
pki/private/client.key \
pki/reqs/client.req

If you want to create your OpenVPN tunnel at startup, you need to configure our service in /etc/rc.conf, sysrc gives your the ability to do that easily.

# enable openvpn service 
sysrc openvpn_enable="YES"
sysrc openvpn_if="tun"
sysrc openvpn_configfile="/usr/local/etc/openvpn/client.conf"

Everything seems good! It’s the time to start our OpenVPN client service and watch if all is working as expected!

# we can now start openvpn
service openvpn start

If you don’t want to tip your password key every time when are starting your server or your client configuration, you can remove this protection with openssl command.

# on server side
cd /etc/openvpn/pki/keys
openssl rsa -in server.key -out server.wop.key
# on client side
cd /usr/local/etc/openvpn/pki/keys
openssl rsa -in client.key -out client.wop.key

You can now create numbers of new client and connects them to your fresh OpenVPN server. Its a good start for our infrastructure!

Monitoring tunnel

I think a best practice is to monitor and automatize everything just after all is working. You can monitor your tunnel in multiple way, personally, I like monit, a small and open-source monitoring tool. Here 2 small examples to ensure our tunnel is up and running:

# we check if the process openvpn is running
check process openvpn matching "/usr/local/sbin/openvpn"
# we check if our network is reachable
check network openvpn-local address 10.0.1.1

If our OpenVPN crash, or, for some reason, we can’t reach our listening address, monit will send you an alert (mail by default).

Monitoring connected client

Our VPN is now up and running! You have created lot of certificate, and all your hardware is connected to your new beautiful server! But… How to supervise your client on server side?

OpenVPN create a status file in /var/log/openvpn/openvpn-status.log containing all information about connected client by adding status openvpn-status.log parameter in its configuration file.

If you want to do more, you can also enable management interface by adding management ${hostname} ${port} in your OpenVPN configuration file. This will give you access to a dynamic command prompt accessible via telnet.

Android Clients

How to connect a mobile phone or a tablet on Android? You can use OpenVPN for Android, also available on FDroid. I think, if you did all the work before, configuring a graphical OpenVPN client will be not complicated.

Next Time

VPN is a really interesting topic! This is the first one of a probably long series. This part was dedicated to client/server tunnel based on OpenVPN. In another parts, I will show you how to create a distributed network with Tinc on OpenBSD/FreeBSD, and how to share route with OSPF and bird/openospfd. We will talk about some other tunnel technology like IPSec, OCServ, MLVPN, ll other small tools to make tunnel everywhere (like netcat, socat, stunnel…) and much more (hiding a VPN or a tunnel, automatic deploy with salt/ansible…)!

Thanks

  • Thanks to Calomel website, help me a lot! ❤
  • Thanks to OpenBSD, its really easy to create anything on it! ❤
  • Thanks to OpenBSD Amsterdam, nice project, I’m using your server to build my distributed VPN, and it just works. ❤
  • Thanks to FreeBSD and HardenedBSD projects. ❤
  • Thanks to you, anonymous reader, to follow me and all my adventures in the digital world!

Resources