OpenVPN Setup Guide
Browse securely from anywhere using a personal VPN with OpenVPN, LDAP, FreeBSD, and PF.
A VPN allows you to securely extend a private network over the internet via tunneling protocols and traffic encryption. For most people, a VPN offers two primary features: (1) the ability to access services on your local network over the internet, and (2) secure internet connectivity over an untrusted network. In this guide, I'll describe how to set up a personal VPN using OpenVPN on FreeBSD. The configuration can use both SSL certificates and LDAP credentials for authentication. We'll also be using the PF firewall to NAT traffic from our VPN out to the internet.
One important note about running your own VPN: since you are most likely hosting your server using a VPS or hosting provider, with a public IP address allocated specifically to you, your VPN will not give you any extra anonymity on the internet. If anything, you'll be making yourself more of a target, since all your activity can be trivially traced back to your server's IP address. So while your VPN will protect you from a snooping hacker on the free WiFi at Starbucks, it won't protect you from a federal investigation.
This guide assumes you are running FreeBSD with the PF firewall. If you're using a different Unix flavor, I'll probably get you most of the way there—but you'll be on your own when configuring your firewall and networking.
Finally, I've used example.com and a non-routable public IP address for all the examples in this guide. You'll need to replace them with your own domain name and public IP address.
With all that out of the way, let's get started.
First, you'll need to install OpenVPN. You can use your distribution's package manager, but my examples assume you're using the FreeBSD
ports tree. I recommend building with the EASYRSA option—you'll need it later.
OpenVPN should ship with a default configuration file. Open it up in your editor to see all the available options. I've pasted my own
configuration below, along with some commentary where appropriate. You'll almost certainly need to make some changes.
For added security, you can have clients supply their LDAP credentials in addition to their
provisioned SSL certificates. You'll need to install a plugin first:
Then, just supply your LDAP connectivity details in a separate configuration file. Make sure
you include each option below—I spent hours troubleshooting this part because I had neglected the
Timeout parameter.
If you're running your own DNS server for the VPN, make sure it's listening for DNS queries on the VPN's interface.
If you followed my DNS Hosting Guide, you'll just need to add the VPN's network address to
BIND's configuration file:
Finally, you need to generate a Certificate Authority and an SSL key pair. You'll distribute the CA to your
clients so they can verify the authenticity of your VPN server. These files can be generated using the Easy-RSA
package. Install it now if you didn't specify it in OpenVPN's build options earlier.
Copy the whole Easy-RSA package into OpenVPN's configuration directory.
EDIT (29 Nov 2017): In a production setup, it's never a good idea to do certificate management on a public-facing server.
Once you've verified everything works, you should move your Easy-RSA directory to a secure location away from the prying eyes of the internet. (Thanks
to @DavidSommerseth for reminding me to point this out.)
Easy-RSA is configured by setting options in the vars configuration file:
Now you're ready to generate your certificate infrastructure:
Copy the generated files into a separate directory, as we specified in openvpn.conf:
Enable OpenVPN to start on boot:
Finally, start OpenVPN!
You can check for any startup issues in /var/log/messages.
OpenVPN will create a secure virtual network for your VPN clients, but it's up to your firewall to
route traffic from your VPN interface out to the internet using Network Address Translation (NAT).
If you followed the PF section of my FreeBSD Server Guide,
you just need to make a few small tweaks to pf.conf. I've highlighted the important changes in bold.
Reload PF for these changes to take effect (you may want to do this from a serial console in case you
messed something up):
Tell the kernel to enable packet forwarding in order to support NAT:
One last thing: since your server will now be forwarding packets, you must disable hardware offloading on your network card to avoid nasty issues:
To make these changes permanent, make the following changes in /etc/rc.conf:
For clients to connect to your VPN server, they will need a client configuration file (usually with an
.ovpn extension) and a certificate key pair signed by your VPN's CA. You'll
have to generate these files on your server and provide them to your clients out of band. I usually
just put the files in a tarball and email them, but you might have more stringent security requirements.
Proceed accordingly.
The client configuration file will be the same for all clients, so let's start with that first. Create a
file called client.conf in OpenVPN's configuration directory. Most of the
options in this file will mimic your configuration in openvpn.conf.
Now you need to create a client certificate. You'll need a different certificate for each device you
want to connect to the VPN, because a single certificate cannot be used for multiple simultaneous connections.
In this example, we'll create a certificate called macbook for my $2000 ssh machine.
Note the nopass option used here—since I'm additionally using password authentication
via LDAP, I don't see a need to encrypt the certificate with a password. If you aren't using password authentication, you probably
want to have your clients generate their own private key and send you a Certificate Signing Request. You can read about that
process in the Easy-RSA documentation.
Anyway, the macbook key pair is now located in the pki directory
on the server. Now we need to shuffle some filenames around and generate a tarball so I can get the configuration onto my MacBook.
A tarball with the server's CA certificate, the client key pair, and the client configuration file for macbook
is now located at /tmp/macbook.tar.gz. You can then scp (or worse, email) this file to the client machine and
fire up your OpenVPN client. On macOS, I'm a big fan of Tunnelblick. Just double-click the .ovpn
file and Tunnelblick will automatically import your VPN profile.
As of this writing, there is currently one small change to make in Tunnelblick: edit your VPN profile and change OpenVPN version to Latest (2.4.4).
This allows you to use the more secure GCM cipher. In future versions of Tunnelblick, this step may be unnecessary.
After that, you can connect to your VPN server and start browsing. You can verify your traffic is being tunneled through the VPN by checking your external public IP address
(I use icanhazip.com). If you run into any issues, check the OpenVPN logs on your client and server.
To troubleshoot, try pinging a public IP address (like 8.8.8.8), as well as your own server (10.8.0.1).
If you can ping locally but can't ping out to the internet, it's most likely a firewall or routing issue.
A VPN is a must-have tool to protect your privacy, especially when using a sketchy public network. It also provides a reliable and convenient way
to securely access your personal "intranet" from anywhere in the world.
Recent versions of OpenVPN have a very small resource footprint, making it
suitable for use on a VPS. When maxing out my server's internet connection with a speed test, OpenVPN delivered 85 mbps while using only
50% of a single CPU core. With typical traffic, system load is minimal.
Configuring the OpenVPN Server
cd /usr/ports/security/openvpn
make install clean
LDAP Authentication
cd /usr/ports/security/openvpn-auth-ldap
make install clean
Notes on Using Your Own DNS Server
Generating the Server Certificates
cd /usr/ports/security/easy-rsa
make install clean
cp -r /usr/local/share/easy-rsa /usr/local/etc/openvpn/easy-rsa
cd /usr/local/etc/openvpn/easy-rsa
./easyrsa.real init-pki
./easyrsa.real build-ca
# You will be prompted for a password when generating the CA. Keep it
# somewhere safe, since you will need it when generating client certs.
# You can put whatever you like for the Common Name. I use
# "vpn.example.com CA".
# relace "vpn.example.com" with your server's FQDN.
./easyrsa.real build-server-full vpn.example.com nopass # change me!
# enter password from previous step when prompted.
./easyrsa.real gen-dh
mkdir /usr/local/etc/openvpn/keys
cd /usr/local/etc/openvpn/keys
cp ../easy-rsa/pki/ca.crt .
cp ../easy-rsa/pki/dh.pem .
cp ../easy-rsa/pki/issued/vpn.example.com.crt . # change me!
cp ../easy-rsa/pki/private/vpn.example.com.key . # change me!
service openvpn start
Routing VPN Traffic to the Internet
pfctl -f /etc/pf.conf
sysctl net.inet.ip.forwarding=1
# replace vtnet0 with your external network interface
ifconfig vtnet0 -tso -lro
Provisioning Your VPN Clients
cd /usr/local/etc/openvpn/easy-rsa
# Generate a client keypair for "macbook" - you will need to provide the CA
# password you specified last time.
./easyrsa.real build-client-full macbook nopass
CLIENT=macbook
SERVER=vpn.example.com # change me!
mkdir /tmp/${CLIENT}
cp pki/ca.crt /tmp/${CLIENT}/ca.crt
cp pki/issued/${CLIENT}.crt /tmp/${CLIENT}/client.crt
cp pki/private/${CLIENT}.key /tmp/${CLIENT}/client.key
cp ../client.conf /tmp/${CLIENT}/${SERVER}.ovpn
chmod 644 /tmp/${CLIENT}/*
tar cvzf /tmp/${CLIENT}.tar.gz /tmp/${CLIENT}
rm -rf /tmp/${CLIENT}
Conclusion