Sunday, 11 May 2014



Updated 2015 January 04 : see the changelog

After the Snowden whistleblower revelations, we learned that what we could have though of as paranoïa or conspiration, is in fact very true. There is a worlwide automated mass surveillance of citizens, it is not targeted investigations but rather full blown automated privacy violation. We may have guessed it, but until now there wasn't proofs or facts to support it. Oh and by the way it is not "just" the USA/NSA surveillance, there is great probabilities that the country you live on is also spying on you. Being a french citizen, I learned that my country is well known for its leading Deep Packet Inspection (DPI) products that it sells to foreign dictators, and that it also spies on its citizens and already provided the NSA enormous ammount of intercepted data. Same goes for the UK, who already accessed part of the NSA's tools for their own purpose, such as spying on Belgium Telecom. I can even mention Sweden who is providing information to the NSA too. Chances are, unfortunately, that no matter where you live, anything you do online is automatically recorded : web surfing, orders, emails, search requests, downloads, and even phone calls and text messages ( including Blackberry). From these last links, it seems that even Germany has its hands on NSA's tool and cooperate with them. So USA, UK, France, Sweden, Germany, who else ?

We may think that well, we will do something about that, but what ? One of the direct answer to automated recording and surveillance is using a Virtual Private Network (VPN). That way, everything going out of your network is encrypted and cannot directly be used to spy on you. Sounds easy, so let's buy a router from a known brand to do it, right ? Unfortunately, NSA can be here too by having probably a backdoor in it allowing them unfeterred access to the router. The last solution is to build your router yourself, based on one of the most secure Operating System (OS) around : OpenBSD.

The search for privacy is not a criminal act, it is a fundamental human right.

The OpenBSD VPN gateway we'll see soon, brings on the following features :
- DHCP server to distribute network parameters to your LAN
- DNS cache/server to speed up DNS requests
- DNS encryption so that every outgoing request cannot be spied on by your ISP
- VPN gateway to your VPN provider (AirVPN is used on this article)
- Watchdog script to check that your connection is secure (VPN and DNS encryption are alive)
- Windows software client displaying in the taskbar information sent by the Watchdog script (optional)
- Prevents any direct Internet access from the LAN (prevents VPN bypass)
- Intercept DNS leaks from the LAN to the Internet, and redirect them to the local DNSCypt.

It could be sume up as an encrypted DNS and VPN gateway, with a monitoring script to warn you and self repair itself in case of something is down, to always stay fully encrypted and to ensure that nothing goes out in clear.

To make an OpenBSD VPN gateway you will need the following :
- A computer with two network cards
- Two Ethernet cables
- An OpenBSD ISO
- A VPN provider subscription, providing raw OpenVPN configuration file

The network map is a typical home network with one or two computers, and one DSL router from the ISP :

I personally bought as computer a Shuttle DS437, which is a fanless and noiseless small case (SSD required), to build a low energy router (10W idle, equivalent to two Raspberry Pi). OpenBSD 5.6-release natively supports RTL8111G (5.5-release did not).

Beforehand, I would like to thanks people from the forum as their help was invaluable to sort my network card driver issue, thanks again guys :-) I would like also to thanks the website BSDNow which provides many useful up to date tutorials, such as The ultimate OpenBSD router.


Before using your downloaded ISO, check its SHA256 checksum with a "sha256sum.exe" (Windows) for instance you can find on the net, or the sha256 file command on OpenBSD.

Once you have booted on the ISO for your architecture, for instance amd64, you just have to answer simple questions to get started, see below.

Please note that supporting the OpenBSD project is very welcome, you can do so by just buying a 3 CDs set, it will help to keep the project kicking.

Below is just an example, you have to enter values according to your country, network card detected name, your ISP DNS, etc... :
(I)nstall, (U)pgrade, (A)utoinstall or (S)hell ? I
Choose your keyboard layout ('?' or 'L' for list) [default] fr
System hostname? (short form, e.g. 'foo') OpenBSD

Which network interface do you wish to configure? (or 'done') [re0] enter
IPv4 address for re0? (or 'dhcp' or 'none') [dhcp]
Netmask? [] enter
IPv6 address for re0? (or 'rtsol' or 'none') [none] enter
Which network interface do you wish to configure? (or 'done') [done] enter
Default IPv4 route? (IPv4 address, 'dhcp' or 'none')
DNS domain name? (e.g. '') [my.domain] enter
DNS nameservers? (IP address list or 'none') [none]

Password for root account? (will not echo) your password !
Start sshd(8) by default? [yes] enter
Start ntpd(8) by default? [no] yes
NTP server? (hostname or 'default') [default] enter
Do you expect to run the X Window System [yes] no
Setup a user? (enter a lower-case loginname, or 'no') [no] guillaume
Full name for user guillaume? [guillaume] enter
Password for user guillaume? (will not echo) your password !
Since you set up a user, disable sshd(8) logins to root? [yes] yes
What timezone are you in? ('?' for list) [Europe/Paris] enter

Which disk is the root disk? ('?' for details) [wd0] enter
Use DUIDs rather than device names in fstab? [yes] enter
Use (W)hole disk ? W
Use (A)uto layout, (E)dit auto layout, or create (C)ustom layout? [a] enter
Location of sets? (cd disk ftp http or 'done') [cd] enter
Which CD-ROM contains the install media? (or 'done') [cd0] enter
Pathname to the sets? (or 'done') [5.5/amd64] enter

Below the sets to choose are in function of the OpenBSD flavor you install. If it is OpenBSD 5.5-release (from the official website or from CD set), you don't need any X packages that you can remove with "-x*" and "enter". If however you either use OpenBSD 5.5-current (from an online snapshot for instance) or that you use a release flavor, but expect to build packages from ports instead of from the "pkg" command line, you will need at least the xbase55.tgz :
Set name(s)? (or 'abort' or 'done') [done] -game55.tgz
Set name(s)? (or 'abort' or 'done') [done] enter
Directory does not contain SHA256.sig. Continue without verification? [no] yes

Installation should proceed, then at the end :
Location of sets? (cd disk ftp http or 'done') [done] enter
Time appears wrong. Set to 'xxx xxx x xx:xx:xx xxxx 2014'? [yes] enter
# reboot

Install done, you can now log in :

Login with the root account, and modify the sudoer file to allow your user account to use "sudo" :
# visudo
# Uncomment to allow people in group wheel to run all commands
# and set environment variables.

From now on, you can login with your unprivileged account from SSH and continue the configuration.

If you installed OpenBSD 5.5-release, please apply all available security updates from It contains a security fix for the Heartbleed vulnerability.

It is a good idea to add in the fstab file the mount options "softdep" and "noatime". The first one increase disk performances, while the second will prevent the "last access time" file properties to be written. While it also allows to increase disk speed, it is above all interesting for SSD drives to avoid writing too often to them (which reduces their lifetime).

