In this example, I will create a virtual private network (VPN) server with OpenVPN and Easy-RSA on a FreeBSD VPS. OpenVPN clients, that connect to this VPN server, will then be able to connect to other OpenVPN clients, that are also connected to this VPN server. This is true, even though, the host of each client might not be able to do so otherwise due to carrier-grade network address translation (CG-NAT) or other network limitations. A client, can be any kind of host, such as a server, network device, personal computer workstation or smartphone mobile device. I will be using OpenVPN 2.6.12 and Easy-RSA 3.2.1 on FreeBSD 13.4. I will use the name “foobar” for the VPN server and “foobar-client” for OpenVPN clients.
+------------------------------+
| VPN Server (Germany) |
|------------------------------|
| Internet IP: 123.123.123.123 |
| VPN IP: 10.8.0.1 |
+------------------------------+
|
|
| VPN Subnet: 10.8.0.0/24
----------------------------------------------
| |
| // Internet // |
DSL FW | | 5G CGNAT
+-----------------------+ +-----------------------+
| Client 1 (Copenhagen) | | Client 2 (Sweden) |
|-----------------------| |-----------------------|
| Local IP: 172.213.1.9 | | Local IP: 192.168.1.5 |
| VPN IP: 10.8.0.11 | | VPN IP: 10.8.0.12 |
+-----------------------+ +-----------------------+
Install OpenVPN and Easy-RSA on the VPN server.
OpenVPN is an open source virtual private network (VPN) solution, that provides secure connections and virtual networks on Internet. It uses SSL and TLS protocols for encryption, which ensures secure data transmission between clients and servers on the virtual network. OpenVPN supports a range of configurations, such as point-to-point and site-to-site setups. It can transmit over UDP or TCP through custom ports, which makes it flexible and capable of bypassing firewalls, network translations and other network limitations.
Easy-RSA is a command-line utility, that simplifies the process of creating and managing a public key infrastructure (PKI) for VPNs and other services, that rely on SSL and TLS certificates. It automates certificate generation, sets up a certificate authority (CA) and issues server and client certificates and does key management.
# pkg install -y openvpn easy-rsa
Create a CA and certificates for the VPN server with Easy-RSA.
Create the PKI with Easy-RSA. The PKI will consist of the CA, digital certificates, keys and revocation lists. The passphrase, that will be set for the CA, will be required, when creating certificates for clients.
# easyrsa init-pki
# easyrsa build-ca
# easyrsa build-server-full foobar nopass
# easyrsa gen-dh
Create a TLS-Auth key with OpenVPN.
A TLS-Auth key is a static, pre-shared key, that is used by OpenVPN to add an extra layer of security to the TLS handshake process. It authenticates the initial packets of the connection, which prevent unauthorized and malicious connections, such as denial-of-service attacks, from reaching the OpenVPN server.
# openvpn --genkey secret pki/ta.key
Copy certificates and TLS-Auth key to OpenVPN directory.
# mkdir /usr/local/etc/openvpn
# cp -pR pki/{ca.crt,dh.pem,issued,private,ta.key} /usr/local/etc/openvpn/
# chown -R openvpn:openvpn /usr/local/etc/openvpn
# chmod 600 /usr/local/etc/openvpn/*
Configure OpenVPN.
Configure basic settings, cryptographic settings and VPN network with client-to-client, IP address persistance and secure authentication.
# nano /usr/local/etc/openvpn/openvpn.conf
port 1194
proto udp
dev tun
ca ca.crt
cert issued/foobar.crt
key private/foobar.key
dh df.pem
topology subnet
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist ipp.txt
push "route 10.8.0.0 255.255.255.0"
client-config-dir ccd
client-to-client
keepalive 10 120
tls-auth ta.key 0
data-ciphers AES-256-GCM:AES-256-CBC:AES-128-GCM:CHACHA20-POLY1305
cipher AES-256-CBC
auth SHA256
max-clients 5
user openvpn
group openvpn
persist-key
persist-tun
status openvpn-status.log
log openvpn.log
verb 3
mute 20
explicit-exit-notify 1
If you prefer, you can also begin with a copy of the sample configuration, that has comments.
# cp /usr/local/share/examples/openvpn/sample-config-files/server.conf /usr/local/etc/openvpn/openvpn.conf
Configure client configuration directory (CCD).
OpenVPN identifies each client by its common name (CN) from the client certificate. OpenVPN looks for a file with the same name in the client configuration directory (CCD). In this example, 2 OpenVPN client hosts, 1 and 2, will be assigned static IP addresses in the VPN.
# mkdir -m 700 /usr/local/etc/openvpn/ccd
# nano /usr/local/etc/openvpn/ccd/client-1
ifconfig-push 10.8.0.11 255.255.255.0
# nano /usr/local/etc/openvpn/ccd/client-2
ifconfig-push 10.8.0.12 255.255.255.0
Start OpenVPN server.
Configure FreeBSD to start OpenVPN at boot. Start OpenVPN.
# service openvpn enable
# service openvpn start
# tail -n 50 /usr/local/etc/openvpn/openvpn.log
Enable routing in FreeBSD.
# sysctl net.inet.ip.forwarding=1
# echo 'net.inet.ip.forwarding=1' >> /etc/sysctl.conf
Configure and start PF.
# ifconfig
# nano /etc/pf.conf
interface = "vtnet0"
network = "10.8.0.0/24"
set skip on lo
scrub in on $interface all fragment reassemble
nat on $interface from $network to any -> ($interface)
pass in on $interface proto udp from any to ($interface) port 1194
# service pflog enable
# service pf enable
# service pf start
# service pflog start
Disable TCP checksum offloading.
If TCP checksum offloading is not disabled, then the network interface card (NIC) may offload the calculation of checksums to the hardware, which can lead to a mismatch in checksums. This can lead to dropped connections and corrupt data.
# ifconfig vtnet0 -rxcsum -txcsum -rxcsum6 -txcsum6 -tso -lro
# nano /etc/rc.conf
ifconfig_vtnet0="DHCP -rxcsum -txcsum -rxcsum6 -txcsum6 -tso -lro"
Create certificate for OpenVPN FreeBSD client with Easy-RSA.
# easyrsa build-client-full client-1 nopass
Private-Key and Public-Certificate-Request files created.
Your files are:
* req: /root/pki/reqs/client-1.req
* key: /root/pki/private/client-1.key
Certificate created at:
* /root/pki/issued/client-1.crt
Update certificates and keys in the OpenVPN directory.
# cp -pR pki/{issued,private} /usr/local/etc/openvpn/
# chown -R openvpn:openvpn /usr/local/etc/openvpn
Transfer certificates and keys to OpenVPN client.
In this example, I create a zip file, that contain the CA certificate, the TLS-Auth key, the client key and the client certificate. These certificates and keys will be needed for OpenVPN configuration on the OpenVPN client side.
# mkdir foobar-client
# cp pki/ca.crt foobar-client/foobar-ca.crt
# cp pki/ta.key foobar-client/foobar-ta.key
# cp pki/private/client-1.key foobar-client/
# cp pki/issued/client-1.crt foobar-client/
# zip -j foobar-client foobar-client/*
I recommend automizing this with a script, that takes the CN as an argument.
Install certificates and keys on OpenVPN FreeBSD client.
Extract the zip file, that contains the certificates and keys, that is needed for the OpenVPN configuration.
# unzip foobar-client.zip -d /usr/local/etc/openvpn
Archive: /root/foobar-client.zip
extracting: /usr/local/etc/openvpn/foobar-ca.crt
extracting: /usr/local/etc/openvpn/foobar-client-1.crt
extracting: /usr/local/etc/openvpn/foobar-client-1.key
extracting: /usr/local/etc/openvpn/foobar-ta.key
# chown openvpn:openvpn /usr/local/etc/openvpn/foobar-*
Create configuration file on OpenVPN FreeBSD client.
# nano /usr/local/etc/openvpn/foobar-client-1.ovpn
client
dev tun1
proto udp
remote 123.123.123.123 1194
resolv-retry infinite
nobind
persist-key
persist-tun
ca foobar-ca.crt
cert foobar-client-1.crt
key foobar-client-1.key
tls-auth foobar-ta.key 1
cipher AES-256-CBC
auth SHA256
remote-cert-tls server
verb 3
Start OpenVPN on the FreeBSD client.
# nano /etc/rc.conf
openvpn_enable="YES"
openvpn_configfile="/usr/local/etc/openvpn/foobar-client-1.ovpn"
openvpn_dir="/usr/local/etc/openvpn"
# service openvpn start
Confirm, that the tun interface is up.
# ifconfig tun1
tun1: flags=8043<UP,BROADCAST,RUNNING,MULTICAST> metric 0 mtu 1500
options=80000<LINKSTATE>
inet 10.8.0.11 netmask 0xffffff00 broadcast 10.8.0.255
groups: tun
nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
Opened by PID 4885
Confirm, that the VPN server and other connected clients can be reached.
# ping 10.8.0.1
PING 10.8.0.1 (10.8.0.1): 56 data bytes
64 bytes from 10.8.0.1: icmp_seq=0 ttl=64 time=40.965 ms
Run multiple instances of OpenVPN on FreeBSD.
FreeBSD can run multiple instances of OpenVPN. This is done by assigning each one to a unique tun interface. An example of this could be one OpenVPN client for a privacy related VPN, such as Mullvad, and a business related VPN. Make sure, that each OpenVPN configuration will use different tun devices, different networks and different start scripts. The default start script in FreeBSD support multiple instances by linking the script to names, that can be applied in rc.conf.
In this example, I will run this OpenVPN client as an additional client.
# ln -s /usr/local/etc/rc.d/openvpn /usr/local/etc/rc.d/openvpn_foobar
# nano /etc/rc.conf
openvpn_foobar_enable="YES"
openvpn_foobar_configfile="/usr/local/etc/openvpn/foobar-client-1.ovpn"
openvpn_foobar_dir="/usr/local/etc/openvpn"
# service openvpn_foobar start
There will now be multiple tun devices with active networks.
# ifconfig
The routing table on a FreeBSD host with multiple OpenVPN clients.
In the following example, I used the built-in network analysis utility netstat to inspect the routing table on a FreeBSD host with multiple OpenVPN clients. The output is shown without resolving numeric addresses and ports to names. Traffic will be routed through the most direct and specific route. This is also known as the one with the longest prefix match. In the example, a connection to a client on 10.8.0.0/24 will be routed through tun1, and not tun0, even though Internet traffic on 0.0.0.0/1 will be routed through tun0. The same is true for connection to hosts on the LAN, because the 192.168.1.0/24 prefix is longer than the 128.0.0.0/1. Because the VPN subnet is directly reachable through the tun1 interface, there is no need to add an extra hop IP address as a gateway. The flags are used to indicate, if the link is up (U), and, if the link is a VPN gateway or default gateway (G).
# netstat -rn
Destination Gateway Flags Netif
127.0.0.1 link#2 UH lo0
192.168.1.0/24 link#1 U em0
10.8.0.0/24 link#4 U tun1
0.0.0.0/1 10.10.0.1 UGS tun0
128.0.0.0/1 10.10.0.1 UGS tun0
default 192.168.1.1 UGS em0
Solution: Static IP address for OpenVPN clients not working.
I noticed, that the assignment of static IP address for the OpenVPN clients did not work. The CCD had been defined. The client certificate CN matched the CCD filename in which the assigned IP address is defined. The syntax for IP address and subnet mask matched the documentation. I found, that the CCD directory had mode 600 and not 700. I fixed it and restarted OpenVPN. It worked.
# grep ccd /usr/local/etc/openvpn/openvpn.conf
client-config-dir ccd
# grep client-1 /usr/local/etc/openvpn/issued/client-1.crt
Subject: CN=client-1
# cat /usr/local/etc/openvpn/ccd/
ifconfig-push 10.8.0.11 255.255.255.0
# ls -ld /usr/local/etc/openvpn/ccd
drw------- 2 openvpn openvpn 6 Nov 12 13:37 /usr/local/etc/openvpn/ccd
# chmod 700 /usr/local/etc/openvpn/ccd
# service openvpn restart
More abount OpenVPN.
- https://openvpn.net/community-resources/reference-manual-for-openvpn-2-4/
- https://www.server-world.info/en/note?os=FreeBSD_14&p=openvpn&f=2
- https://forums.freebsd.org/threads/running-two-vpn-connections-at-startup.46389/
- https://openvpn.net/
- https://easy-rsa.readthedocs.io/en/latest/
Attribution and sharing.
Feel free to link to this guide, if you find it useful. Contributions and feedback is always appreciated.