Thoughts on OpenBSD!
OpenBSD in my opinion is one of the best home/server OS. It's secure, robust and POSIX compliant (to a very large extent). Yes, it does lack RBAC (SELinux-like) and extremely complex ACLs (miss you systrace). But it really does a great job at locking the system down and making sure any code/binary shipped by the developers is almost guaranteed to be bug-free.
Again I would like to reiterate that it is not the perfect OS. If it wants mass adoption in any sense (specially enterprise), there are a few things I feel that can be done better.
First I believe either SELinux or AppArmour (like) systems need to be introduced in the OS because security at its heart is a multi-layered control approach (cyber resiliency). In modern times with 0day exploits coming out like marvel movies you cannot claim that "I won't be hacked, cause my source code is hack-proof and I stop memory issues." . Even programs written in Rust have been proved to have various kinds of issues. and then there is the issue of stupid users.
Another simple improvement that can be done is introducing Rust/Zig as the de-facto language, going forward all new systems/applications build for the OS should be written in Rust or Zig. I know the baggage of C is hard to let go and it will take time, maybe a huge number of components are never re-written in Rust, but the point is not to achieve 100% success. There is no 100% success in security and we can't be afraid of change just because C/C++ has ruled the world till now.
Now, let's see the good things,
The PF firewall in OpenBSD is hands down one of its most brilliant features. It's not just a firewall; it's a Swiss Army knife for network security. It handles stateful inspection with ease, supports adaptive rulesets, and even scales effortlessly across multiple CPUs. But what makes PF truly shine is its flexibility—you can do so much more than just basic packet filtering.
For example, you can build a custom Web Application Firewall (WAF) using PF. By writing precise rulesets to inspect HTTP traffic, block malicious patterns, and integrate with intrusion detection systems like Snort or Suricata, you can protect your web applications without relying on heavyweight external tools.
DDoS protection? PF has your back there too. With features like max-src-conn-rate and overload tables, you can automatically detect and block abusive connections. Combine this with ALTQ for traffic shaping, and you can throttle malicious traffic while ensuring legitimate users get a smooth experience—even during an attack.
It doesn’t stop there. PF lets you implement port knocking for stealthy access control, where a sequence of connection attempts to specific ports acts like a "secret handshake" for trusted users. This adds an extra layer of protection for sensitive services.
Want even deeper control? PF integrates seamlessly with OpenBSD’s packet-capture tools like tcpdump, letting you monitor traffic in real-time and script automated responses to anomalies.
How to Write PF Rules:
PF rules are configured in the /etc/pf.conf file. The syntax and logic are straightforward but highly expressive. Below is a breakdown of common rule types and examples.
Basic Syntax
action [direction] on [interface] proto [protocol] from [source] to [destination] [options]
A simple example would be:
block in all # Block all incoming traffic by default
pass out all # Allow all outgoing traffic
if you want to allow certain services:
pass in on em0 proto tcp from any to any port 22 # Allow SSH
block in on em0 proto tcp from any to any port 80 # Block HTTP
if you want state:
pass in on em0 proto tcp from any to any port 80 keep state
if you want to stop DDoS(here this is only for 80 TCP, you may do this for any port):
table <abusers> persist
block in quick from <abusers>
pass in on em0 proto tcp from any to any port 80 max-src-conn-rate 100/5 overload <abusers>
Shape certain traffic?
altq on em0 bandwidth 100Mb hfsc queue { web traffic }
queue web bandwidth 50% priority 5
pass out on em0 proto tcp to any port 80 queue web
SSH with Port knocking?
pass in on em0 proto tcp from any port 12345 keep state
block in on em0 proto tcp from any port 22
All of this is achieved while staying true to OpenBSD’s philosophy.
What else can we use to take advantage of OpenBSD?
- Unveil/Pledge - Restrict system calls/file system
- Sysctl Settings - Kernel-level tunables for enhanced security
Sysctl seems to be most interesting. These flags allow you to fine-tune the kernel’s behavior to harden your system against various attacks.
Below are some flags I always like to set:
| Flag | Default | Recommended | Description |
|---|---|---|---|
kern.securelevel | -1 | 2 | Sets system protection level, restricting kernel modifications. |
hw.smt | 1 | 0 | Disables Simultaneous Multithreading to mitigate side-channel attacks. |
kern.allowkmem | 1 | 0 | Restricts access to /dev/kmem and /dev/mem to prevent debugging exploits. |
| Flag | Default | Recommended | Description |
|---|---|---|---|
vm.swapencrypt.enable | 1 | 1 (unchanged) | Encrypts data written to swap space for sensitive data protection. |
| Flag | Default | Recommended | Description |
|---|---|---|---|
net.inet.ip.forwarding | 0 | 0 (unchanged) | Disables IP forwarding to reduce exposure to network attacks. |
net.inet.tcp.rfc1323 | 1 | 0 | Disables TCP timestamps to prevent timing attacks. |
net.inet.icmp.bmcastecho | 1 | 0 | Disables ICMP broadcast echo requests to prevent amplification attacks. |
| Flag | Default | Recommended | Description |
|---|---|---|---|
fs.posix.setuid | 1 | 0 | Restricts setuid binaries to prevent privilege escalation. |
Unveil/Pledge:
For Unveil/Pledge, I think the below example sums up its capability very nicely.
#include <unistd.h>
#include <stdio.h>
#include <err.h>
int main(void) {
// Unveil: Restrict file system access to only allow reading "/etc/example.txt"
if (unveil("/etc/example.txt", "r") == -1) {
err(1, "unveil failed");
}
// Lock the unveil configuration
if (unveil(NULL, NULL) == -1) {
err(1, "unveil lock failed");
}
// Pledge: Restrict program to only use standard I/O and file reading
if (pledge("stdio rpath", NULL) == -1) {
err(1, "pledge failed");
}
// Try to open and read the file
FILE *file = fopen("/etc/example.txt", "r");
if (!file) {
err(1, "failed to open file");
}
char buffer[128];
while (fgets(buffer, sizeof(buffer), file)) {
printf("%s", buffer);
}
fclose(file);
return 0;
}
In the above, Unveil is used to restrict file system access to /etc/example.txt in read-only mode, and once locked, no other paths can be accessed. Pledge further limits the program to only stdio and rpath system calls, ensuring it cannot perform operations beyond its intended purpose. Together, they form a simple but powerful way to confine applications.
There are some other system-level changes done by the OpenBSD team , if you are interested about lerning more go here and here.
That is it!, hope you like my thoughts on OpenBSD!, it was really fun exploring this awesome OS.