$ sudo vi /etc/fstab
YourDiskDUID.b none swap sw
YourDiskDUID.a / ffs rw,noatime,softdep 1 1
YourDiskDUID.k /home ffs rw,nodev,nosuid,noatime,softdep 1 2
YourDiskDUID.d /tmp ffs rw,nodev,nosuid,noatime,softdep 1 2
YourDiskDUID.f /usr ffs rw,nodev,noatime,softdep 1 2
YourDiskDUID.g /usr/X11R6 ffs rw,nodev,noatime,softdep 1 2
YourDiskDUID.h /usr/local ffs rw,nodev,noatime,softdep 1 2
YourDiskDUID.j /usr/obj ffs rw,nodev,nosuid,noatime,softdep 1 2
YourDiskDUID.i /usr/src ffs rw,nodev,nosuid,noatime,softdep 1 2
YourDiskDUID.e /var ffs rw,nodev,nosuid,noatime,softdep 1 2

We now need to check or modify our network card IP addresses, and enable forwarding. Please adapt the following to your network card names, and your network class. In my example, there is the LAN network with OpenBSD being, and the other side with the ISP router is with OpenBSD being

$ sudo vi /etc/hostname.re0

$ sudo vi /etc/hostname.re1

Check your gateway is correct :
$ cat /etc/mygate

Enable forwarding to allow your LAN traffic to pass trough your new OpenBSD router :
$ sudo vi /etc/sysctl.conf

4. NTP
NTP should have been enabled at setup, if you followed the setup example. Having a router with a correct time and date is always handy when you need to check your logs to see what happened. Here we just need to quickly check that the ntp client is enabled.

$ sudo vi /etc/rc.conf.local
# Starting NTP client

Create ntpd.conf
$ sudo vi /etc/ntpd.conf

If NTP was not started at boot, enable it now :
$ sudo ntpd -s

That's it !

We want to provide network configuration to make use of our VPN gateway to our LAN network, as well as pointing to our DNS cache. Advertising ourself on the network will allow VPN-incapable devices to make use of the VPN like any computer. It is also a seamless way to provide traffic encryption to your LAN computers without modifying their configuration.

First, make sure DHCP will run at startup :
$ sudo vi /etc/rc.conf.local
# Start DHCP server

Modify DHCP configuration :
$ sudo mv /etc/dhcpd.conf /etc/dhcpd.conf.orig
$ sudo vi /etc/dhcpd.conf
option domain-name-servers;

subnet netmask {
option routers;

Here is the main part of our router. We will decide which traffic to forward, normalize the traffic, randomize IP or TCP ID fields, catch DNS leaks, prevent VPN bypass, and so one. I propose below three different PF configuration. The first one should be used as a temporary one, to check your setup and ensure forwarding is working correctly, and eventually check your VPN. The configurations 2 and 3 are more advanced and are the real pf rules. They both do the same things, but one is rule based, while the other one is policy based.

First PF simple configuration rules, to first check your connectivity to the Internet:
$ sudo vi /etc/pf.conf
# VERSION 1 : basic NAT #
set skip on lo
set loginterface egress
match in all scrub (no-df max-mss 1440)

match out on egress from !(egress:network) to any nat-to (egress:0)
match out on tun0 from !(tun0:network) to any nat-to (tun0:0)
pass out quick on egress keep state
pass out quick on tun0 keep state
pass in quick on $lan_if inet

block # block everything else

This second PF configuration is more complete, it does more and and covers more cases. This one is still a "rule based" configuration :
$ sudo vi /etc/pf.conf
# VERSION 2 : more advanced firewall, VPN out only, no tags #
# Variables, Macro, and Tables

# Global Policy
block log all
set block-policy drop
set loginterface egress
set skip on lo
match in all scrub (no-df max-mss 1440 random-id reassemble tcp)

# NAT rules
match out on $vpn from $lan:network to any nat-to ($vpn:0)

# Antispoof
antispoof log quick for (egress)
block in quick log on egress from { no-route urpf-failed } to any
block out quick log on egress from any to no-route

# Block IPV6
block quick inet6 all

# Prevent VPN bypass
block out quick log on egress from $lan:network to any

# Drop outbound DNS requests (53), as we use DNSCrypt
block out quick log on egress proto { tcp udp } from any to any port 53

# Redirect DNS leaks from the LAN, to ourself
pass in quick log on $lan proto { tcp udp } from $lan:network to ! $lan_ip port 53 rdr-to $lan_ip

# Modify IP Type Of Service to speed up DNS and VPN traffic
match out on egress proto udp from $egress_ip to any port 443 set tos lowdelay set prio 6

# Standard rules
pass out quick inet modulate state
pass in quick on $lan

This third PF configuration is the same as above, but "policy based". It means we are in a first time applying internal "tags" to packets, basing then in a second time our filtering on them. The tags help creating policies, and when correctly used, enable us to create trust between two interfaces. For instance, if a packet comes to the LAN side, and is aimed at a remote address, we can tag it as "LAN_FORWARD". Then, when an outbound packet wants to go out of the egress interface, we can check if it has the tag or not, ensuring it belongs to the "LAN_FORWARD" trusted flow. Policy based filtering is not necessarily better, in fact it's easy to misapply tags, and have a packet tagged more than once (thus losing its first tag) and being blocked involuntarily. It's just a matter of taste I guess, and personally I prefer this version.

Also this more advanced version allows you to tag traffic you do not want to go into your VPN interface. Indeed the VPN tunnel being the default route, if you want to connect to your ISP router HTTP interface for instance, it won't connect because you traffic will be routed inside the tunnel. With this ruleset, you can mark any traffic you like with the NO_VPN tag, which will automatically be routed to your egress interface instead of tun0. Finally, we tweak the priority of outgoing UDP 443 traffic (VPN and dnscrypt) to have a greater priority (6) than default traffic (3). With just this tiny modification, I gained an avereage of 20KB/s in my download speed on a 10Mbps connection (1200KB/s max download):
$ sudo vi /etc/pf.conf
# ---------------------------------------------------------------------------------------
table <internet> const { $all_networks, !self, !$lan:network, !egress:network }
table <lan> const { $lan:network, !self }
table <myself> const { self }

# ---------------------------------------------------------------------------------------
block log all
set block-policy drop
set loginterface egress
set skip on lo
match in all scrub (no-df max-mss 1440 random-id reassemble tcp)

# ---------------------------------------------------------------------------------------
match in on $lan from <lan> to $lan_ip
match in on $lan from <lan> to <internet>
match in on $lan proto tcp from <lan> to $gateway port http
match in on $lan proto { tcp udp } from <lan> to ! $lan_ip port domain
match out on egress proto { tcp udp } from <myself> to <internet> port domain
match out on egress tagged NO_VPN
match out on egress proto udp from $egress_ip to <internet> port https
match out on $vpn from <myself> to <internet>
match out on $vpn tagged LAN_FORWARD
match in on egress from { no-route urpf-failed } to any
match out on egress from any to no-route
match inet6 all
   tag LAN_IN
   tag NO_VPN
   tag GW_DNS_LEAK
   tag NO_VPN_OUT
   tag VPN_OUT
   tag VPN_OUT
   tag IPV6

# ---------------------------------------------------------------------------------------
# NAT & Type of Service & Traffic Priority
match out tagged VPN_OUT nat-to ($vpn)
match out tagged NO_VPN_OUT nat-to egress
match out tagged DNSVPN_OUT set tos lowdelay set prio 6

# Blocking spoofed or malformed packets, and IPv6
antispoof log quick for egress label "block_spoofing"
block quick log tagged BAD_PACKET label "block_bad_packet"
block quick log tagged IPV6 label "block_ipv6"

# Prevent lan VPN bypass
block out quick log on egress tagged LAN_FORWARD label "block_lan_forward"

# Redirect DNS leaks from the LAN, and drop outbound DNS requests (53) as we use dnscrypt (443)
pass in quick tagged LAN_DNS_LEAK rdr-to $lan_ip label "rdr_lan_dns_leak"
block out quick log tagged GW_DNS_LEAK label "block_gw_dns_leak"

# Standard rules
pass out quick modulate state
pass in quick tagged LAN_IN
pass in quick tagged LAN_FORWARD

# This rule avoid the VPN tunnel and route traffic to the egress interface
pass in quick tagged NO_VPN route-to ($egress $gateway)

# Do not Log network noise
block in quick proto udp to port { netbios-ns netbios-dgm }

The "label" added in this third pf ruleset allows for a statistical view, to check which rule has been matched, and how many times. First number is only the number of time the rule has been evaluated:
$ sudo pfctl -s label
BLOCK_SPOOFING 132827 0 0 0 0 0 0 0
BLOCK_SPOOFING 123423 0 0 0 0 0 0 0
BLOCK_BAD_PACKET 132827 2 616 2 616 0 0 0
BLOCK_IPV6 132825 0 0 0 0 0 0 0
BLOCK_LAN_FORWARD 132825 81 4140 0 0 81 4140 0
RDR_LAN_DNS_LEAK 132744 60 4429 30 2046 30 2383 30
BLOCK_DNS_OUT 1452 0 0 0 0 0 0 0

The LAN_FORWARD and RDR_LAN_DNS_LEAK could have been triggered when I was terminating the VPN to check I was effectively cut off the Internet. When VPN is down, the default route becomes the ISP router again, not the virtual TUN adapter, and traffic tries naturally to go out.

You can make your own ruleset, it has not to be only rule based or policy base, you can start on a rule based ruleset, add one or two relevant tags, and just one label on a rule you want to keep an eye on. Feel free to be creative :-)

If you installed the "release" flavor, which is recommended, then this step is much more simple. For instance, if you want to install any packages I will talk about, just do (first line once) :
$ export PKG_PATH=
$ sudo pkg_add package1 package2 ...

# Example if you installed the RELEASE version
$ sudo pkg_add dnscrypt-proxy openvpn

Choose your own mirror at :, then you can skip this chapter.

Besides the fact that using packages is easy, you can in addition rely on M:Tier for security update notifications, and security binary packages. Usually, when you have a release version, third party packages (not in base system) updated with security fix are only available in stable and current version, not in release. That requires to download ports sources as below, and to build your packages just to have the packages security fixes. Thanks to M:Tier, you can keep your release version and only use the "pkg_add" command line, and download packages binary security fixes from them.

However if you had to install the "current" flavor, we have to pull the ports sources, and then build it. I'm not an expert in this area, downloading only "ports" sources may be enough, but in case a port needs librairies in other sources too (sys, X11) I download them all. Choose your CVS server first
$ cd /usr
$ export
$ sudo cvs -d$CVSROOT checkout -P src
$ sudo cvs -d$CVSROOT checkout -P ports
$ sudo cvs -d$CVSROOT checkout -P sys
$ sudo cvs -d$CVSROOT checkout -P xenocara
$ sudo cvs -d$CVSROOT checkout -P X11

Later if you want to update your sources :
$ cd /usr/src (OR ports,sys, X11, etc...)
$ sudo cvs -q up -Pd

If you look for a particular port :
$ cd /usr/ports
$ make search key=your_port

Now let's build our packages. It can take a while :
$ cd /usr/ports/net/dnscrypt-proxy
$ sudo make install
$ cd /usr/ports/net/openvpn
$ sudo make install

If your current system and ports sources are in sync, and that you installed the X11 librairies while first installing OpenBSD on your router, it shouldn't have any error here.

8. DNS
Now that DNSCrypt is installed, we can configure it to run at startup and to listen on localhost port 40, and use a dnscrypt server :
$ sudo vi /etc/rc.local
# DNSCrypt-proxy
/usr/local/sbin/dnscrypt-proxy -a -u _dnscrypt-proxy -d -R

Please note that OpenDNS is a US company, and that their servers by default are logging. If for any reason you do not wish to trust OpenDNS, you may choose another logless dnscrypt-enabled DNS resolver at the following list. For instance, if you prefer using a logless EU based server, located in Denmark, you can modify the "-R opendns" parameter by "-R", like we just did. If you choose to do so, please modify this parameter accordingly in /etc/rc.conf.local and in the commands below. Also be informed that the watchdog script won't be able to check that dns requests are encrypted if you choose something else than "opendns", as the other dns resolvers do not provide (to my knowledge) an online dns check.

Start it now and check it is launched :
$ sudo /usr/local/sbin/dnscrypt-proxy -a -u _dnscrypt-proxy -d -R
$ netstat -an | grep
tcp 0 0 *.* LISTEN
udp 0 0 *.*

DNSCrypt won't be directly accessed by the LAN. Our local DNS cache/handler, Unbound, will. It will forward queries to DNSCrypt if it does not have the answer in its cache:
$ sudo vi /var/unbound/etc/unbound.conf
username: _unbound
directory: /var/unbound
chroot: /var/unbound
do-not-query-localhost: no
access-control: refuse
access-control: allow
access-control: allow
hide-identity: yes
hide-version: yes
auto-trust-anchor-file: "/var/unbound/db/root.key"

name: "." # use for ALL queries
forward-addr: # dnscrypt-proxy

Do not forget to modify your /etc/resolv.conf and to replace the whole file by just one line :
$ sudo vi /etc/resolv.conf

Enable Unbound to launch at startup :
$ sudo vi /etc/rc.conf.local
# Unbound
unbound_flags="-c /var/unbound/etc/unbound.conf"

if Unbound is not started, start it and test that your DNS chain is working :
$ pgrep unbound
$ sudo /etc/rc.d/unbound start
$ nslookup

Non-authoritative answer:

We can finally move on to the next step : VPN :-)

