Back to blog

Why I Prefer OpenBSD

Security through simplicity. A novel methodology that I explore, and defend in this blog post.

Post

Introduction

As an aspiring security professional learning about the wonder that is OpenBSD opened my eyes to a new way to secure systems. Security through simplicity. As complexity grows, the first thing you'll notice about modern frameworks is the CVE number1. Jokes aside, in this post I defend my reasoning for admiring simplicity over complexity, reduced attack surface over bloated dependency chains, and why I love OpenBSD.

Why OpenBSD?

You might be asking yourself, why on Earth would I ever need to use OpenBSD when I can use something like Windows Server, or Linux? Don't get me wrong those are great too. Most of my servers run Linux, but if you're anything like me and want your mission-critical software to just work causing as little headaches as possible - OpenBSD might just be that precious gem hiding in a sea of supply-chain attacks, dependency hell (trust me, I know.), and vibecoded slop with 30+ vulnerabilities that will likely never be patched.

Simplicity to the Rescue!

For those who either don't know, or haven't heard of OpenBSD. Here is a brief summary.

OpenBSD is a free, Unix-like operating system started in 1995 by Theo de Raadt. It's built around a simple philosophy; security, correctness, and simplicity above all else. Regular code audits, built-in cryptography, and a relentless focus on doing less but doing it right are what sets it apart from the crowd.2
Another fun fact I enjoy about OpenBSD is it is a Canadian project (I, myself am Canadian). Okay, now I will dive into why I love this operating system!

Need a firewall, DHCP server, HTTP server, or even a load balancer? OpenBSD has you covered

Firewalls are stupid simple in OpenBSD with the glorious pf syntax. Look at this lovely ruleset for a small LAN behind a NAT. So simple, and secure. :)

# /etc/pf.conf - minimal router/firewall with NAT for a LAN

# Interfaces and networks
lan_if = "re1"
lan_net = "10.0.0.0/24"

# Non-routable and reserved address space
table  { \
  0.0.0.0/8, 10.0.0.0/8, 127.0.0.0/8, 169.254.0.0/16, \
  172.16.0.0/12, 192.0.0.0/24, 192.0.2.0/24, 198.18.0.0/15, \
  198.51.100.0/24, 203.0.113.0/24, 224.0.0.0/3, 192.168.0.0/16 \
}

# Default behaviors
set block-policy drop
set loginterface egress
set skip on lo

# Normalize traffic
match in all scrub (no-df random-id max-mss 1440)

# NAT for LAN to the current egress address
match out on egress inet from $lan_net to any nat-to (egress:0)

# Anti-spoofing
antispoof quick for { egress, $lan_if }
block in  quick on egress from  to any
block return out quick on egress from any to 

# Default deny
block all

# Allow established and related traffic
pass out on egress inet keep state

# Allow LAN to anywhere
pass in on $lan_if inet from $lan_net to any keep state

To reload pf configuration we use:

pfctl -f /etc/pf.conf

DHCP servers are even easier to spin up.

# /etc/dhcpd.conf
subnet 10.0.0.0 netmask 255.255.255.0 {
  option routers 10.0.0.1;
  option domain-name-servers 10.0.0.1;
  range 10.0.0.51 10.0.0.250;
}

To enable dhcpd service with our new configuration we use the following commands as root:

# rcctl enable dhcpd

# rcctl start dhcpd

Wanna run your own self-hosted, recursive, caching DNS resolver? Of course, OpenBSD has that too. Baked into the default install is unbound. No external packages, or dependencies required.

# /var/unbound/etc/unbound.conf
server:
    # If no logfile is specified, syslog is used
    # logfile: "/var/log/unbound/unbound.log"
    verbosity: 0

    interface: 127.0.0.1
    interface: 10.0.0.1
    interface: ::1
    port: 53
    do-ip4: yes
    do-udp: yes
    do-tcp: yes

    access-control: 10.0.0.0/24 allow
    access-control: 127.0.0.0/8 allow
    access-control: ::1 allow
    access-control: 0.0.0.0/0 refuse
    access-control: ::0/0 refuse
    
    # May be set to no if you don't have IPv6 connectivity
    do-ip6: yes

    # You want to leave this to no unless you have *native* IPv6. With 6to4 and
    # Terredo tunnels your web browser should favor IPv4 for the same reasons
    prefer-ip6: no

    # Use this only when you downloaded the list of primary root servers!
    # If you use the default dns-root-data package, unbound will find it automatically
    #root-hints: "/var/lib/unbound/root.hints"

    # Trust glue only if it is within the server's authority
    harden-glue: yes

    # Require DNSSEC data for trust-anchored zones, if such data is absent, the zone becomes BOGUS
    harden-dnssec-stripped: yes

    # Don't use Capitalization randomization as it known to cause DNSSEC issues sometimes
    # see https://discourse.pi-hole.net/t/unbound-stubby-or-dnscrypt-proxy/9378 for further details
    use-caps-for-id: no

    # Reduce EDNS reassembly buffer size.
    edns-buffer-size: 1232

    # Perform prefetching of close to expired message cache entries
    # This only applies to domains that have been frequently queried
    prefetch: yes

    # One thread should be sufficient, can be increased on beefy machines. In reality for most users running on small networks or on a single machine, it should be unnecessary to seek performance enhancement by increasing num-threads above 1.
    num-threads: 1

    # Ensure kernel buffer is large enough to not lose messages in traffic spikes
    so-rcvbuf: 1m

    # Ensure privacy of local IP ranges
    private-address: 192.168.0.0/16
    private-address: 169.254.0.0/16
    private-address: 172.16.0.0/12
    private-address: 10.0.0.0/8
    private-address: fd00::/8
    private-address: fe80::/10

    # Ensure no reverse queries to non-public IP ranges (RFC6303 4.2)
    private-address: 192.0.2.0/24
    private-address: 198.51.100.0/24
    private-address: 203.0.113.0/24
    private-address: 255.255.255.255/32
    private-address: 2001:db8::/32

To enable our new DNS resolver we use the following command, similar to the one used for our DHCP server. See man rcctl for more information not covered in this post.

OpenBSD also teaches you how to effectively read and use documentation.

# rcctl enable unbound

# rcctl start unbound

Wrapping Up

These are just some of the reasons I enjoy using OpenBSD. It really is a versatile operating system, and the peace of mind knowing it is secure by default is just the cherry on top. OpenBSD isn't for everyone, but then again, neither is reading the manual. If you've made it this far, you might just be one of us.

Sources

  1. NPM Security Vulnerabilities
  2. OpenBSD — Wikipedia
  3. OpenBSD PF: Building a Router
  4. Build a Simple Router and Firewall
  5. rcctl (8) - OpenBSD manual pages
  6. unbound.conf (5) - OpenBSD manual pages
  7. dhcpd.conf (5) - OpenBSD manual pages