This document describes using OpenVPN to set up a virtual private network for secure communications between roaming clients and my home network. Lately, I’ve found myself in situations where I needed internet access but I didn’t really trust the network I was on. My solution was to set up a VPN server in my home network that I could connect to from my laptop while on the road. That way, if anyone happened to be sniffing the local network, all they’d see are encrypted packets heading to my network. Some of the features you will find described here include:
- A bridged VPN using a tap device
- Access to my home network through the VPN server
- Certificate based authentication
- TLS keys
- Authentication with user credentials
- Redirection of all network traffic through the VPN
- VPN clients running on Mac OS X and Windows 7
I chose OpenVPN for setting up my VPN for a couple of reasons. The biggest one was the fact that I’d be using it with a Windows and a Mac client and I wanted something that would work with both. Not only that, but I wanted it to work without having to muck about with the clients too much. The Windows laptop is a work laptop with other VPN software already installed (proprietary, commercial stuff, of course, that I can’t use for connecting to home) and I didn’t want what I was doing to end up wrecking any of that. Another reason I chose OpenVPN is that it is under active development and that it is quite popular, meaning there are lots of resources available to help with setting it up.
The particular version of OpenVPN that I’m using is 2.3.3.
You will find an overview, including a very nice diagram, of the Arda Network here.
For The Truly Paranoid
When I set up my VPN it was with the idea of denying local networks the ability to look over my shoulder and monitor what I’m doing. For this purpose my VPN fits the bill perfectly. However, there is a downside. Anyone watching packets will be able to see where they’re going, namely my home network. A little bit of investigation will lead anyone with the inclination back to my website. My setup fails miserably at anonymizing my activity. If I had wanted that, I’d have chosen to use one of the many VPN services available in the internet. But I wanted to do more than just fill a need I had for secure communications, I wanted to learn how VPNs work. If what you’re looking for is to anonymize your internet activity, this is not the document for you. Look into an anonymizing VPN service or use Tor. But if you’re going to use Tor just remember, if you’re the only Tor user on a particular network The Man can still find you.
The VPN Server
I have OpenVPN installed on a FreeBSD server running on my home network. I installed it from FreeBSD’s ports tree using the standard make install clean process. The VPN server is not the gateway for my network, I have a router for that, but it does act as a gateway for any clients connecting through it. By that I mean when I connect to my home network with a VPN client, I can access all the machines on my network. I’ve also set up my VPN so that network traffic destined for the internet, like web browsing, goes through my VPN as well. If it didn’t do that, the whole purpose of using the VPN to prevent local network from seeing what I’m up to would be futile. Fear not. I’ll point out the particular configuration items that determine how web browsing is routed.
One of the first decisions you’re going to have to make when setting up OpenVPN is whether to use routing or bridging. Your choice has consequences regarding how your clients communicate with the VPN server, how certain types of software will work across the VPN tunnel, and how you build your configuration files.
I won’t get into too much discussion about the virtues of routing over bridging and visa versa here. There is plenty of documentation on the subject at the OpenVPN website. I will say that I went with bridging mainly because I was having trouble getting the routing right so that my VPN clients could access more than just my VPN server. Given what I’ve learned since my first attempt to set up OpenVPN, I think I could get a routed VPN working now. But I have the bridged VPN working and it’s doing what I need it to do. Right now I have no reason to switch to a routed VPN.
The Configuration File
Here are the contents of my OpenVPN configuration file.
local 192.168.10.15 port 1194 proto udp dev tap0 ca /usr/local/share/certs/cacert.pem cert /usr/local/share/certs/servercert.pem key /usr/local/share/certs/serverkey.pem dh /usr/local/etc/openvpn/dh2048.pem ifconfig-pool-persist ipp.txt server-bridge 192.168.10.15 255.255.255.192 192.168.10.48 192.168.10.60 ifconfig 192.168.10.16 255.255.255.192 push "redirect-gateway def1 bypass-dhcp" push "dhcp-option DNS 192.168.10.11" keepalive 10 120 tls-auth /usr/local/etc/openvpn/tlskey.pem 0 cipher AES-256-CBC auth SHA256 comp-lzo max-clients 10 user nobody group nobody persist-key persist-tun plugin /usr/local/lib/openvpn/plugins/openvpn-plugin-auth-pam.so login status openvpn-status.log
There are a few things to take note of in my configuration file.
- Because I’ve set up a bridged VPN, I specify a tap device. I also specify the device number. This device must match the device I create at boot time in my /etc/rc.conf file. More on that below.
- I’m using certificate based authentication to verify the identity of clients connecting to my VPN server. I created my own CA, based on OpenSSL, to create the certificates. The file the ca directive points at contains my root certificate that I use to sign my VPN server and client certificates.
- The server-bridge line is what tells OpenVPN to allow clients to access my entire home network and not just the VPN server itself. The first two parameters specify a gateway address (the IP address of the VPN server’s physical interface) and the subnet mask of my home network. The third and forth parameters specify the start and end of a pool of IP addresses that will be handed out to VPN clients when they connect.
- The ifconfig statement assigns an IP address and subnet mask to the tap device. I’ve found documents that claim the tap device must be assigned an address, that the bridge device must be assigned an address, or that things will work if neither is assigned an address. I found that I absolutely needed to assign an address to the tap device. If I remove the ifconfig line from the configuration file, my VPN stops working.
- The first push directive is what tells VPN clients to redirect all their network traffic through the VPN tunnel. Without this line, clients can still access my home network but things like web browsing will be routed through the local network, visible to anyone who’s watching. The bypass-dhcp parameter tells clients to still use the local network for DHCP traffic. This is so DHCP laptops won’t loose their lease with the local DHCP server. There’s nothing more annoying than dropping your VPN connection to discover that your IP address has been handed out to another client.
- The second push directive assigns the DNS server on my home network to the VPN client. You won’t get too far if you direct all client traffic through the VPN and the client is still trying to use local DNS servers for name resolution. Looking through DNS query logs is also a great way to find out how people have been spending their time on the internet.
- In addition to using certificates, I also created a shared secret that I put on all my VPN clients. This adds another layer of protection in case a certificate is compromised. It also helps defend the VPN server against DoS attacks as incoming VPN packets that haven’t been signed with the correct tls-auth key can be dropped without responding to the client at all. Certificate authentication requires a certain amount of communication between client and server before the authentication request fails. Make sure the second parameter to tls-auth is 0 (zero).
- The user and group directives are very important. They cause OpenVPN to drop privileges after it starts. It means that if someone is able to compromise OpenVPN itself, they won’t have free reign on my VPN server. I use this feature on every software package I have that offers it.
- The plugin directive tells OpenVPN to require a user ID and password before VPN clients can connect to my network. This is in addition to certificate and TLS key authentication. Some people would call this overkill. I call it defence in depth. I’ll have more to say on what the user ID and password are checked against later.
I used this command to create the Diffie-Hellman parameter file specified by the dh directive. Notice that I’m specifying 2048 bits instead of the more common 1024 bits.
openssl dhparam -outform PEM -out dh2048.pem 2048
And here is the command I used to create the TLS key pointed to by the tls-auth directive.
openvpn --genkey --secret tlskey.pem
The tap Device
I added these lines to my rc.conf file to create the tap and bridge devices and to bridge the tap device with my server’s physical interface which happens to be em on my box.
cloned_interfaces="tap0 bridge0" ifconfig_bridge0="addm tap0 addm em0 up"
I also added this line to rc.conf to allow my server to route packets. This is necessary to allow the VPN clients to see more than just the VPN server itself.
I found a number of documents that use scripts to create and destroy the tap device (or tun device if you’re using a routed VPN) and to bridge it with the server’s physical interface. I couldn’t get these configurations to work for me. I got a lot of permission denied errors when the script would try to create the virtual interface device. I’m not sure why this is. It may be because I tell OpenVPN to drop privileges after startup. I have not, however, investigated the problem thoroughly enough to come to a definitive conclusion. Using scripts has its advantages and disadvantages. They give you more control over when the tap and bridge devices are created and destroyed but it also means that you have to give OpenVPN permission to run arbitrary scripts. Since setting up a VPN is all about security, it just doesn’t seem right to give OpenVPN this permission if I don’t have to. And since my setup is working without scripts, it turns out I don’t have to.
As I mentioned above, in addition to certificates, clients have to supply a user ID and password before they can connect to my VPN. There are a couple of reasons I’ve added this additional layer of authentication to my VPN. The first is that I don’t like relying on certificates exclusively for access to my home network. If someone can get their hands on a client certificate and private key, by breaking into one of my laptops for example, they’d have unfettered access to my network if I didn’t have credential authentication in place. The second reason is that one of my laptops really isn’t mine, it’s a work laptop. It’s not entirely under my control so it is conceivable that its VPN certificate and key could be copied without my knowledge. And lastly, my VPN certificates are installed on laptops, one of which is usually with me when I travel. They are in greater danger of being stolen then a desktop sitting at my office would be. I feel better knowing that someone would need to supply a correct password before they could get into my network even if they had successfully stolen and broken into one of my laptops.
OpenVPN comes out of the box with the ability to check a user ID and password against a user account using PAM. There are also projects that allow user authentication using LDAP or RADIUS. I’m just using the PAM authentication included with OpenVPN. Using LDAP or RADIUS seemed like overkill even to me. To that end, I set up a user on my VPN server especially for VPN access. It doesn’t exist on any other server. Here’s what it looks like in my passwd file.
The user account’s shell is set to nologin and its home directory is set to /nonexistent which, as you might expect, doesn’t exist. I can’t log into the server with this account which is just what I want. The password I use with this account is also different from what I use with my regular login account. All this is to minimize the damage someone can do if they ever get a hold of my VPN password. Even if someone had my VPN password and a valid certificate, they’d still need a second set of credentials before they could log into any of my servers.
To run OpenVPN at system boot, all I had to do was add this line to my rc.conf file.
That’s it. There are some optional parameters available that I didn’t use. You can change the location of OpenVPN’s configuration file and specify the interface device to use; tun or tap. I didn’t see any advantage to specifying tap this way so I didn’t.
The Mac Client
I installed Tunnelblick on my Mac which bundles OpenVPN with a Mac specific GUI. It’s pretty slick. It allows you to configure multiple VPN configurations if you have more than one OpenVPN server you regularly connect to. I didn’t need that particular feature but it might come in handy for others.
When first configuring Tunnelblick, you have the option to use an existing OpenVPN configuration file. This is what I did. I created a configuration file then pointed Tunnelblick at it once I thought it was finished. The thing is, Tunnelblick takes this configuration file and uses it to create its own file which is the one it consults from then on. If you continue to modify the original configuration file, Tunnelblick won’t see the changes. But fear not, Tunnelblick has an option in its GUI to display the configuration file for the currently selected connection. Once displayed, you can edit this file and save it if you need to make changes to the configuration after initial creation. The file displayed by Tunnelblick looks exactly like a regular OpenVPN configuration file so you don’t have to worry about any surprises there. No problem once you know this is how Tunnelblick works.
Here is my Tunnelblick configuration file.
client dev tap proto udp remote vpn.arda.homeunix.net 1194 resolv-retry infinite nobind user nobody group nobody persist-key persist-tun ca cacert.pem cert clientcert.pem key clientkey.pem ns-cert-type server verify-x509-name "atlas.arda.homeunix.net" name tls-auth tlskey.pem 1 cipher AES-256-CBC auth SHA256 comp-lzo auth-user-pass lladdr 00:ff:32:b6:80:de explicit-exit-notify verb 3
Unlike the server’s configuration file, the certificates and tls-auth key don’t have a path associated with the filenames. This is required by Tunnelblick.
The ns-cert-type directive is a way to prevent another client with a valid certificate from pretending to be your VPN server. You have to ensure that you’re setting the nsCertType x509v3 extension properly when you create your certificates. See more about this in the Certificates section.
The verify-x509-name directive helps ensure the client is connecting to the proper VPN server. This directive tells OpenVPN to check the server certificate’s distinguished name to see that it matches what the client expects. There are different ways to use this option but I’m using it to check the server certificate’s common name. This happens to be the fully qualified domain name of my OpenVPN server.
You need to make sure the cipher and auth parameters match what you’re using on the server. If com-lzo is specified on the server, it needs to be here, too. Make sure the second parameter to tls-auth is set to 1.
The auth-user-pass directive is what tells the client to ask the user for a user ID and password.If the server can’t authenticate the user ID and password, then the client is not allowed to connect even if its certificate and TLS key are valid.
The lladdr directive sets the MAC address of the tap device to the specified value.I needed to do this for two reasons. One, OS X seems to like to assign a different MAC address to the device every time it is created and two, I use MAC address filtering on my home network. If I don’t have the tap device’s MAC address in my router’s allow list, the VPN client can’t reach the internet. The Windows OpenVPN client always begins it’s tap/tun device MAC addresses with 00::ff so I did the same here. The rest of the address I just made up.
The Windows Client
For my Windows laptop, I downloaded the OpenVPN Windows Installer available from the OpenVPN Community Downloads page. It’s a typical Windows installation without any surprises. A description of the Windows GUI provided by OpenVPN can be found here.
One thing that is important to remember, OpenVPN on Windows needs to be run with Administrator privileges. It needs this because it will be changing the routing table when it is started. If you have Administrator privileges, then you need to start OpenVPN by right-clicking on its icon and selecting, Run as Administrator. If you don’t have Administrator privileges, you will need to choose Run As… and enter credentials for an admin user.
Here is my config.ovpn file.
client dev tap proto udp remote vpn.arda.homeunix.net 1194 resolv-retry infinite nobind persist-key persist-tun ca cacert.pem cert clientcert.pem key clientkey.pem ns-cert-type server verify-x509-name "atlas.arda.homeunix.net" name tls-auth tlskey.pem 1 cipher AES-256-CBC auth SHA256 comp-lzo auth-user-pass explicit-exit-notify verb 3
This configuration file looks a lot like the one on my Mac. The user and group directives are missing because they just don’t work on Windows. The lladdr directive is also missing because on Windows, the tap device is always given the same MAC address so I don’t have the same problem with an ever changing MAC address like I had with OS X.
Probably the biggest headache you’ll come across when managing an SSL based anything are the certificates. If you’re rolling your own certificates, you’ll have to set up a certificate authority or CA. If you’re intent on doing things The Right WayTM, you’ll have a root certificate that you keep offline and an intermediate CA certificate that you use for signing client requests. You’ll be distributing your CA certificate to all your clients so they can validate your VPN server’s certificate and you’ll be handling your root and intermediate CA private keys in such a way that all the bad people out there won’t get their hands on them. If you ever loose control of your CA private keys, after all, the game’s over and you’ll be generating new certificates for your CA and all your clients.
Even if you don’t set up your own CA, you’ll still be generating certificate requests for all your VPN clients (and your OpenVPN server, too), getting the requests signed and managing all the keys associated with these certificates. And you’ll be doing this periodically because it’s good practice to set an expiry date on your certificates.
And I haven’t even gotten to what to do if one of your certificates is compromised. In that case, you’ll be creating a revocation for the compromised certificate and putting the revocation on your OpenVPN server so that it will reject connections using that certificate. If ever one of my laptops is stolen, one of the things I’ll be doing, besides a lot of swearing, is to revoke the certificate that is on that laptop and make sure OpenVPN is configured to reject it.
If all this sounds like a lot of work, it is. And the workload only increases as the number of certificates goes up. There are tools out there to help reduce the workload but I haven’t come across anything that takes away the pain completely. Bottom line is that certificate management is an ongoing affair and you’ll be spending a certain amount of time doing it.
A tool discussed in the OpenVPN documentation intended to reduce the work of creating a CA and managing certificates is easy-rsa. It’s basically a wrapper that invokes OpenSSL to perform various operations related to generating the certificates needed by OpenVPN. I haven’t used this tool myself as I already set up my own OpenSSL based CA that I use when generating certificates for my web and mail servers. I didn’t really need to do anything new to use it for generating the certificates OpenVPN would use. I won’t go into the details of how my CA is set up. That could be it’s own Setup document. I will, however, discuss one aspect of certificate creation that is relevant to OpenVPN.
When you use certificate based authentication with OpenVPN, you need an SSL certificate on the VPN server and the clients. There is nothing special about the certificate that goes onto the VPN server compared to the certificates that will be going onto the clients, however. The OpenSSL command to create the requests is the same in both cases and the command to sign the requests by your CA is the same, too. Well, that’s not entirely correct. The command to sign the requests can be the same but it doesn’t have to be. You can make the VPN server certificate different from the client certificates if you want to. In fact, there is a good reason to do so.
If you’ve looked at my Windows and Mac OpenVPN configurations, you’ll notice that they both include the ns-cert-type server parameter. This is telling OpenVPN to look for a particular x509v3 extension in the certificate offered by the VPN server, the Netscape Cert Type extension, and that the value of this extension is SSL Server. You can view the extensions of a certificate with OpenSSL
openssl x509 -in <vpn_server_cert_filename> -noout -text ... X509v3 extensions: X509v3 Basic Constraints: CA:FALSE Netscape Cert Type: SSL Server ...
The reason you want OpenVPN to check this is so that a machine with a legitimate VPN client certificate can’t masquerade as the VPN server. To pull off such a deception, the perpetrator would likely need to perform a successful DNS cache poisoning attack to get your client to connect to his machine instead of the true VPN server in the first place. If he can do that, however, he can configure his instance of OpenVPN to act as a server and voila, you’re connected to some unknown machine that is likely logging everything you do.
To get the correct extension into my certificates, I put them in my OpenSSL configuration file. I have my configuration file divided into sections that I can call from the command line when signing certificate requests.
Here are the relevant extensions in the client section.
[ client ] nsCertType = client extendedkeyusage = clientAuth
And here are the ones from the server section.
[ server ] nsCertType = server extendedKeyUsage = serverAuth
You’ll notice that I’ve listed two extensions from each section. In my setup, OpenVPN is only making use of the nsCertType extension to verify that it’s connecting to a server certificate. OpenVPN has a parameter I haven’t used, remote-cert-eku, that looks at the extendedkeyusage extension. The nsCertType extension is deprecated, at least according to the OpenSSL documentation, and certificates signed by different CAs may not include it. The extendedkeyusage extension is current, however, so you should be safe relying on it. Since I’m in control of my own CA, I can use whichever extension I want.
Things To Do
OpenVPN has features that I haven’t touched on in the description of my setup. Some of these features I’d like to take advantage of but just haven’t had time. Some of them aren’t really applicable to my situation. You might want to investigate some of these topics further.
- Running OpenVPN in a chroot jail. This is a fantastic idea and is something I should do. I just haven’t gotten to it yet.
- Restricting access to clients. OpenVPN allows you to restrict client access to your network in a very granular fashion. Not all clients need to have access to all the computers on your network and different clients can have different access rights. This sort of thing is less important for me since my VPN is really just for me. It could be a way to mitigate damage, though, if someone was able to scam my laptop (and my credentials) from me.
- Instead of using OpenVPN to hand out IP addresses to VPN clients, just hand the job over to my DHCP server. This would give me a lot of control over what DHCP options to send to clients. I can’t see a big need for this right now but maybe in the future.
- Deal with the fact that I have a dynamically assigned public IP address. I don’t have a static IP from my ISP and so my public facing IP could change at any time. This would cause problems for any VPN connections that happen to be established when the IP change occurs. OpenVPN has ways to deal with this scenario. My public IP changes so infrequently, though, that it just hasn’t become a problem, yet.
- Configure OpenVPN to reject revoked certificates. Once again, OpenVPN does offer configuration directives that let you pass it a list of revoked certificates so it can correctly ignore these if someone tries to use one to access my network. Since I haven’t needed to revoke any certificates, yet, I just haven’t set up OpenVPN to use a certificate revocation list.
Software Home Sites
|Various descriptions of OpenVPN on FreeBSD||http://ivoras.net/blog/tree/2013-06-03.openvpn-on-freebsd.html|