OpenBSD router

1 Jan

The ultimate OpenBSD router


Live demo in BSD Now Episode 011 and Episode 013.

“Friends don’t let friends use consumer networking equipment.”

This is a saying that many sysadmins and BSD/UNIX fans have probably heard. It’s really easy to go to a store and pick up a cheap little plastic router, but you might regret it later. They’re proprietary, have security issues and offer very little flexibility. Often times, these routers are severely limited in functionality because they’re aimed at consumers who aren’t very tech-savvy. We’re going to show you how to build your own, and take back control of your network! Note that this will only be a wired router, not a WiFi access point. That may come at a later time.

Hardware and Introduction

This is a list of hardware I’ll be using (although nothing in this tutorial is specific to it).

Buy whatever hardware you want, just make sure the network cards are supported beforehand. The Soekris board I chose uses Intel NICs that are known to have good BSD support.

I should probably define exactly what a router is, since everyone has different requirements. For mine, I want it to basically be a drop-in replacement for those cheap consumer ones you get. I’ve got three computers that need to share my cable internet connection. One of them is a FreeBSD server that I’d like to be able to SSH into remotely, but I don’t want any other inbound connections to pass through. That means my router will be doing the following things:

  • Performing Network Address Translation
  • Giving my server “meimei” and my laptop “suigintou” static IP addresses
  • Handing out local IP addresses via DHCP to everyone else
  • Doing local DNS caching for the LAN
  • Allowing incoming SSH connections to my server
  • Allowing incoming SSH connections to the router (for maintenance, can be disabled later)
  • Using only IPv4
  • Automatically emailing me when there’s an update available
  • Optionally, redirecting all of my traffic (or just certain parts) through Tor or a VPN
  • Not being a pain in the ass to set up.. for the reader at least

This ultimate router will be running nothing more than OpenBSD. Everything I’m using is included in the base system unless otherwise noted. Pretty cool! The same setup can be done with FreeBSD, but you’ll need to use the older pf syntax and a DHCP server from ports. If you’re using FreeBSD, the files will also be in a different location in some cases. This guide is written purely for OpenBSD. On their website, they have a little write-up about setting up a home gateway. It will provide a foundation for this tutorial’s pf config file.

I’m going to assume you’re capable of installing the OS on your machine and have a working internet connection. If you want a fully-encrypted installation, see our tutorial for that. If you have a serial cable, install it that way. I didn’t have one, so I installed the OS on the USB drive from another computer, then put it in the Soekris box and it booted. I used the whole device as one partition and installed to that. I let DHCP on the modem configure my first network interface and left the rest of them untouched.

The hardware I chose has four NICs. They show up in the OS as em0, em1, em2 and em3. I’m going to be using the first one as the external interface and the other three as the internal interfaces for the LAN. If you need more, I’d recommend getting a system with more NICs or picking up a cheap gigabit switch. The Soekris I’m using also has a PCIe x1 slot that can be used for expansion.

Filesystem and Network Configuration

So, let’s get a shell and start working. You can use the serial console, SSH into the router from another system or just do this all on another computer booted from the USB drive (swapping it into the Soekris when done). First of all, since I’m using a flash drive for the OS, I want to minimize the number of writes to it. I’ll append the “noatime” flag to the mount point and enable soft updates. You may also want to consider using mfs for /var and /tmp.

# vi /etc/fstab

Assuming my root device is “sd0” (yours might not be) my fstab will look like this:

/dev/sd0a / ffs rw,noatime,softdep 1 1

You’ll need to reboot for this to take effect, but let’s get everything in place before we do that. Next, let’s add a few network settings so we can bridge your internal interfaces with the virtual vether interface.

# echo up > /etc/hostname.em1
# echo up > /etc/hostname.em2
# echo up > /etc/hostname.em3
# echo 'inet' > /etc/hostname.vether0
# vi /etc/hostname.bridge0

Add the following:

add vether0
add em1
add em2
add em3
blocknonip em1
blocknonip em2
blocknonip em3

We need to enable IP forwarding.

# echo 'net.inet.ip.forwarding=1' >> /etc/sysctl.conf

Now OpenBSD can pass traffic through the interfaces when needed.


