Search This Blog

Thursday, February 14, 2019

OpenVPN on OpenBSD

It seems like I have a habit of not doing things the easy way. A great example of this is, instead of using something like Rocks, I built all of the cluster's software up from CentOS. This post will be another example of forgoing the easy for the sake of learning.

VPN's are a great tool to prevent ISPs and other internet companies from collecting valuable data on you. Internet companies, e.g. Google (owners of Blogger, the irony of which is not lost on me), collect data on what you do online in order to target you with ads. They also bundle the data and sell it to other companies, such as political organizations specializing in political manipulation. Government agencies collect data, i.e. spy, on their own citizens' internet usage. Saying you have nothing to hide is a terrible argument. "Arguing that you don't care about the right to privacy because you have nothing to hide is no different than saying you don't care about free speech because you have nothing to say." Enough of that rant.

While the free VPN's are mostly crap if not malicious, a few of the more prominent paid for VPN's certainly appear to do what they advertise. Many of these VPN services provide a VPN client for download. This client abstracts all of the background details of how the VPN is configured, is established, working, etc, and often include useful tools, such as routines for forcing all internet traffic over the VPN and for preventing DNS leaks. As a learning experience, I signed up for a month of one of the more prominent ones: PIA, or Private Internet Access. PIA has an excellent VPN client available for many OS's, including Windows, Mac, most flavors of linux, routers, etc. However, they do not have one that supports OpenBSD. You can probably guess where this is going...

Enter OpenVPN. OpenVPN is a very popular open-source VPN server-client program. It also has a port for OpenBSD. PIA's servers have OpenVPN servers setup, and they provide sample .ovpn configuration files. Now begins the adventure of getting OpenVPN working on OpenBSD.

Installing OpenVPN was simple enough using the pkg_add command. As of OpenBSD 6.4, the openvpn version number is 2.4+, which is good. The first step though was configuring the firewall using pf and pf.conf. I needed the firewall to do a few things: 1. Block all traffic except: 2. Let communications, SSH in particular, on my private LAN pass, 3. allow inbound and outbound traffic on the VPN's virtual interface. The OpenVPN FAQ and pf man pages were excellent resources, as was this blog, which I unfortunately didn't find until a couple hours into this. The following is my pf.conf file (with some stuff obscured):

#....header lines

# external interface:
ext_if = "interface0"
opvn_if = "tun0"
lan_net = "192.168.X.0/24"

# don't filter on loopback interface
set skip on lo0

# scrub incoming packets
match in all scrub (no-df)

block drop all

# antispoof for $opvn_if inet
block in quick from urpf-failed
# only allow ssh connections from LAN. Can restrict to a few IP's later.
# as it is, not necessary due to previous and following rules.
# block return in quick on $ext_if proto tcp from ! $lan_net to $ext_if port ssh
# Alternatively:
# pass in on $ext_if inet proto tcp from $lan_net to $ext_if port ssh flags S/SA synproxy state

# pass all traffic to and from the LAN
# these rules will create state entries due to the default
# "keep state" option which will automatically be applied.
pass in on $ext_if from $lan_net
pass out on $ext_if to $lan_net

# At this point, can only communicate on LAN.

# pass tcp, udp, and icmp out on the interface to internet
pass out on $opvn_if proto { tcp udp icmp } all modulate state 
# must open port udp 1197:
pass out on $ext_if proto udp from self to any port 1197
pass in on $ext_if proto udp from any to self port 1197

# By default, do not permit remote connections to X11
block return in on ! lo0 proto tcp to port 6000:6010

# Port build user does not need network
block return out log proto {tcp udp} user _pbuild
This successfully blocks internet traffic if the tun0 interface is down, i.e. if the VPN is not connected.  The next step was to configure the openvpn client. This is done with a .ovpn file. There are lots of useful guides for this online: 1 2 3 4 5 . Link 5 includes information for setting up an openvpn server on openbsd, too. You can download a PIA OpenVPN configuration file from the generator (log into your account) or from here. I used the "strong" settings, which include better cryptographic algorithms. I modified the PIA files slightly. Here's an example:

dev tun0
proto udp
remote 1197
remote 1197
remote 1197
remote 1197
resolv-retry infinite
# add downgrade privileges after initialization:
# requires sudo package
# user _openvpn
# group _openvpn
# chroot /var/openvpn 
#prevent passwords from being cached in memory:
cipher aes-256-cbc
auth sha256
remote-cert-tls server
verb 1
reneg-sec 0
...crl verify....(removed from this post for brevity) certificate ...(removed from this post for brevity)
# disable-occ
# being polite:
writepid /etc/openvpn/curpid
#prevent dns leaks. must use pia dns servers
# doesn't work on linux: push "dhcp-option DNS"
script-security 2
up "/bin/sh /etc/openvpn/change-resolve-conf"
down "/bin/sh /etc/openvpn/undo-resolve-conf"
# add to force all client traffic through VPN
redirect-gateway def1
I won't explain all of the settings. I removed the crl verify and ca certificates because they're very long and not necessary for this post, but they would be where the ...'s are. I commented out the disable-occ so that I could see warnings. OpenVPN with PIA gives warnings about the type of cipher or auth being used being inconsistent between the client and server. It turns out that these are false warnings and can be ignored (I did output at verb 4 once to check). I write the pid out to a file. Originally, I was trying to use that in a alias that could kill the openvpn process, but I found it was easier to use "pkill openvpn". The last line "redirect-gateway def1" is very important: that does a few things to your routing table (and undoes them when openvpn exits), but its primary purpose is to force all of your internet traffic over the VPN. On the rare occasions where that isn't the desired behavior, then you can do some fancy routing instead.

Now all of the traffic should be going through the VPN, which means that, if the VPN provider doesn't keep logs (they say they don't), then there's no way for your ISP/anyone to know what you are doing over the VPN (unless you expose yourself by logging into a website or something like that). However, there is still a way for your ISP to know which websites you visit...this is called a DNS leak. There are a few ways to check for DNS leaks. One of the easiest is to go to If your ISP is listed as the server, then you have a DNS leak. Another way is to do: nslookup -type=NS  or  dig . The "Server" entry will be the DNS server...if its your local gateway or your ISP's, then you have a DNS leak. This is why I added the script-security 2 and the following two lines to the .ovpn configuration file. Before I added those, I expected to (and did) have a DNS leak. The contents of the scripts are as follows:

mv /etc/resolv.conf /etc/resolv_conf.orig
mv /etc/resolv_ovpn_conf /etc/resolv.conf  
mv /etc/resolv.conf /etc/resolv_ovpn_conf
mv /etc/resolv_conf.orig /etc/resolv.conf

Basically, one (up script) changes the resolv.conf file from the one with my router's IP in it (the original) to one with the PIA DNS servers, and the down script (run when openvpn closes) changes it back. This works great and prevents the DNS leak.

The last thing to do was figure out how to start openvpn without having to type out the full command: !/usr/local/sbin/openvpn --daemon --config /etc/openvpn/openbsd.ovpn (note: this has to be done as root). There are a few options: 1. create a /etc/hostname.tun0 file containing "up" on the first line and the above command on the second line, 2. create a rc.d script (see helpful link 5 above), 3. create aliases. Option 1 and 2 will auto-start the VPN on boot, and of the two, option 1 is recommended. Option 3 allows you to start (and stop) the VPN at will, and is what I implemented first, though it wasn't straightforward. OpenBSD uses ksh by default and doesn't have something like a .bashrc or .chsrc. It has a .profile. However, digging into the documentation, it's recommended that you create a $HOME/.kshrc file for your aliases and put a line in the .profile that calls it. However, if I log in as a normal user, then all of my shells start out as user shells, which means the .kshrc of root is never read even when I do su root. I have to do su root, then ksh -l, in order to force the .kshrc (and thus aliases) to be read. Kind of a pain, but it works. I created an alias for pkill openvpn, which allows me to stop vpn. I will probably switch to Option 1 shortly. 

After all this, I decided to download the PIA client on my windows machine just to try it. It took ~10 minutes to setup vs. the ~10 hours this took, haha. But at least I have an understanding of how all of this works now.

Update: See this post for updates to the PF rules (more secure) and ovpn configuration.

1 comment:

  1. Thanks for this post! This is exactly what I'm trying to do right now and I got kinda stuck. Your ~10 hours have been very helpful.