9. VPN
We arrive at the most crucial part : choosing a VPN provider. There is plenty of VPN providers, more than I wish to try. However, if we set criteria like the following, there is not many VPN providers left:
- logless (do not keep logs !)
- OpenBSD compatible (should support OpenVPN configuration file)
- not based in the US (the NSA is there)
- provides an online OpenVPN config file generator
- displays its servers status in realtime
- accept payments in BitCoins
- provides censorship bypass options, by offering multiple ways to mount the VPN (80/443 UDP/TCP, SSH tunneling, etc...)
- should not drop the connection speed to a crawl ! (Internet experience should stay the same)
- allows trying for a few days cheaply
- should offer servers on various countries
- very secure : 4096 bit RSA, AES-256-CBC, 4096 bit Diffie-Hellman, HMAC SHA1, TLS 2048 bit additional key, Perfect Forward Secrecy (PFS)

I have found such a VPN provider : AirVPN

You are free to use any VPN provider of course, but the setup bellow is a working configuration with AirVPN only. There is many configurations possible, I could have chosen a single remote server, or a country, you can play with their online OpenVPN generator to suit your needs. I have chosen to select 3 servers, the first one is the main I will use, and the two others are backup servers.

Please note that the 3 remote VPN IP addresses I will write from now one are fake/random IP, you cannot copy/paste the configuration as is, you have to modify it accordingly to your remote VPN servers you have chosen.


Create your config directory, I created it on "/usr/local/etc/openvpn" :
$ sudo mkdir -p /usr/local/etc/openvpn
$ sudo chown root:wheel /usr/local/etc/openvpn

Now copy via SSH/SFTP (WinSCP for Windows for instance) the files the AirVPN generator created, below is an example :
$ sudo ls /usr/local/etc/openvpn/
airvpn.ovpn ca.crt ta.key user.crt user.key
$ sudo vi /usr/local/etc/openvpn/airvpn.ovpn
dev tun

# Depending on what you have chosen, TCP or UDP (not both !)
proto udp

# When there is multiple "remote" options, OpenVPN tries to connect
# in the order the servers are specified
# Country 1 : Server 1 (Main)
remote x.x.x.x 443

# Country 1 : Server 2 (Backup)
remote x.x.x.x 443

# Country 2 : Server 3 (Backup)
remote x.x.x.x 443

# Chroot OpenVPN and drop root privileges (don't forget to "mkdir /var/empty/tmp")
user _openvpn
group _openvpn
chroot /var/empty

resolv-retry infinite
remote-cert-tls server
cipher AES-256-CBC
comp-lzo no
verb 3
explicit-exit-notify 5

# do a chmod 600 on them
ca "/usr/local/etc/openvpn/ca.crt"
cert "/usr/local/etc/openvpn/user.crt"
key "/usr/local/etc/openvpn/user.key"
tls-auth "/usr/local/etc/openvpn/ta.key" 1

In case you did not yet do it :
$ sudo chmod 600 /usr/local/etc/openvpn/*.*
$ sudo mkdir /var/empty/tmp

Launch OpenVPN at startup :
$ sudo vi /etc/rc.local
# OpenVPN
/usr/local/sbin/openvpn --config /usr/local/etc/openvpn/airvpn.ovpn --daemon

Let's connect !
$ sudo /usr/local/sbin/openvpn --config /usr/local/etc/openvpn/airvpn.ovpn --daemon

You can check in "/var/log/message" that your VPN connected without error, then check with "ifconfig" that the interface "tun0" is active, and finally with a "route -n show" check that the route was added.

You can browse with a client to a website such as to check you are going trough your VPN. You can also go to the AirVPN homepage, where it will displays to which VPN server you are connected. You can also do a "traceroute" and see the VPN magic :-)


Now that our OpenVPN is running chrooted, in the event it would misbehave because of a vulnerability, it is a good idea to additionaly enforce a systrace profile on it. Systrace will allow us to define which system calls are allowed or not. While systrace and chroot alone are not considered secure enough, combining chroot, systrace, and privilege revocation, still makes exploitation harder.

To approach systrace, you have to know there is a learning mode in wich a profile/policy file is filled with explicit calls, and an enforce mode which denies any call not allowed in the policy. Once the learning mode has generated its file, you must edit it to make explicit calls more generic (i.e EXPLICIT_IP:53 -> *:53). You can run a systrace learning mode with $ sudo systrace -A /bin/foo   which will store the application profile in /home/your_user/.systrace/bin_foo file. Once you edited the profile and moved it to /etc/systrace, you can run the program in enforce mode with $ sudo systrace -a -U /bin/foo   (-U argument will use the file located in /etc/systrace).

Below is a working profile I generated. Although it is working on my system, you must test it in learning mode first.
$ sudo vi /etc/systrace/usr_local_sbin_openvpn
Policy: /usr/local/sbin/openvpn, Emulation: native
# Global
native-fstat: permit
native-close: permit
native-read: permit
native-munmap: permit
native-sigprocmask: permit
native-__sysctl: permit
native-gettimeofday: permit
native-minherit: permit
native-mquery: permit
native-sigaction: permit
native-getpid: permit
native-sendsyslog: permit
native-issetugid: permit
native-getentropy: permit
native-pread: permit
native-fork: permit
native-exit: permit
native-setsid: permit
native-dup2: permit
native-poll: permit
native-recvfrom: permit
native-write: permit
native-ioctl: permit
native-wait4: permit
native-writev: permit
native-readv: permit
native-sigreturn: permit
native-clock_gettime: permit

# Memory
native-mprotect: prot eq "PROT_READ" then permit
native-mprotect: prot eq "PROT_NONE" then permit
native-mprotect: prot eq "PROT_READ|PROT_WRITE" then permit
native-mmap: prot eq "PROT_READ|PROT_WRITE" then permit
native-mmap: prot eq "PROT_READ" then permit
native-mmap: prot eq "PROT_NONE" then permit
native-mmap: prot eq "PROT_READ|PROT_EXEC" then permit

# File read
native-fsread: filename eq "/var/run/" then permit
native-fsread: filename match "/usr/local/lib/*" then permit
native-fsread: filename match "/usr/lib/*" then permit
native-fsread: filename match "/usr/lib/*" then permit
native-fsread: filename match "/usr/lib/*" then permit
native-fsread: filename match "/usr/local/etc/openvpn/*" then permit
native-fsread: filename match "/usr/share/zoneinfo/*" then permit
native-fsread: filename eq "/etc/malloc.conf" then permit
native-fsread: filename eq "/usr/share/nls/C/" then permit
native-fsread: filename eq "/var/empty" then permit
native-fsread: filename eq "/var/empty/tmp" then permit
native-fsread: filename eq "/etc/resolv.conf" then permit
native-fsread: filename eq "/etc/group" then permit
native-fsread: filename eq "/etc/spwd.db" then permit
native-fsread: filename eq "/<non-existent filename>: /usr/share/nls/C/" then permit
native-fsread: filename eq "/<non-existent filename>: /usr/share/nls/C./" then permit

# File write
native-fswrite: filename eq "/dev/tun0" then permit
native-fswrite: filename eq "/dev/null" then permit

# Socket operations
native-socket: sockdom eq "AF_INET" and socktype eq "SOCK_DGRAM" then permit
native-socket: sockdom eq "AF_UNKNOWN(17)" and socktype eq "SOCK_RAW" then permit
native-getsockopt: permit
native-setsockopt: permit
native-sendto: sockaddr match "inet-*:443" then permit

# Chroot & Privilege revocation
native-setgroups: permit
native-chdir: filename eq "/" then permit
native-chroot: filename eq "/var/empty" then permit
native-setgid: gid eq "577" then permit
native-setuid: uid eq "577" and uname eq "_openvpn" then permit

# Exec
native-execve: filename eq "/sbin/ifconfig" and argv match "/sbin/ifconfig tun0 * mtu 1500 netmask up -link0" then permit
native-execve: filename eq "/sbin/route" and argv match "/sbin/route add -net * -netmask *" then permit
native-execve: filename eq "/<non-existent filename>: /sbin/route" and argv match "/sbin/route delete -net * -netmask *" then permit

# Process
native-fcntl: cmd eq "F_SETFD" then permit
native-fcntl: cmd eq "F_SETFL" then permit

Be aware that learning mode will generate a profile for programs called by openvpn, such as ifconfig and route. Below are the two automatically generated policies I did not modify. If you go trough a learning mode, these files will be created for you. Anyway, for your information:
$ sudo vi /etc/systrace/sbin_route
Policy: /sbin/route, Emulation: native
native-__sysctl: permit
native-mmap: prot eq "PROT_READ|PROT_WRITE" then permit
native-mprotect: prot eq "PROT_READ" then permit
native-getrtable: permit
native-getpid: permit
native-geteuid: permit
native-socket: sockdom eq "AF_UNKNOWN(17)" and socktype eq "SOCK_RAW" then permit
native-shutdown: permit
native-write: permit
native-mprotect: prot eq "PROT_READ|PROT_WRITE" then permit
native-fstat: permit
native-fsread: filename eq "/etc/malloc.conf" then permit
native-issetugid: permit
native-getentropy: permit
native-minherit: permit
native-mprotect: prot eq "PROT_NONE" then permit
native-ioctl: permit
native-munmap: permit
native-exit: permit

$ sudo vi /etc/systrace/sbin_ifconfig
Policy: /sbin/ifconfig, Emulation: native
native-__sysctl: permit
native-mmap: prot eq "PROT_READ|PROT_WRITE" then permit
native-mprotect: prot eq "PROT_READ" then permit
native-socket: sockdom eq "AF_INET" and socktype eq "SOCK_DGRAM" then permit
native-ioctl: permit
native-mprotect: prot eq "PROT_READ|PROT_WRITE" then permit
native-munmap: permit
native-exit: permit

It can probably be simplified and the calls grouped differently, and it may have to be modified regarding GUID and UID on your system for _openvpn user and group. To test the profile in learning mode:
$ sudo systrace -A -U /usr/local/etc/openvpn --config /usr/local/etc/openvpn/airvpn.ovpn --daemon
$ tail -f /var/log/messages

If everything is working as expected, check your policy file. If nothing new, you can kill openvpn and restart it in enforce mode:
$ sudo pkill openvpn
$ pgrep openvpn
$ sudo systrace -a -U /usr/local/etc/openvpn --config /usr/local/etc/openvpn/airvpn.ovpn --daemon
$ tail -f /var/log/messages

If you want to run it under a systrace policy from the startup, you have to modify the /etc/rc.local file like this:
$ sudo vi /etc/rc.local
# OpenVPN
systrace -a -U /usr/local/sbin/openvpn --config /usr/local/etc/openvpn/airvpn.ovpn --daemon

Your VPN setup is now complete. When OpenVPN starts it drops its privileges from root to the _openvpn user, chroots itself, and runs under a systrace policy.

At this point, you should have a fully working OpenBSD gateway, providing DHCP, DNS cache/encryption, and VPN. However, it is critical to ensure that both DNS encryption and VPN are constantly operational. If by any chance one is stopped, you should be cut off the internet, but that's not always the case. At first I wasn't preventing LAN to be forwarded and NATed out on PF rules, it just happened that once the VPN was up, all traffic was going trough it (tun0 interface). One time, VPN disconnected because of tests I was doing, but I still had internet access, and was sending out clear traffic without knowing it (should not happen with aforementioned PF rulesets). Also, your DNS queries work, fine, but how do you know the encryption is working ? Once again the previous PF rulesets should ensure that regular DNS requests on TCP/UDP port 53 are blocked, but if anything goes wrong on the DNS queries to OpenDNS (port 443), it's better to be warned. Finally, if OpenVPN or DNSCrypt-proxy processes crash for any reason, it's better to have a monitoring script to launch them back.

The following Watchdog script is not mandatory for the OpenBSD gateway to work, you could stop here and not going further, but this script is a kind of privacy insurance. You must edit the variables at the start of the script, to add your IP addresses, your network card names, and your three remote VPN servers you are using. This script basically checks that you have network connectivity, then checks OpenVPN is started, if so checks that you are in fact really connected to a VPN, identifies which remote server. Afterwards it goes on checking that dnscrypt-proxy is started, and verifies that DNS queries are really encrypted.

For every check, this script by default can send UDP packets to two Windows computers on the LAN, but it can be one, more, or none. Just edit the "RECEIVER" variables at the beginning, and edit the "send_notification()" function to specify to which computer send notifications, or to not send anything at all. If you choose to let LAN notifications enabled, the Windows client is covered on the next chapter.
I developped this little script to act as a "guardian" out of curiosity, but noticed that even without it the above configuration just works and is very stable. I no longer use this script, that I nonetheless keep in this article to let you an example you can eventually play with to fit your needs.
$ sudo vi /home/guillaume/
# Watchdog v1.0

# Variables
# -------------------------------------------------------------------------------------

VPN_SERVER_1="x.x.x.x" # Country 1 - Server 1
VPN_SERVER_2="x.x.x.x" # Country 1 - Server 2
VPN_SERVER_3="x.x.x.x" # Country 2 - Server 3
VPN_LABEL_1="Swiss - Server Tarzan"
VPN_LABEL_2="Swiss - Server John"
VPN_LABEL_3="Canada - Server Albert"
RECEIVER_1="" # first LAN computer
RECEIVER_2="" # second LAN computer

GATEWAY=`cat /etc/mygate`
TIME=`date +"%b %e %H:%M:%S"`
LOG_TIME="$TIME $UNAME watchdog :"
NETWORK_CONNECTED=`ifconfig $EXT_INTERFACE | grep active`
VPN_CONNECTED=`ifconfig $VPN_INTERFACE | grep active`
VPN_ROUTE=`route -n show | grep 0/1 | grep $VPN_INTERFACE`
VPN_PID=`pgrep openvp`
VPN_IP=`ifconfig $VPN_INTERFACE | grep "inet " | cut -d'-' -f1 | cut -d' ' -f2`
DNSCRYPT_PID=`pgrep dnscrypt-proxy`

# Functions
# -------------------------------------------------------------------------------------

# This function will send an UDP packet with netcat to inform of the connection status
send_notification() {
echo "$1" | nc -uw 0 $RECEIVER_1 7951
echo "$1" | nc -uw 0 $RECEIVER_2 7951

# Main
# -------------------------------------------------------------------------------------

# If network is disconnected, exit
if [ -z "$NETWORK_CONNECTED" ]; then
send_notification "DOWN_NETWORK (Interface $EXT_INTERFACE not active)"

# If gateway is unreachable, exit
GATEWAY_CHECK=`ping -c 2 -w 2 $GATEWAY | grep "100% packet loss"`
if [ ! -z "$GATEWAY_CHECK" ]; then
send_notification "DOWN_GATEWAY ($GATEWAY unreachable.)"

# Check if OpenVPN is started
if [ -z "$VPN_PID" ]; then
echo "$LOG_TIME OpenVPN process not found, starting it..." >> $SYS_LOG
echo "$LOG_TIME OpenVPN process not found, starting it..." >> $WD_LOG
/usr/local/sbin/openvpn --config /usr/local/etc/openvpn/airvpn.ovpn --daemon
send_notification "DOWN_VPN (OpenVPN process not found)"

# Check if interface tun0 is active
if [ -z "$VPN_CONNECTED" ]; then
echo "$LOG_TIME Interface $VPN_INTERFACE not connected" >> $SYS_LOG
echo "$LOG_TIME Interface $VPN_INTERFACE not connected" >> $WD_LOG
send_notification "DOWN_VPN (Interface $VPN_INTERFACE not connected)"

# Check if a route for interface tun0 exists
if [ -z "$VPN_ROUTE" ]; then
echo "$LOG_TIME Route for $VPN_INTERFACE not found." >> $SYS_LOG
echo "$LOG_TIME Route for $VPN_INTERFACE not found." >> $WD_LOG
send_notification "DOWN_VPN (Route for $VPN_INTERFACE not found)"

# Check if tun0 has an ip address
if [ -z "$VPN_IP" ]; then
send_notification "DOWN_VPN (No IP for $VPN_INTERFACE.)"

# Check the connectivity of all our remote VPN servers
CHECK_VPNSERVER_1=`ping -c 3 -w 2 $VPN_SERVER_1 | grep "100.0% packet loss"`
CHECK_VPNSERVER_2=`ping -c 3 -w 2 $VPN_SERVER_2 | grep "100.0% packet loss"`
CHECK_VPNSERVER_3=`ping -c 3 -w 2 $VPN_SERVER_3 | grep "100.0% packet loss"`

# If all of the servers are unreachable
if ([[ ! -z "$CHECK_VPNSERVER_1" ]] && [[ ! -z "$CHECK_VPNSERVER_2" ]] && [[ ! -z "$CHECK_VPNSERVER_3" ]]); then
echo "$LOG_TIME All VPN servers are unavailable." >> $SYS_LOG
echo "$LOG_TIME All VPN servers are unavailable." >> $WD_LOG
send_notification "DOWN_VPN (All VPN servers are unavailable.)"

if [ -z "$CHECK_VPNSERVER_1" ]; then
echo "$LOG_TIME $VPN_LABEL_1 up." >> $WD_LOG
echo "$LOG_TIME $VPN_LABEL_1 down." >> $WD_LOG

if [ -z "$CHECK_VPNSERVER_2" ]; then
echo "$LOG_TIME $VPN_LABEL_2 up." >> $WD_LOG
echo "$LOG_TIME $VPN_LABEL_2 down." >> $WD_LOG

if [ -z "$CHECK_VPNSERVER_3" ]; then
echo "$LOG_TIME $VPN_LABEL_3 up." >> $WD_LOG
echo "$LOG_TIME $VPN_LABEL_3 down." >> $WD_LOG

# Retrieve our VPN server IP, by reading the last successful connection (tail -n 1)
VPN_REMOTE_IP=`grep "Peer Connection Initiated with" /var/log/messages | tail -n 1 | cut -d'_' -f 2 | cut -d']' -f 2 | cut -d':' -f 1`

# Only do active online IP checking when local log does not have the answer
if [ -z $VPN_REMOTE_IP ]; then
ACTIVE_IP_CHECK=`echo "GET http://$WEBSITE_IP_CHECKER HTTP/1.0\n\n" | nc $WEBSITE_IP_CHECKER 80 | grep "Your IP address is" | cut -d' ' -f5 | cut -d'<' -f1`

if [ "$VPN_REMOTE_IP" = "$VPN_SERVER_1" ]; then
elif [ "$VPN_REMOTE_IP" = "$VPN_SERVER_2" ]; then
elif [ "$VPN_REMOTE_IP" = "$VPN_SERVER_3" ]; then

# All good our VPN is connected
echo "$LOG_TIME OpenVPN alive ($VPN_PID)." >> $WD_LOG

# Check if DNSCrypt is started
if [ -z "$DNSCRYPT_PID" ]; then
echo "$LOG_TIME Dnscrypt-proxy process not found, starting it..." >> $SYS_LOG
echo "$LOG_TIME Dnscrypt-proxy process not found, starting it..." >> $WD_LOG
/usr/local/sbin/dnscrypt-proxy -a -u _dnscrypt-proxy -d -R opendns
send_notification "DOWN_DNS (Dnscrypt-proxy process not found)"

# Check if our DNS requests are effectively encrypted
CHECK_DNS=`dig txt | grep "dnscrypt enabled"`
if [ -z "$CHECK_DNS" ]; then
# Dnscrypt started but dns requests not encrypted
echo "$LOG_TIME dnscrypt-proxy started but dns requests not encrypted ($DNSCRYPT_PID)." >> $SYS_LOG
echo "$LOG_TIME dnscrypt-proxy started but dns requests not encrypted ($DNSCRYPT_PID)." >> $WD_LOG
send_notification "DOWN_DNS (dns requests not encrypted)"
# All good, Dnscrypt started
echo "$LOG_TIME dnscrypt-proxy alive ($DNSCRYPT_PID)." >> $WD_LOG
send_notification "UP_DNS (pid : $DNSCRYPT_PID)"

Now edit the crontab to run it every minute :
$ sudo crontab -e
# Watchdog
* * * * * /bin/sh /home/guillaume/

You can view the scrip working by checking its log file (example with imaginary VPN server names):
$ tail -f /var/log/watchdog.log
May 10 15:54:01 OpenBSD watchdog : Swiss Tarzan up.
May 10 15:54:01 OpenBSD watchdog : Swiss John up.
May 10 15:54:01 OpenBSD watchdog : Canada Albert up.
May 10 15:54:01 OpenBSD watchdog : OpenVPN alive (13279).
May 10 15:54:01 OpenBSD watchdog : dnscrypt-proxy alive (11395).

It is best to check the script is covering you in case of OpenVPN crash :
$ sudo pkill openvpn
Now check the "/var/log/watchdog.log" log file, and you should see that the script notices OpenVPN being stopped, and attempting to start it again. You can check VPN connection steps in "/var/log/messages".

This script by itself, without LAN notifications, reaches its purpose : monitoring and acting automatically to recover a dead process, and keeping you secure. If you also want the LAN notifications, to know your DNS/VPN state from a Windows client, please proceed to the next chapter.

The Windows client is just an icon in the taskbar, and a minimal right-click menu to show informations. The icons are the following :
Blue icon is when the program has started but did not receive any information yet.
Red icon is when a "DOWN" has been received, either for DNS or for VPN.
Green icon is when both DNS and VPN are "UP", and all checks passed successfully.

If the icon is green, the client tells you when was the last time it received an "UP" notification :

If you do a right click, a small popup menu appears, displays which VPN you are connected to if the information was available in the /var/log/messages of the router, or at last resort displays your visible public address. For both VPN IP and DNS entries, you can click them to open websites to check your online status :

You can download the archive containing both the source code (purebasic) and the built Windows binary : Download Here

If you want it to run at startup, you will have to do it manually, either a shortcut in the start menu, or a scheduled task triggered at session opening.

Even now, with all of our outgoing traffic encrypted, four main problems still exist :
1 - once our traffic goes out of the VPN tunnel, it becomes again visible (even if it cannot be linked to us)
2 - our browser can still be used against us to track what we done
3 - our emails can still be read by automated surveillance tools
4 - our searches on search engines like Google are read and stored

For the first point, it means that we should make our best efforts to connect mostly to HTTPS websites, so that the connection stays encrypted until its final destination. To achieve that, using a browser extension such as HTTPS Everywhere is a quick and easy way. It is available for Google Chrome, Firefox, and Opera. It contains a database of more than 1500 websites. If you connect to them in plain HTTP, this extension modifies the request to connect with HTTPS.

For the second point, even if you are connecting using different VPN servers each time, most websites are tracking you, voluntarily or not (often because of tracking ads displayed). To prevent your surf habits from being tracked, another browser extension is here to help : Ghostery. Ghostery knows about nearly 2000 trackers, and recognize more than 2000 tracking patterns. It can incidentally speed up your browsing too, and is available on many browsers : Google Chrome, Firefox, Opera, and Safari.

For the third point, even if we go through a VPN, and connect in HTTPS to our email provider, for instance Gmail, our emails are still stored in clear text and read automatically by Google, and by foreign agencies (I'm referring to Snowden's slides about NSA having access to Google, Facebook, Apple, etc...). Our emails contains mostly private information, credentials to most websites, and even our bank account. I'm keeping an eye on fully-encrypted zero-knowledge privacy email services such as Lavaboom which is in private Beta for now (the provider does not have the encryption/decryption keys to read your emails.

The fourth point can be easily overlooked, but every search we do on Google are sent and stored. Like Mikko Hypponen says (video), we are brutally honest with search engines. They can know more about us than our family or our best friends. The trick here is to move away from Google search engine and try something else like Duckduckgo. At the time of writing, they are developing their next version, available at They do not track you, they do not store your searches, and they do not send your search to websites you visit (usually a website knows the search you did to fall on it). Their "next" version have an equivalent of Google image, and is customizable. Also, if there is any privacy risk they warn you :

Last but not least, be smart. Indeed, it is a moot point to want protecting its privacy and using a VPN, if the purpose is to upload your entire life on a public social service such as Facebook !

With this simple VPN gateway, you are starting to protect your privacy, bravo :

don't forget the sticker, you won't be protected without it !

Having the VPN done externally, not on your computer, offers many advantages :
- it cannot be disabled by a malware on your computer
- it protects you at boot time and when opening your session
- it works for any Operating System
- it works for devices which are not VPN-capable
- it protects your whole LAN not just one computer
- if you format and install another OS on your client computer, you don't have to reinstall the VPN
- it is optimized, as a dedicated hardware fully works on that task
- it creates a DMZ, if you connect anything directly on your ISP router, behind the OpenBSD router
- if there is a backdoor on your ISP router, it cannot be used to reach your LAN.

However we should not forget to keep us informed on NSA's capabilities regarding VPN decryption, even if AirVPN provider seems not affected. I think the world has been shaken by the last revelations, and that new or improved security service will come to help us.

Anyway, using technical means to protect your privacy is just one part of the picture, it doesn't stop the automated mass surveillance. Beyond protecting your privacy, you can fight for it by registering to privacy associations and websites, and participate to bring awareness on privacy issues : "Fight for the future" website, or "Digital Freedom Manifesto" by F-Secure

As I said in the introduction :
The search for privacy is not a criminal act, it is a fundamental human right.

Updated 2015 January 04 :
- OpenVPN : chroot added, login/password text file removed, systrace profile
- DNS : Bind removed, Unbound used instead (with chroot too).
- PF : advanced ruleset (third ruleset) improvements (set prio, route-to, tables, and few simplifications)
- Changelog section added

Updated 2014 November 30
- fixes mistakes, tested and working on OpenBSD 5.6


Follow me @gkweb76


  1. Awesome article, thanks! I currently run IPFire and AirVPN but I was looking at switching to pfSense 2.2 once it's out of testing. Your detailed article has given me another good alternative, though I'm not sure it would work with my 450Mbps wireless N card in access point mode?

    1. Thanks for your comments :-) The best to do is to check that your wireless card chipset is compatible with OpenBSD at You can also check the 5.5 "What's new" page and see if your wireless card chipset is mentioned.


  2. Best practice is to use visudo to edit the sudoers file.

  3. OpenDNS:
    - is a US company
    - logs everything

    1. If one does not trust OpenDNS, it is possible to keep using DNSCrypt by pointing to other DNS servers which are "DNSCrypt enabled", a list is available at :
      It requires to modify the /etc/rc.conf.local, and to change only the last parameter after "-R", for instance to use a logless Denmark DNS server :
      # Start DNScrypt proxy, using a logless EU Denmark DNS server
      /usr/local/sbin/dnscrypt-proxy -a -u _dnscrypt-proxy -l /dev/null -d -R

      Also, a modification of the watchdog script (if used) is required, as the test "dig txt" does not work anymore to check that DNS requests are encrypted.

      That is a good advice, I will update the article to include such comment.


  4. Great article! I have a question. How do assign a hostname to the re0 interface and setup DNS for it?

    1. Thanks. The hostname can be changed in /etc/myname. DNS is well covered in this article. You can check the official FAQ too at


  5. Salut Guillaume

    Thanks for your contribution to further the use of OpenBSD.

    I have problems trying to adapt your write-up on "Firewall" to my needs and hope that you can help me.

    I, too, am a customer of AirVPN and it provides config files of many servers located in multiple countries. How do I incorporate them into your firewall pf rules so that I can switch from one AirVPN gateway to another without problems? Can your firewall pf rules be modified to accommodate such an action?

    Secondly I do not have a separate machine to do my VPN externally. You see I installed OpenBSD on my notebook computer and use it as a desktop OS. I connect to AirVPN's various servers using this desktop OS.

    In summary I wish to have a set of firewall rules to do the following:

    1. Open a terminal in OpenBSD (let's call it T-1), cd to the directory where AirVPN config files are located, choose a config file that has a server, for example, in Poland, and type the command sudo openvpn --verb 3 --config

    2. When the VPN connection is established, I type a command in a separate terminal (let's call it T-2) to launch your firewall rules so that all browser traffic, FTP traffic, ping traffic will go through the VPN tunnel.

    3. In a third terminal, T-3, I launch a web browser and am able to surf the internet.

    4. I go back to T-1 and press Ctrl+C simultaneously to terminate the VPN connection.

    5. I try to use the web browser to surf to new websites and discover that I am unable to. I am also unable to ping websites or do FTP. (It shows that your firewall rules work to prevent your web browser from connecting to the internet when the VPN connection is terminated abruptly.)

    6. I go back to T-2 terminal and type a command to clear your firewall rules.

    7. In T-3 terminal, I am able to surf the internet using a web browser. This time, however, I am not using the VPN connection.

    Guillaume, I would appreciate it very much if you could enhance your post on firewall rules to take into account what I describe in the preceding paragraphs.

    1. Hello,

      The article's firewall ruleset version 2 already enables you to connect to any VPN servers, it is not restricted to a particular IP. VPN IP addresses are specified in the Watchdog script but it's not necessary to use it. You do not need to reload the firewall when you connect to another VPN server. If it's not working, something is probably not right in your ruleset.

      As I do not have your specific setup (OpenBSD laptop with VPN), I prefer being cautious about giving you untested advices, without seing your ruleset and configuration. May be you can jump on and post your entire ruleset and configuration?


    2. Salut mon ami et un grand merci pour ta réponse si rapide.

      Regarding the Watchdog script: can it be modified for use on an OpenBSD (desktop) OS? As I had written in my earlier post, I do not have a separate machine for use as an OpenBSD router and firewall.

      You wrote: "VPN IP addresses are specified in the Watchdog script but it's not necessary to use it."
      -- Just so that I understood correctly, your firewall rules are already sufficient to prevent my web browser from surfing when the VPN connection is terminated abruptly

      Regarding your statement "As I do not have your specific setup (OpenBSD laptop with VPN)", what specific setup do you need so that you can help me and other people in my situation?

      You suggested that I post my ruleset and configuration on But my questions on firewall pf rules are specific to OpenBSD. Will that forum accept my posts?


    3. My bad, the forum I should have directed you is

      For the firewall ruleset, it is easy to check by yourself you can or cannot surf anymore when VPN is disconnected, as you described in your procedure (from 1 to 7). On an OpenBSD router, the above ruleset does just that.

      Regarding your other questions, this article has been entirely based on an external router. Doing it on your laptop may or may not work, you are free to try, but I won't be able to help for your specific setup (I need hardware and time to thouroughly test such setup and report back). If one day I buy a laptop I may try your setup and publish a new article.

      In the meantime, I wish you all the best !



  6. Maybe I don't understand, but wouldn't running a VPN from your router just allow your VPN provider free access into your internal network? Is anyone running a separate hardware firewall behind their VPN router to prevent VPN provider intrusion into the network?

    1. Technically, if you do not enable port-forwarding on your VPN, no unsollicited traffic can come to you. In the end you have to trust someone. If you don't trust your ISP and your VPN provider, the only solution is to stay disconnected. It's your VPN provider business to keep your connection private and safe, doing network intrusion into their clients networks would be suicide. However, as they could theoretically be hacked, to then attack the clients, a defense in depth approach is always a good thing : as you say having the VPN connection done on another hardware, or adding an intrusion detection system like Snort on the LAN side.


  7. What computer did you use as your router?

    1. From the article: "I personally bought as computer a Shuttle DS437, which is a fanless and noiseless small case [...]network card chipset RTL8111G,".


    2. My english not so good. Many apologies.

      I meant to say that with the shuttle, are you happy with the performance?

      Do you regret that choice or any others?

      What is max lan to wan throughput?

      Since you have everything coming into one port, are you doing routing on the shuttle or on a 1gbe switch?

      Is there some problems or issues you wish you had done differently?

      Also, your diagram shows the wifi outside the firewall. Was this intenstionsl?

      Thank you and sorry for not writing words better. Great site.

    3. I'm happy with the Shutlle, however I cannot talk about performance as there is only two computers on the LAN side, and I have a 10Mbps Internet connection. The router has two network plugs/ports, so on ethernet cable plugged into the ISP router, and one plugged into an internal switch (LAN). All the routing is done on the Shuttle. The only thing I would improve would be to have a router with Intel NIC chipset instead. My diagram shows a random pic found to designate a router, mines (ISP + OpenBSD) have their WIFI disabled and WIFI card removed for the Shuttle.


    4. The only ones i have found with intel nics are soekelis and they are way overpriced for their specs. The only one I found that looks good is the apu1d but that also has realtek nics. which apparently suffer from throughput issues. Do you know of any others that are comparable to youir setup? also, isn't the shuttle mainly used for onscreen displays at tradeshows... hence the vesa mount.

    5. Any computer having two network interfaces should do the job. I had a hard time finding a mini computer as most, with a reasonable price, only have one interface. The Shuttle can indeed be used for something else than a router obviously. You can for instance install a Linux/FreeBSD desktop on it, mount it behind a screen, and use it as a discreet low energy desktop computer.


  8. I saw from Shuttle spec ( that DS437 has a single-Chip 1T1R WLAN Controller Realtek RTL8188CE wireless lan card inside. Does it work under openbsd?

    Is a driver being attached to this wireless card? Please provide the output of pciconf -lbcev.

    Thank you in advance, great article!

    1. I cannot answer you about the wireless card as I physically removed it. You should check on OpenBSD website for hardware compatibility:


  9. I've redirected stderr to /dev/null in tcpdump because of a warning in the scripts:
    tcpdump -enr /var/log/pflog 2>/dev/null
    And it could be better to use "mktemp" to create the text files in the scripts and remove them before exiting the script.

    1. I think your made a comment for another article, probably "Be your own provider with OpenBSD"

      The script is provided for OpenBSD 5.6 release, and with proper input for the variables at the begining of the script, there should be no error (interface and label). Of course the label should match the one used in pf. You can improve the script as you wish, it was a quick write :-)


  10. The barebone ShuttleDS437 has two network interfaces.Did you make a hotspot of the device?
    If so why didn't you use freeradius accounting? Or is radius accounting wpa2-enterprise security for connected wireless devices not necesary?

  11. Hi Guillaume !
    Thank you for this tutorial.
    #1 I have a OVH VPS, any comments from you on ovh? secure?
    #2 I tried BSDNow's ultimate OpenBSD router tutorial with 5.8, great!
    #3 Do you have a internet VPN provider recommendation?
    Thank you!

    1. Hi,

      I have no idea of OVH security, although generally OVH is well known and appreciated in France. About VPN provider choice, I liked AirVPN when I was not using my own VPN VPS server.