LinkedIn Sourceforge Twitter

Vincent's Blog

Pleasure in the job puts perfection in the work (Aristote)

OpenVPN on OpenBSD

Posted on 2017-06-05 18:14:00 from Vincent in OpenBSD VPN

I've read several articles concerning the setup of OpenVPN on OpenBSD. But I've decided to post this one because none I've read address the problem coming from the setuid.

Indeed, to avoid that OpenVPN stays in memory with the root user, you can configure it to setuid to an anther user, usually _openvpn.

Problem, when you close the connection, this user must have the correct rights to set back your networks configuration settings: route, resolv.conf.

1. Installation of OpenVPN

1.1 Introduction

I will not goes into all details. You can find good information here and here or here. But you can follow my methodology as a receip and you will have OpenVPN on your OpenBSD server, on your OpenBSD client and on Android.

In short:

  • I'll generate the certificates on a dedicated machines
  • Install and configure OpenVPN on my server
  • Install and configure OpenVPN on an OpenBSD client
  • Install and configure OpenVPN on an android machine

The goal of this VPN is to surf internet always from the same country.

In my case, this allow me to use some of my internet subscriptions (which use the ip location concept) from all over the world.

Form every places on the earth, my internet facing IP will always be the same, the one of my VPN server.

1.2 Create the certificates

For security reasons, the certificates will not be created on the server where OpenVPN server will run.

So, on this dedicated machine, I propose you to do:

pkg_add openvpn easy-rsa
mkdir -p /my/certificate/repository
cd /my/certificate/repository
cp -r /usr/local/share/easy-rsa .
cd easy-rsa
cp vars.example vars

Edit vars, change and uncomment required settings.
I propose to, at least, change the settings starting by EASYRSA_REQ.
Then generate the certificates.

./easyrsa init-pki
./easyrsa build-ca  or ./easyrsa build-ca nopass

Up to you to decide is you want to protect your Certificate Authority activities by a password.

./easyrsa build-server-full vpnserver nopass
./easyrsa build-client-full client1 nopass

Generate as many certificates as you want, and give it a clear name to each of them.

We generate the Deffie Hellman file and the TLS-Auth file

./easyrsa gen-dh
openvpn --genkey --secret vpn-ta.key

The TA key is optional, but I propose to generate one because, following the documentation, this adds an additional layer of HMAC authentication on top of the TLS control channel to mitigate DoS attacks and attacks on the TLS stack.

1.2 On the OpenVPN server

1.2.1 The OpenVPN part

Install OpenVPN:

pkg_add openvpn

Copy the required certificates just created. I propose to post them in a /etc/openvpn directory. As you may suppose, thanks to transfer those certificate in a secured way. Not via ftp or poor email systems; but prefer scp

scp pki/ca.crt <user>@<server>:openvpn/
scp vpn-ta.key  <user>@<server>:openvpn/
scp pki/issued/vpnserver.crt  <user>@<server>:openvpn/
scp pki/private/vpnserver.key <user>@<server>:openvpn/
scp pki/dh.pem  <user>@<server>:openvpn/

Then ssh to your server and do (you should be root for this):

cp /usr/local/share/examples/openvpn/sample-config-files/server.conf /etc/openvpn/server.conf

In server.conf, edit at least: port, ca, cert, key, dh, push bypass dhcp, push dns, tls-auth.
Here after my complete server.conf file

port 1194
proto udp
dev tun
ca /etc/openvpn/ca.crt
cert /etc/openvpn/vpnserver.crt
key /etc/openvpn/vpnserver.key  # This file should be kept secret
dh /etc/openvpn/dh.pem
ifconfig-pool-persist ipp.txt
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS"
push "dhcp-option DNS"
keepalive 10 120
tls-auth /etc/openvpn/vpn-ta.key 0 # This file is secret
cipher AES-256-CBC
user _openvpn
group _openvpn
status openvpn-status.log
verb 3

To avoid port scanners scanning default ports, I propose you to change the port and select a non default one.

As you can see, this setup will propose the clients to bypass the dhcp rules and to push some DNS entries. In this case, I will push the OpenDNS ip addresses. You can put what ever you prefer as DNS.

1.2.2 The kernel part

To finalise the setup of you server, you have to tell it that he has to accept and forward ip messages.

In /etc/sysctl.conf, please assure that you have:


If you do not want to reboot your machine, you can type this:

sysctl net.inet.ip.forwarding=1

1.2.3 The firewall part

Please make sure that you have at least this in your pf.conf file.

# vpn 
pass in on $ext_if proto udp from any to $ext_if port {1194}
pass in quick on $tun_if  
pass out on $ext_if from to any nat-to ($ext_if)

$ext_if is your external interface. To know it, just type "ifconfig". If you are running on a virtual environment, it could be "vio0"