Next we tell the DHCP server to start on boot, but we’ll wait until we have a valid config file in place before we actually start it. Let’s also configure OpenNTPD so that our router’s time will always be correct.

# echo 'dhcpd_flags="vether0"' >> /etc/rc.conf.local

If you aren’t using DNSCrypt (more on that in a minute), run

# echo 'ntpd_flags="-s"' >> /etc/rc.conf.local

But otherwise, we have to start ntpd another way. We’ll get into that later. Now we configure the DHCP server:

# mv /etc/dhcpd.conf /etc/dhcpd.conf-original
# vi /etc/dhcpd.conf

This is what I use:

option domain-name-servers;
subnet netmask {
    option routers;
    host meimei {
        hardware ethernet 00:00:00:00:00:00;
    host suigintou {
        hardware ethernet 11:11:11:11:11:11;

You can specify any IP address range you want to use and any DNS servers you want to use. By default, I want all clients to query my local DNS resolver first. This will speed up repeated lookups and is handy to have. Use the MAC addresses of your computers if you want static IPs.

Packet Filtering

The centerpiece of this entire guide is the file /etc/pf.conf. That’s where all the firewall rules will be going, and can be customized in many different ways. If you’d like to really learn pf, I recommend picking up a copy of The Book of PF. I’ve read it all the way through and it’s a really great book. There’s also a nice collection of slides you can look through.

# mv /etc/pf.conf /etc/pf.conf-original
# vi /etc/pf.conf

As mentioned before, there are lots of resources online for learning the pf syntax. For my purposes, I ended up with something like this:

int_if="vether0 em1 em2 em3"
set block-policy drop
set loginterface egress
set skip on lo0
match in all scrub (no-df)
match out on egress inet from !(egress:network) to any nat-to (egress:0)
antispoof quick for egress
block in quick on egress from urpf-failed
block in quick from no-route to any
block in quick inet6 all
block out quick inet6 all
block in all
block out log quick on egress proto udp from any to any port 53
pass out quick inet keep state
pass in on egress inet proto tcp to (egress) port 222 rdr-to
pass in on egress inet proto tcp from any to (egress) port 2222 flags S/SA synproxy state
pass in on { $int_if }

This example is assuming you want DNSCrypt enabled, which we will discuss in the next section. If you aren’t using it, remove the line

block out log quick on egress proto udp from any to any port 53

or all of your DNS lookups will fail! You can download a commented version of the config here. My router will be running SSH on port 2222 and the server will be running SSH on port 222, both open to the internet. Check /etc/ssh/sshd_config for more options. Running sshd on an alternative port, while not really providing any additional security, will cut down the rogue login attempts. You definitely don’t want your USB flash drive being worn out from /var/log filling up with Chinese IPs. Modify the “ListenAddress” line of sshd_config if you only want the router’s sshd listening for connections on the LAN. See the pf.conf man page linked above if you want to learn more about each of the rules in more detail.

DNS Caching

Setting up a local DNS caching server is very easy. We’ll be using BIND, which is included in the OpenBSD base system as well. In the future, it will probably be replaced by unbound. The default configuration for named is mostly fine, but let’s change one bit so that it only accepts connections from the LAN and forwards my requests to a real DNS server.

# echo 'named_flags=""' >> /etc/rc.conf.local
# vi /var/named/etc/named.conf

I’m going to use OpenDNS as my forwarder, but you can change it to any DNS server you want. Here’s a diff to show what I changed:

--- named.conf  Tue Jul 30 13:52:23 2013
+++ named.conf  Thu Nov 28 16:32:57 2013
@@ -10,15 +10,14 @@
 // the IPv6 localhost address.
 acl clients {
-   localnets;
-   ::1;

 options {
    version ""; // remove this to allow version queries

    listen-on    { any; };
-   listen-on-v6 { any; };
+   forwarders {; };

    empty-zones-enable yes;

If you want to use DNSCrypt to encrypt your DNS lookups, there are a few more steps involved. The above diff will look more like this:

--- named.conf  2013-12-31 16:37:09.000000000 -0500
+++ named.conf  2013-12-31 16:40:15.000000000 -0500
@@ -10,15 +10,15 @@
 // the IPv6 localhost address.
 acl clients {
-   localnets;
-   ::1;

 options {
    version ""; // remove this to allow version queries

+   forward only ;
+   forwarders { port 40 ;} ;
    listen-on    { any; };
-   listen-on-v6 { any; };

    empty-zones-enable yes;

Then we’ll install dnscrypt-proxy. Unfortunately, it’s not part of the base system, so we’ll need to install it from ports or packages.

# export PKG_PATH=`uname -r`/packages/`uname -m`/
# pkg_add dnscrypt-proxy
# echo "/usr/local/sbin/dnscrypt-proxy -a -d" >> /etc/rc.local
# echo "nameserver" > /etc/resolv.conf
# chflags schg /etc/resolv.conf

Since ntpd needs DNS to query time servers, it has to start after dnscrypt-proxy is running if you want to use the “-s” flag. Here is a workaround for that:

# echo "/usr/sbin/ntpd -s" >> /etc/rc.local

If you don’t need the “-s” flag, and are ok with it syncing your clock gradually over time,

# echo 'ntpd_flags=""' >> /etc/rc.conf.local

is a much “neater” solution than adding it to /etc/rc.local. It depends on how obsessed you are with aesthetics. Finally, reboot to make sure everything works.

# reboot

At this point you should be able to plug in some computers to the other ethernet ports and everything will work. They’ll be assigned IP addresses and granted access to the internet, while being protected by the firewall. If that’s all you want, you can stop reading right here. However, there are some other cool things you can do…


It’s possible to configure the router to send you nightly emails using nothing but the OpenBSD base system and an email account. I realize not everyone likes gmail, but that’s what I am using for this example. I’ve created a throwaway account that will only be used to email logs and other things to my normal email account.

# mv /etc/mailer.conf /etc/mailer.conf-original
# vi /etc/mailer.conf

Add the following:

sendmail     /usr/sbin/smtpctl
send-mail    /usr/sbin/smtpctl
mailq        /usr/sbin/smtpctl
makemap      /usr/libexec/smtpd/makemap
newaliases   /usr/libexec/smtpd/makemap

Next we add the email account you’ll be sending the mail from.

# newaliases
# echo 'gmail' > /etc/mail/secrets
# chown root:_smtpd /etc/mail/secrets
# chmod 640 /etc/mail/secrets
# makemap /etc/mail/secrets

Move the default OpenSMTPD configuration to a backup file and create a new one.

# mv /etc/mail/smtpd.conf /etc/mail/smtpd.conf-original
# vi /etc/mail/smtpd.conf

Add the following:

listen on lo0
table aliases db:/etc/mail/aliases.db
table secrets db:/etc/mail/secrets.db
accept for local deliver to mbox
accept for any relay via tls+auth:// auth <secrets>

Finally, disable sendmail and enable OpenSMTPD by default.

# /etc/rc.d/sendmail stop
# echo 'sendmail_flags=NO' >> /etc/rc.conf.local
# echo 'smtpd_flags=""' >> /etc/rc.conf.local
# /etc/rc.d/smtpd start

Now you should be able to take the output of any command and send it with your email account. We can test it by doing something like this:

# echo "Woah, my router can send emails! Nice tutorials as always dude!" | mail -v

While this is very basic to most admins, the ability to send email from the router is really cool. You can pipe any command or script’s output to an email, send it off and then check what’s going on in the morning. It can be used for firewall logs, automatically checking for updates and patches, really anything you can think of! It’s a phenomenal tool that can be used very creatively. You could even install GPG and encrypt the emails before sending them, especially if you’re not a fan of gmail’s privacy history.

Automatic Security Notifications

Here’s a little script to check for errata (security or “reliability” updates to the OS) every night that I wrote. Before we make it, I’m going to get a checksum of the current errata page for my version of the OS. I don’t claim to be a sed expert, but this works for my needs. If you want to create a new unprivileged user to run the script, that might be a good idea. The automatic source code patching will only work as root, unless you change the permissions of /usr/src. If you don’t want the automatic patching, everything else can be adjusted to run as a normal user. Obviously make sure you actually have the system source code in /usr/src or that part won’t work at all. If you want something less intrusive that simply alerts you that there are patches, check the commented version. You will have to manually check the errata and patch it yourself in that case.

# ftp -V4o -`uname -r | cut -c 1,3`.html | sha256 > /root/checksum
# ftp -V4o /usr/local/bin/erratacheck

The script contains the following (plus comments and more info):

# OpenBSD errata alert script from

sleep `jot -r 1 0 3600` 

UNM=`uname -r`
VER=`uname -r | cut -c 1,3`
CVS=`uname -r | cut -c 1`_`uname -r | cut -c 3`
FETCH=`ftp -V4o - $URL | $SHA`

if [ "$LASTKNOWN" == "$FETCH" ]; then
    echo "Check $URL for rebuilding instructions." > $ERR
    echo "Files affected:" >> $ERR
    cd /usr/src
        if sysctl kern.version | grep -q stable; then
            cvs -q up -rOPENBSD_$CVS -Pd 2>&1 | tee -a $ERR
        if sysctl kern.version | grep -q current; then
            echo "this script is not for -current."
            exit 1
        if sysctl kern.version | grep -q beta; then
            echo "this script is not for -beta."
            exit 1
        ftp -V4$UNM.tar.gz
        tar xzf $UNM.tar.gz
        rm $UNM.tar.gz
        mv $UNM/*/*patch .
        rm -r $UNM
        for f in *patch
            patch -p0 -f < $f 2>&1 | tee -a $ERR
            rm $f
    lynx -dump -nolist $URL | \
    sed -e '1,23d' | sed -e :a -e '$d;N;2,4ba' -e 'P;D' >> $ERR
    cat $ERR | \
    mail -s "New OpenBSD patches for `hostname`" $WATASHI
    rm $ERR
    echo $FETCH > $CHK

Be sure to replace the email variable with your address. Then just put /usr/local/bin/erratacheck in the crontab and make it executable. The script will run at 7:30AM every day. You can change it to any time you want.

# chmod +x /usr/local/bin/erratacheck
# crontab -e

Add a line similar to this one:

30    7    *       *       *       /bin/sh /usr/local/bin/erratacheck

Transparent Tunneling via SSH or Tor

The next concept I’m going to talk about is sending all (or some) traffic through Tor or a VPN. If you wanted all traffic from all your LAN computers to go through either, it’s just a matter of running the service on the router and redirecting the traffic. You might also want to read our stunnel tutorial for more ideas. You can redirect only traffic going to certain hosts or from certain computers – get creative with it!

In this example, I’m going to add a few bits to /etc/pf.conf so that my laptop suigintou (with the IP will have all its external TCP traffic forwarded through an SSH tunnel or Tor.

# vi /etc/pf.conf

We’ll add something like this:

pass in quick on $int_if proto tcp from to egress rdr-to port 9050

On the router itself, we need to set up a tunnel to listen on port 9050. If you have a remote SSH server, you can run something like this (preferably in tmux):

$ ssh -v -ND you@remote_ip

And all traffic headed towards will be sent through the encrypted tunnel. This has the downside (or advantage, depending on how you look at it) of disconnecting you if there’s any problem with the SSH tunnel. You’ll have to rerun it on the router before your traffic can pass again. You can check your external IP by visiting

Port 9050 is also what Tor uses by default, but that’s not included with the base system. If you want a transparent Tor proxy, you’ll have to install it from ports or packages. It’s recommended to run Tor in tmux also.

# export PKG_PATH=`uname -r`/packages/`uname -m`/
# pkg_add -v tor
$ tor

This will produce the same end result as the SSH tunnel, but the traffic will go through the Tor network. You can really do any kind of VPN service with this, provided that it listens on a local address and port. OpenVPN would work nicely with this setup.

Power Saving

You may also want to enable apmd to save power if your hardware supports it. It will scale the CPU down during idle times and turn it up when the load reaches a certain point. Check the man page for a few different options. I’ll be using the “cool running performance adjustment” mode.

# echo 'apmd_flags="-C"' >> /etc/rc.conf.local
# /etc/rc.d/apmd start

You can check what level (with 0 being the lowest, 100 being the highest) the CPU is running at with:

# sysctl hw.setperf

Or force it to use the lowest power setting:

# sysctl hw.setperf=0

Try different levels and apmd settings to find the balance you’re most comfortable with. Always running it on the lowest setting might limit the data throughput too much, but it will really depend on what hardware you’re using.


In closing, I think that OpenBSD makes for a great public-facing firewall. It’s really handy to have all the tools needed in the base system. If you’re not comfortable setting things up yourself via the commandline, pfSense is a good option. FreeBSD, NetBSD or DragonflyBSD would all make excellent routers and firewalls as well. Whatever OS you decide to use, remember to keep it patched and up to date. Otherwise, you end up in a similar situation as you were when you had the consumer crap. OpenBSD does a new release every six months. Consider supporting their work by purchasing cool stuff. Now start replacing routers.

Originally written by TJ for | Last updated: 2013/12/31

Latest News

Christmas & New Year


Episode 16 was just uploaded, and that’s the last one we’ll be doing live for this year. Episode 17 will be on Christmas, and feature a prerecorded interview with Scott Long about his BSD magic over at Netflix.

Thanks for watching everyone! We look forward to more BSD Now in…

Welcome iXsystems


As you may have noticed in Episode 015, BSD Now has gotten our first sponsor!

We’re very happy to welcome iXsystems to the BSD Now team. In case you aren’t familiar with them, they have quite a long history with FreeBSD. Their current CTO is in fact Jordan Hubbard, one of…

Update 2013/11/26


Hi BSD Now fans. Here’s a sneak peek at our upcoming schedule:

11/27: Jordan Hubbard, co-founder of FreeBSD and creator of ports, to talk about FreeBSD’s founding and future. The tutorial will be an update to the OpenBSD router guide with some new improvements I’ve made.
12/04: George Wilson from Delphix…

Reflections on Podcasting


tl;dr → This is not a technical news post, it’s just my bantering about the show. You can skip it entirely if you’re not interested. Usually I post news from the whole team, but this time it’s just me.

The first episode of BSD Now was scheduled to be recorded on…

Episode 018: Eclipsing Binaries


Direct Download:

Video | HD Video | MP3 Audio | OGG Audio | Torrent


Faces of FreeBSD continues

Our first one details Shteryana Shopova, the local organizer for EuroBSDCon 2014 in Sophia
Gives some information about how she got into BSD
“I installed FreeBSD on my laptop, alongside the Windows and Slackware Linux I was…

Episode 017: The Gift of Giving


Direct Download:

Video | HD Video | MP3 Audio | OGG Audio | Torrent

This episode was brought to you by

Interview – Scott Long –

FreeBSD at Netflix, OpenConnect, network performance, various topics

All the tutorials are posted in their entirety at
Send questions, comments, show ideas/topics, or stories you want mentioned on…

Episode 016: Cryptocrystalline


Direct Download:

Video | HD Video | MP3 Audio | OGG Audio | Torrent


Secure communications with OpenBSD and OpenVPN

Starting off today’s theme of encryption…
A new blog series about combining OpenBSD and OpenVPN to secure your internet traffic
Part 1 covers installing OpenBSD with full disk encryption (which we’ll be doing later on…

Episode 015: Kickin’ NAS


Direct Download:

Video | HD Video | MP3 Audio | OGG Audio | Torrent


More faces of FreeBSD

Another installment of the FoF series
This time they talk with Reid Linnemann who works at Spectra Logic
Gives a history of all the different jobs he’s done, all the programming languages he knows
Mentions how he first…

var _gaq = _gaq || [];
_gaq.push([‘_setAccount’, ‘UA-43804335-1’]);
_gaq.push([‘_setDomainName’, ‘’]);

(function() {
var ga = document.createElement(‘script’); ga.type = ‘text/javascript’; ga.async = true;
ga.src = (‘https:’ == document.location.protocol ? ‘https://ssl&#8217; : ‘http://www&#8217;) + ‘’;
var s = document.getElementsByTagName(‘script’)[0]; s.parentNode.insertBefore(ga, s);

Tinggalkan Balasan

Isikan data di bawah atau klik salah satu ikon untuk log in:


You are commenting using your account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s

%d blogger menyukai ini: