OpenBSD's reputation for being "secure by default" leads some teams to deploy it as a firewall or bastion and then never touch it again, yet the defaults only cover what the project ships: the default pf ruleset passes traffic, only the two most recent releases get security fixes, and every service, doas rule and SSH key an administrator adds is outside the project's guarantee. This guide explains what OpenBSD actually does for you (code auditing, exploit mitigations, privilege separation, pledge and unveil) and what you still have to do: privileged access with doas, pf policy, patching with syspatch, securelevel, logging and the daily security report. It is for administrators and security engineers running OpenBSD as firewalls, routers, VPN gateways, jump hosts, mail or web servers. Facts are from the OpenBSD project's own pages and manual pages, checked 8 November 2026, when OpenBSD 8.0 (released 20 October 2026) and 7.9 were the supported releases. Compare the approach with the FreeBSD Security Hardening Guide.
This is an independent guide from NHI Mgmt Group, based on the OpenBSD project's public documentation as of November 2026. It is not sponsored or reviewed by the OpenBSD project. Product features change, so confirm settings against the OpenBSD project's current documentation.
Key takeaways
- Secure by default means few services, not a locked firewall. OpenBSD disables all non-essential services, but the shipped
/etc/pf.confends inpass; write a default-deny policy yourself. - Stay on a supported release. Only the two most recent releases receive base-system security and reliability fixes; a new release appears roughly every six months, so an untouched box falls out of support within about a year.
- pledge and unveil shrink what a compromised program can do. pledge limits system calls to declared promise groups and kills the process on violation; unveil hides the filesystem except named paths. Base daemons use them; your own software should too.
- doas is the privilege boundary. Rules are evaluated last-match-wins and deny by default; scope them to a group, a target user and an absolute command path.
- Use the built-in evidence. The daily security(8) report, pf logging, authlog and lastcomm flags for pledge and unveil violations give detection without extra agents.
How identity and access work in OpenBSD
OpenBSD uses classic Unix identities: users and groups in /etc/master.passwd and /etc/group, with password hashes using bcrypt (an OpenBSD innovation). Base daemons run under dedicated unprivileged users (prefixed with an underscore, such as the _pbuild user the default pf ruleset blocks from the network) and many use privilege separation, splitting a small privileged parent from an unprivileged, chrooted worker.
| Identity | Where it is defined | Risk | Control |
|---|---|---|---|
| root | master.passwd | Full control; can lower security unless securelevel blocks it | No direct SSH login with password (sshd default is prohibit-password; set no), use doas, raise securelevel |
| wheel group members | /etc/group | The sample doas.conf permits all of wheel with keepenv | Replace the sample with scoped rules; keep wheel small |
Daemon users (_name) | Created by base and packages | Compromise of a daemon gives its user and files | Privilege separation, pledge, unveil, chroot; pf user rules for egress |
| Automation and service accounts | Local users with SSH keys | Long-lived keys with doas nopass | One user per job, forced commands, doas rule per command; see the SSH Key Management Guide |
| Human administrators | Local users, SSH keys | Shared accounts, stale keys | Named accounts, key or certificate login, regular key review |
The daily security report checks master.passwd and group for syntax errors, empty passwords, suspicious UIDs and GIDs and duplicate entries, checks root's dotfiles and home directory, and flags .rhosts and .shosts files. It runs from daily(8) and mails root, so make sure root's mail is forwarded to a monitored mailbox.
Admin roles and privileged access
OpenBSD ships doas(1) as its privilege escalation tool. A doas.conf rule has the form permit|deny [options] identity [as target] [cmd command [args ...]]. The last matching rule determines the action, and if no rule matches, the request is denied.
| doas option | Effect | Guidance |
|---|---|---|
nopass | No password required | Only for a single, absolute-path command used by automation |
persist | Does not ask for the password again for some time after authentication | Acceptable for interactive admins; avoid on shared or exposed hosts |
nolog | Do not log successful execution to syslogd | Avoid: removes your audit trail |
keepenv | Retains the caller's environment variables | Avoid; use setenv { VAR } to pass only what is needed |
as target | Restricts the target user (default: all users) | Always set, usually as root or a service user |
cmd and args | Restricts command and arguments (default: all commands) | Use absolute paths, as the manual advises; args alone means no arguments |
Example scoped rules (replace the shipped permit keepenv :wheel):
permit persist :wheel as rootfor a small admin group that must type its password.permit nopass backup as root cmd /usr/sbin/dumpfor a backup service account.deny :staffplaced after broader rules if a sub-group must be excluded (remember the last match wins).
securelevel limits even root. At level 1 (the multi-user default) /dev/mem and /dev/kmem cannot be opened, raw disks of mounted filesystems are read-only and system immutable and append-only file flags cannot be removed. Level 2 adds read-only raw disks whether mounted or not and stops the clock being set backwards; the manual notes it can also limit the damage of a compromised firewall by prohibiting changes to packet filter rules. Only init can lower securelevel, so a change requires a reboot, which is the point. Combine level 2 with schg flags on critical binaries and configuration for appliances such as firewalls. For cross-platform guidance on superuser accounts see the Root, sudo and superuser accounts on Linux and Unix guide; ATT&CK tracks abuse of elevation tools as T1548.003 Sudo and Sudo Caching.
pledge, unveil and the exploit mitigations
OpenBSD's security page describes a file-by-file audit of critical components that has run since the summer of 1996, fixing bugs whether or not exploitability is proven, and a stream of mitigations that make the bugs that remain harder to exploit.
pledge(2)
pledge separates the POSIX feature set into about three dozen subsystems. A program declares the promises it needs, for example stdio rpath inet dns; subsystems not listed become unavailable, and most attempts to use them kill the process with an uncatchable SIGABRT. Later calls can only reduce promises, and execpromises sets what an executed child may use. Even with promises, some behaviour tightens on the first call: PROT_EXEC mappings need the prot_exec promise, setuid and setgid bits are ignored on chmod, and only a few ioctl operations are allowed. pledge first appeared in OpenBSD 5.9.
unveil(2)
The first call to unveil removes visibility of the entire filesystem except the named path with permissions r, w, x and c (create and remove). Further calls add paths; calling unveil(NULL, NULL) or a pledge without the unveil promise locks the list, which the manual strongly recommends. Disallowed access returns EACCES or ENOENT, and lastcomm(1) marks a terminated process with the U flag if unveil blocked file access. unveil first appeared in 6.4. The manual warns that programs misbehave when files disappear, so unveil directories rather than individual files.
| Mitigation | What it does | Attack it raises the cost of |
|---|---|---|
| pledge | System call restriction by promise group | Post-exploitation shell, network or file access from a compromised daemon |
| unveil | Per-process filesystem view | Reading keys, configuration or other users' data after compromise |
| Privilege separation | Small privileged parent, unprivileged chrooted worker | Root compromise through a parsing bug |
| W^X (since 3.3), execute-only code where supported | Memory is writable or executable, not both | Code injection |
| Randomised malloc and mmap, PIE, library order randomisation, random kernel relinking | Unpredictable memory layout | Memory corruption exploits relying on known addresses |
| Stack protector, RETGUARD | Per-function cookies checking return addresses | Return-oriented programming |
| Immutable memory and pinsyscalls | Locks mappings and pins where system calls can be made from | Changing memory permissions and calling the kernel from injected code |
What this means for you: base daemons already pledge and unveil, but third-party packages may not. When you write or commission software for OpenBSD, call pledge and unveil early in main() after configuration is read, and test with the narrowest promises that work. Mitigations map to NIST SP 800-53 SI-16 Memory Protection and SC-39 Process Isolation.
Integrations, API tokens and service accounts
- SSH keys are the main machine credential. Automation, backup and configuration management reach OpenBSD hosts over SSH. Use one key per job, restrict with
from=and forced commands in authorized_keys, and pair with a single-command doas rule. Hardening detail is in the OpenSSH Server Hardening Guide (OpenSSH is developed by the OpenBSD project). - Daemon credentials: keys and secrets for relayd, httpd, iked, smtpd and similar daemons belong in root-owned files readable only by the daemon's privileged parent; unveil reduces what a compromised worker can read.
- Configuration management: agents and pull-based tools typically need root; prefer push over SSH with scoped doas rules to a long-running root agent, and keep their tokens out of world-readable files.
- Patch infrastructure: syspatch fetches from the mirror named in
/etc/installurland verifies patches before installing (its manual refers to signify(1)). Point every host at a mirror you trust and monitor outbound access to it. - pf user rules: pf can match traffic by local user, as the default ruleset does to keep the
_pbuilduser off the network. Use the same technique to restrict which service users may make outbound connections.
Firewalling with pf
The shipped /etc/pf.conf contains set skip on lo, block return, pass (establish keep-state), a block on inbound X11 ports 6000 to 6010 on non-loopback interfaces and a block on outbound traffic for the _pbuild user. In other words, it is permissive. The pf FAQ recommends a default-deny policy whose first filter rule is block all. pf uses last-matching-rule-wins unless a rule has quick, keeps state for TCP, UDP and ICMP by default, logs matching packets through pflogd(8) when a rule has log, and supports urpf-failed for reverse path checks.
| Element | Baseline choice | Why |
|---|---|---|
| Default policy | block log all first | Anything not explicitly allowed is dropped and visible |
| Spoofing | antispoof for each interface and block in quick from urpf-failed | Drops packets claiming addresses from the wrong side |
| Management access | SSH only from a management table, for example <admins> | Limits brute force (T1110) and SSH lateral movement (T1021.004) |
| Services | Explicit pass in per published port | Matches the service inventory |
| Egress | Pass out by interface, user and destination where practical | Limits command-and-control and data exfiltration |
| Brute-force tables | max-src-conn-rate with overload <bruteforce> flush | Automatically blocks abusive sources |
Check syntax with pfctl -nf /etc/pf.conf before loading, keep the previous ruleset to roll back with pfctl -f, and if you run securelevel 2, plan rule changes around reboots. General ruleset review practice is in the Firewall Rule Review Checklist.
Logging and detection
| Source | What it tells you | Watch for |
|---|---|---|
| security(8) daily mail | Account file problems, setuid and setgid changes, changes to files in /etc/mtree/special and your own .secure lists, /etc/changelist diffs, disklabel changes, package installs and removals | Any unexpected change; build mtree lists for critical directories |
| Authentication logs | SSH logins and doas use (doas logs successful execution to syslogd unless nolog) | New source addresses, root logins, doas by unexpected users |
| pflog | Packets matching log rules | Blocked inbound scans, unexpected outbound connections |
| lastcomm(1) accounting | Processes flagged after pledge or unveil violations | Any flagged daemon: a possible exploitation attempt |
| syspatch -c | Patches available but not installed (suitable for cron) | Non-empty output older than your patch SLA |
Forward syslog to a central collector so that evidence survives a compromised host. A daemon killed by pledge is a signal worth an alert: it may be a bug, or it may be an exploit hitting the wall the mitigation built, as covered for exploitation in T1068 Exploitation for Privilege Escalation.
Metrics: percentage of hosts on a supported release; days from errata publication to syspatch install; number of doas rules with nopass or without cmd; percentage of hosts with a default-deny pf policy; number of unreviewed changes in the daily security report; percentage of hosts at securelevel 2 among appliances.
Hardening baseline
- Install minimal: select only the sets you need, enable only required daemons, and review what runs with
rcctl ls on. - Accounts and SSH: named admin accounts, key or certificate login,
PermitRootLogin no, and password authentication off where keys are in place. - doas: replace the sample rule with scoped rules; no
nolog, no blanketnopass. - pf: default-deny with antispoof, management tables and logging; test with
pfctl -nfand keep the previous file for rollback. - Patching: run
syspatchon amd64, i386 and arm64 (it keeps a rollback tarball;syspatch -rreverts the last patch), update packages, and plan a release upgrade every six months so you never fall outside the two supported releases. - Integrity: create
/etc/mtree/*.securelists for critical directories, set immutable flags on appliance configuration and raise securelevel to 2 where operations allow. - Logging: forward syslog and pflog centrally, forward root's mail, enable process accounting if you want pledge and unveil violation evidence.
- Review: quarterly review of doas rules, SSH keys, pf rules and the service list.
| Control area | NIST CSF 2.0 | NIST SP 800-53 Rev. 5 |
|---|---|---|
| Minimal services, secure configuration | PR.PS-01 | CM-6, CM-7 |
| doas least privilege, securelevel | PR.AA-05 | AC-6 |
| pf default deny and segmentation | PR.IR-01 | SC-7 |
| syspatch and supported releases | PR.PS-02 | SI-2 |
| pledge, unveil, memory mitigations | PR.PS-01 | SI-16, SC-39 |
| mtree, changelist and the security report | DE.CM-09 | SI-7, AU-2 |
Common misconfigurations
- Trusting the default pf.conf: it passes traffic; a "firewall" built on it may block nothing but X11.
- Running an unsupported release: no more errata. Stay on a supported release, apply security updates promptly with
syspatchand watch the OpenBSD errata page. - Keeping
permit keepenv :wheeland addingnopass: every wheel member, and anyone who steals their session, gets passwordless root. - Using
nologin doas rules: privileged actions disappear from the audit trail. - Ignoring the daily security mail: root's mail goes nowhere, so setuid and changelist alerts are never read.
- Assuming packages are pledged: third-party software may run with full system call access; contain it with dedicated users, chroot and pf user rules.
Practitioner checklist
- Every host runs OpenBSD 8.0 or 7.9 (the supported releases at 8 November 2026) and has a release upgrade date planned.
syspatch -cruns from cron and missing errata are installed within your patch SLA.- Only required daemons are enabled; the list is reviewed with
rcctl ls on. - doas.conf uses scoped rules with target users and absolute command paths; no
nolog;nopassonly for single commands. - SSH uses named accounts and keys, with root login disabled.
- pf starts with
block log all, uses antispoof and management tables, and changes are syntax-checked before load. - Appliances run at securelevel 2 with immutable flags on critical files.
- Root's mail is forwarded and the daily security report is reviewed.
- Syslog and pflog are forwarded to a central collector.
- In-house software for OpenBSD calls pledge and unveil and locks unveil after setup.
Standards and references
- OpenBSD: Security
- OpenBSD manual: pledge(2)
- OpenBSD manual: unveil(2)
- OpenBSD manual: doas.conf(5)
- OpenBSD PF User's Guide: Packet Filtering
- OpenBSD manual: syspatch(8)
- OpenBSD manual: securelevel(7)
- OpenBSD: Innovations
Related NHI Mgmt Group resources: FreeBSD Security Hardening Guide · OpenSSH Server Hardening Guide · Root, sudo and superuser accounts on Linux and Unix · Patch Management Guide