← Back to All Writeups

Infrastructure Hardening in OpenBSD with Packet Filter (PF) and Pledge/Unveil

OpenBSD remains the gold standard in architectural simplicity, audited codebases, and deterministic UNIX systems. In this note, we detail essential guidelines for hardening an internet-exposed host and serving static web content with a zero attack surface.

1. Principle of Isolation & Least Privilege

OpenBSD’s architecture does not rely on fragile abstraction layers. Its security model is built on native kernel primitives:

  • pledge(2): Restricts the system calls a process can make once initialized.
  • unveil(2): Restricts file system visibility strictly to explicitly declared paths.
  • W^X (Write XOR Execute): Strict memory protections preventing unauthorized code execution by default.
+-------------------------------------------------------------+
|                      OpenBSD Host                           |
|  +---------------------+        +-------------------------+ |
|  |     relayd(8)       |  --->  |        httpd(8)         | |
|  |  (TLS Termination)  |        | (pledge: rpath, inet)   | |
|  +---------------------+        +-------------------------+ |
|            ^                                 |              |
|            | PF stateful inspection          v              |
|      [ Public WAN ]                /var/www/htdocs          |
+-------------------------------------------------------------+

2. Packet Filter Configuration (/etc/pf.conf)

A strict default-deny policy (block return all) combined with stateful inspection and rate-limiting against brute force attempts:

# /etc/pf.conf - OpenBSD Edge Host Ruleset
ext_if = "vio0"

# Global normalization
set skip on lo
set block-policy drop
set loginterface $ext_if

# Packet scrubbing to prevent evasion via fragmentation
match in all scrub (no-df random-id max-mss 1440)

# Default: Drop all unsolicited ingress/egress traffic
block drop in log all
block return out all

# Allow essential egress (DNS, NTP, HTTPS for package updates)
pass out quick on $ext_if proto udp to any port { domain, ntp } keep state
pass out quick on $ext_if proto tcp to any port { http, https, domain } keep state

# Controlled ICMP (Required for Path MTU Discovery)
pass in quick on $ext_if inet proto icmp icmp-type { echoreq, unreach, timex } keep state

# SSH with aggressive rate limiting
table <bruteforce> persist
block quick from <bruteforce>
pass in quick on $ext_if proto tcp to ($ext_if) port 22 \
    flags S/SA keep state \
    (max-src-conn 10, max-src-conn-rate 3/60, overload <bruteforce> flush global)

# Public HTTP/HTTPS traffic
pass in quick on $ext_if proto tcp to ($ext_if) port { 80, 443 } keep state

3. Native httpd(8) Configuration

OpenBSD’s native web daemon runs in a chroot jail under /var/www by design:

# /etc/httpd.conf
server "valmis.net" {
    listen on * port 80
    root "/htdocs/valmis.net"

    hsts {
        preload
        subdomains
        max-age 31536000
    }
}

4. Kernel sysctl Hardening (/etc/sysctl.conf)

# /etc/sysctl.conf
net.inet.ip.forwarding=0
net.inet.ip.redirect=0
net.inet.ip.sourceroute=0
net.inet.tcp.drop_synfin=1
net.inet.tcp.always_keepalive=1
net.inet.tcp.syn_use_2msl=1

Summary

Combining a Go-compiled Static Site Generator + OpenBSD httpd(8) + PF eliminates modern web vulnerabilities by design. Zero dynamic runtimes, zero databases, and zero unnecessary privileges.