1.2.4 To start it

To start your OpenVPN, you just have to start it with reference to your configuration file.

For test phases, start it in a foreground mode, just type:

openvpn --config /etc/openvpn/server.conf

For a more definitive solution, and as stated in the documentation, it's recommended to put this command in the /etc/hotsname.tun file

!/usr/local/sbin/openvpn --daemon --config /etc/openvpn/server.conf

2. OnpenVPN client on OpenBSD

For the client side, we will put all required certificates and configuration elements in one single file: a .ovpn file.

Such .ovpn file can be transferred to your OpenVPN client.

I will show it can be used on OpenBSD and on Android.

2.1. Create the ovpn file

On the machine where you have create the client's certificates, please perform the following instructions:

cd /my/certificate/directory/easy-rsa
cp /usr/local/share/examples/openvpn/sample-config-files/client.conf .

Edit client.conf and after "client" add:

script-security 2
up client.up
down client.down

Assure that "remote", "port" and "comp-lzo" are corresponding to the setting in your server.conf

Comment the lines with "ca ca.cert", "cert client.crt", "key client.key" and the line with "tls-auth ta.key"

Generate your client.ovpn by executing the following commands:

cat client.conf > client.ovpn
echo "<ca>" >> client.ovpn
cat pki/ca.crt >> client.ovpn
echo "</ca>" >> client.ovpn

echo "<cert>" >>  client.ovpn
cat  pki/issued/client1.crt >>  client.ovpn
echo "</cert>" >> client.ovpn

echo "<key>" >>  client.ovpn
cat pki/private/client1.key >>  client.ovpn
echo "</key>" >>  client.ovpn

echo "key-direction 1" >> client.ovpn
echo "<tls-auth>" >>  client.ovpn
cat vpn-ta.key >>  client.ovpn
echo "</tls-auth>" >>  client.ovpn

Voila. Your client configuration file is ready. You just have to transfer it (in a secured way) to your client.

2.2. Start OpenVPN client on OpenBSD

You just have to execute the following:

openvpn --config client1.ovpn

But because OpenVPN change the uid of the process, it cannot perform correctly the commands to restore the system where it was before the opening of the VPN.

So, let check my tricks here under.

2.3. Start OpenVPN client on Android

I'm not a fan of google's stuff, so I propose you to download OpenVPN from f-Droid here

The easiest way to configure your mobile, is to use the .opvn file you have created here above.

I remind that it's not the goal to use the same certificates for different users / devices, so create certificates for your android mobile too.

Once on your mobile, you just have to import it from the OpenVPN App. And that's it :-).

You will have VPN with your server and you will get access to internet (since this is the goal of this VPN) via your VPN server.

3. Some tricks

3.1 Allow doas on /sbin/route when starting OpenVPN

I mention first that you could chroot your OpenVPN and keep it running with root user. But I would explain the other solution: run OpenVPN in the main system but with a setuid to the _openvpn user.

Where you run OpenVPN on OpenBSD, I propose you a trick to avoid annoying situations where your routes are not restored after a stop of OpenVPN. This is basically a problem on the clients.

Typically this is the problem you could have. We see that after the setuid, the problems start:

Sun May 28 18:41:53 2017 /sbin/route add -net -netmask
add net gateway
Sun May 28 18:41:53 2017 /sbin/route add -net -netmask
add net gateway
Sun May 28 18:41:53 2017 GID set to _openvpn
Sun May 28 18:41:53 2017 UID set to _openvpn
Sun May 28 18:41:53 2017 Initialization Sequence Completed
Sun May 28 18:41:56 2017 event_wait : Interrupted system call (code=4)
Sun May 28 18:41:56 2017 /sbin/route delete -net -netmask
route: must be root to alter routing table
Sun May 28 18:41:56 2017 ERROR: OpenBSD/NetBSD route delete command failed: external program exited with error status: 1
Sun May 28 18:41:56 2017 /sbin/route delete -net xx.xx.xx.xx -netmask
route: must be root to alter routing table
Sun May 28 18:41:56 2017 ERROR: OpenBSD/NetBSD route delete command failed: external program exited with error status: 1
Sun May 28 18:41:56 2017 /sbin/route delete -net -netmask
route: must be root to alter routing table
Sun May 28 18:41:56 2017 ERROR: OpenBSD/NetBSD route delete command failed: external program exited with error status: 1
Sun May 28 18:41:56 2017 /sbin/route delete -net -netmask
route: must be root to alter routing table
Sun May 28 18:41:56 2017 ERROR: OpenBSD/NetBSD route delete command failed: external program exited with error status: 1
Sun May 28 18:41:56 2017 Closing TUN/TAP interface

