Configure OpenVPN on FreeBSD like road-warrior script

Introduction

I wanted to install and configure OpenVPN on Free/OpenBSD, just for fun. With no background, and no knowledge, I was able to manually install and configure everything by analyzing Nyr's openvpn-install script for linux. Also by reading "Building firewalls with OpenBSD and PF" by Jacek Artymiak, and networking section of "Absolute FreeBSD" by Michael W. Lucas.

Warning!

In some cases, I still don't know how things exactly work. Configuration I present here, was made based on linux script and with sugar-based food. It worked for me, it may not work for you. None the less, I still think it's good example on how it should be configured on BSD based servers.

Steps

Install software and certificates

# This should also install easy-rsa package as dependency
pkg install openvpn openssl ca_root_nss
    

Generate keys

mkdir -p /etc/openvpn/server/easy-rsa/
cd /etc/openvpn/server/easy-rsa/
easyrsa init-pki
easyrsa --batch build-ca nopass
easyrsa build-server-full server nopass

# Certificate for client, will be used in creating .ovpn file.
# If You have multiple clients, generate multiple certificates.
# You can do that in last step.
easyrsa build-client-full "d3suu" nopass

easyrsa gen-crl
cp pki/ca.crt pki/private/ca.key pki/issued/server.crt pki/private/server.key pki/crl.pem /etc/openvpn/server
chown nobody:nobody /etc/openvpn/server/crl.pem
chmod o+x /etc/openvpn/server/
openvpn --genkey --secret /etc/openvpn/server/tc.key

cat >/etc/openvpn/server/dh.pem <<EOF
-----BEGIN DH PARAMETERS-----
MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
-----END DH PARAMETERS-----
EOF
    

Build server config file

# Here, 'local' is Your public address, specifically one on interface You want to run OpenVPN on.

cat >/etc/openvpn/server/server.conf <<EOF
local 123.456.789.1
port 1194
proto udp
dev tun
ca ca.crt
cert server.crt
key server.key
dh dh.pem
auth SHA512
tls-crypt tc.key
topology subnet
server 10.8.0.0 255.255.255.0
EOF

echo 'push "redirect-gateway def1 bypass-dhcp"' >> /etc/openvpn/server/server.conf

echo 'ifconfig-pool-persist ipp.txt' >> /etc/openvpn/server/server.conf

# Google DNS servers, can change them to any You want.
echo 'push "dhcp-option DNS 8.8.8.8"' >> /etc/openvpn/server/server.conf
echo 'push "dhcp-option DNS 8.8.4.4"' >> /etc/openvpn/server/server.conf

cat >>/etc/openvpn/server/server.conf <<EOF
keepalive 10 120
cipher AES-256-CBC
user nobody
group nobody
persist-key
persist-tun
status openvpn-status.log
verb 3
crl-verify crl.pem
EOF

echo "explicit-exit-notify" >> /etc/openvpn/server/server.conf
    

Build OpenVPN daemon

This is actually unfinished, I only tested it by directly running OpenVPN executable in config directory with tmux. You probably will need all client certificates generated at this point, so it would be a good idea to generate *.ovpn files first.

openvpn --cd /etc/openvpn/server --config /etc/openvpn/server/server.conf
    
# Enable OpenVPN service

echo 'openvpn_dir="/etc/openvpn/server/"' >>/etc/rc.conf
echo 'openvpn_configfile="/etc/openvpn/server/server.conf"' >>/etc/rc.conf
echo 'openvpn_enable="YES"' >>/etc/rc.conf
    

Setup PF for NAT forwarding

# Enable forwarding in kernel
sysctl net.inet.ip.forwarding=1
echo 'net.inet.ip.forwarding=1' >>/etc/sysctl.conf

# Configure PF
# Here, xn0 is my internet-side interface
# tun0 is generated by OpenVPN at runtime
#
# Without NAT forwarding, OpenVPN will still work, and clients will connect, however they will not be connected to internet, only OpenVPN network (10.8.0.0)

cat >/etc/pf.conf <<EOF
nat on xn0 from any to any -> (xn0)
pass in all
pass out all
EOF

# Run PF
echo 'pf_enable="YES"' >>/etc/rc.conf
service pf onestart
    

Build client *.ovpn files

If You need multiple clients, generate new certificates for them (if You didn't do that when generating all certificates).

easyrsa build-client-full "d3suu" nopass
    

In this case, file is generated specifically for me. Modify to Your needs.

cat >~/d3suu.ovpn <<EOF
client
dev tun
proto udp
remote 123.456.789.1 1194
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
auth SHA512
cipher AES-256-CBC
ignore-unknown-option block-outside-dns
block-outside-dns
verb 3
<ca>
EOF

cat /etc/openvpn/server/easy-rsa/pki/ca.crt >> ~/d3suu.ovpn
echo "</ca>" >> ~/d3suu.ovpn
echo "<cert>" >> ~/d3suu.ovpn
sed -ne '/BEGIN CERTIFICATE/,$ p' /etc/openvpn/server/easy-rsa/pki/issued/d3suu.crt >> ~/d3suu.ovpn
echo "</cert>" >> ~/d3suu.ovpn
echo "<key>" >> ~/d3suu.ovpn
cat /etc/openvpn/server/easy-rsa/pki/private/d3suu.key >> ~/d3suu.ovpn
echo "</key>" >> ~/d3suu.ovpn
echo "<tls-crypt>" >> ~/d3suu.ovpn
sed -ne '/BEGIN OpenVPN Static key/,$ p' /etc/openvpn/server/tc.key >> ~/d3suu.ovpn
echo "</tls-crypt>" >> ~/d3suu.ovpn