Basically we see that OpenVPN is using commands "/sbin/route" to perform some changes when you start and stop it. It does this on both the client and the server.

Some of those changes are performed after the setuid to the user you have mentioned in the OpenVPN configuration's file. So, my proposed solution is to replace the /sbin/route program by the following script (route_my):


if [ "$(id -u)" = "0" -o "$1" = "show" ]; then
  /sbin/rroute $*
  doas /sbin/rroute $*

As you can see I have renamed the original /sbin/route by /sbin/rroute.

This small script allows the user referenced in /etc/doas.conf to run the /sbin/route command now renamed /sbin/rroute. In other words, you must add in your /etc/doas.conf file, the following:

permit nopass _openvpn as root cmd /sbin/rroute

Please note that the user must be the one you have put in your OpenVPN configuration file: server.conf or client.conf. On OpenBSD is _openvpn

This is not a solution that will resist to an upgrade of your system, so I propose you to have the following startup script for OpenVPN (


if [ "`file -b /sbin/route | grep "ELF"`" ]; then
  echo "we move /sbin/route to /sbin/rroute"
  doas mv /sbin/route /sbin/rroute
  echo "we move route_my to /sbin/route"
  doas cp route_my /sbin/route
  echo "/sbin/route type is:"
  file -b /sbin/route
echo "start openvpn"
doas openvpn --config /etc/openvpn/server.conf

Basically this script check if the /sbin/route is a binary. In such case, it replace is by a local script called route_my which is the script here above.

As you can notice, I use several "doas" in order to be able to start OpenVPN from my own user.
Such setup is perfect for a VPN client where you want to start/stop the VPN when you want and without rebooting your machine.

As consequence, you have to have the following in /etc/doas.conf:

permit nopass vi as root cmd openvpn
permit nopass vi as root cmd cp args route_my /sbin/route
permit nopass vi as root cmd mv args /sbin/route /sbin/rroute

3.2 Allow OpenVPN to update the /etc/resolv.conf

On the client side, there is still one small problem with OpenVPN on OpenBSD: the DNS.

Indeed, to update the DNS entries in resolv.conf, we have to explain to OpenVPN how to do it.
For that I'm using shell script provided by OpenVPN but not present in the OpenBSD package :(.

Basically you have to copy the client.up script and client.down script on your machine. And make sure that those scripts are executable (chmod +x ...)

The "client.up" is run by OpenVPN before the setuid to _openvpn. So, it works perfectly. BUT, the "client.down" does not work as it is, since it's run by the user _openvpn.

The most "OpenVPN way to do it", would be to use a plugin provide with the package which is called: But I've done it the "OpenBSD way", with doas.

So, I have to adapt it like this:

if [ -e /etc/resolv.conf.ovpnsave ] ; then
  # cp + rm rather than mv in case it's a symlink
  doas cp /etc/resolv.conf.ovpnsave /etc/resolv.conf
  doas rm /etc/resolv.conf.ovpnsave

I have to make sure that _openvpn can do those command by adapting accordingly /etc/doas.conf

permit nopass _openvpn as root cmd cp args /etc/resolv.conf.ovpnsave /etc/resolv.conf
permit nopass _openvpn as root cmd rm args /etc/resolv.conf.ovpnsave

Then you have to adapt your OpenVPN configuration file like this:

# Specify that we are a client and that we
# will be pulling certain config file directives
# from the server.
script-security 2
up client.up
down client.down

In the first part of the configuration file, you have to add the last 3 lines. It tells to OpenVPN that he can run scripts and it gives the location of them.

Each time you will start OpenVPN, the client.up will be executed. Each time you stop/kill the OpenVPN process he will execute the script client.down

Note: my client.ovpn and client.up and client.down are on the same folder. If you want to put them in separated folder, please adapt your client.ovpn file accordingly.

38, 36
displayed: 9477

1. From Mark on Sun Dec 10 20:35:17 2017

Thank you so much. I have been reading manuals and searching online for 2 days for a solution. Many questions have been posted over the years about this issue, but nobody seems to ever post an answer. I have even read one response claiming that OBSD does not use resolv.conf (which we know is brutally incorrect. I have been plagued with DNS leaks due to OpenBSD reolv.conf not updating and your client.up & client.down scripts worked flawlessly. NO more leaks.

2. From Vincent on Tue Dec 12 06:22:58 2017


3. From Markus Hutmacher on Sun Jul 29 22:27:29 2018

Hi, thanks for sharing this. I'm running OpenBSD 6.3 and had to put an "rm -f" into the client.down script as well as into the doas.conf file. This means "permit nopass _openvpn as root cmd rm args -f etc/resolv.conf.ovpnsave" Without the -f I was asked for the root-password when bringing the tunnel down. Regards Markus

What is the second letter of the word Moon?