FreeBSD and OpenBSD port hardening starts from a better baseline than most Linux distributions, but a good default only lasts until someone installs the first package. If you run BSD systems on public IPs, such as firewalls, mail relays, jail hosts or small VPS instances, this article covers what listens by default, how pf behaves differently on the two systems, which mistakes reopen ports you thought were closed, and how to confirm the result from outside.
What a Fresh BSD Install Actually Exposes
A default OpenBSD 7.x install listens on very little. sshd is enabled on port 22 if you said yes during install. smtpd binds to localhost only. OpenNTPD runs as a client and doesn’t accept inbound queries. Running netstat -an -f inet | grep LISTEN on a clean system usually returns two or three lines.
FreeBSD is similar but depends more on installer choices. Since FreeBSD 14.0, the default MTA is dma instead of Sendmail, so the old habit of adding sendmail_enable=”NONE” to rc.conf matters less on new builds. It still matters on systems upgraded in place from 12.x or 13.x. The bsdinstall hardening menu lets you disable remote syslogd sockets and enable a few sysctl protections. Many admins skip that screen.
On FreeBSD, run sockstat -46l to see listeners. Then run service -e to list every enabled rc script. Compare the two outputs. Anything listening that isn’t in the enabled list deserves a closer look.
The Myth of “Secure by Default” Forever
OpenBSD’s claim of “only two remote holes in the default install, in a heck of a long time” is true and impressive. It’s also often misread. It applies to the default install, not to a box running nginx, PostgreSQL, Samba and a Node.js app from packages.
Once you run pkg_add or pkg install, your exposure depends on upstream software and how you configure it. The BSD base system doesn’t protect a Redis 7 instance bound to 0.0.0.0. Plenty of Redis, Elasticsearch and MongoDB exposures have turned up on BSD hosts because the admin assumed the OS covered them.
The base system does help in specific cases. When the OpenSSH regreSSHion bug (CVE-2024-6387) was disclosed in July 2024, Qualys noted that OpenBSD wasn’t vulnerable because of how its syslog handles signals. Treat that as a nice bonus, not a strategy.
pf on FreeBSD Is Not pf on OpenBSD
Both systems ship pf, but they’ve diverged. FreeBSD’s pf comes from an older OpenBSD codebase with many backported features. Some syntax still differs, including NAT and redirection rules. A pf.conf copied from an OpenBSD guide can fail to load on FreeBSD, or load and behave in ways you didn’t intend.
An experienced BSD admin follows the same sequence on either platform:
1. Write a default-deny ruleset: block all or block return, then set skip on lo, then explicit pass rules for each public service.
2. Check the syntax without loading it, using pfctl -nf /etc/pf.conf.
3. Load it with a safety net, for example a cron job or at job that runs pfctl -d in 5 minutes if you lock yourself out.
4. Confirm the rules pf actually loaded with pfctl -sr. Don’t trust the file.
5. Make the firewall persistent. On FreeBSD, run sysrc pf_enable=YES. OpenBSD enables pf by default.
Step 5 causes more surprises than any other. Here’s a common case. An admin debugs a FreeBSD 13.2 jail host, runs pfctl -e by hand, and everything looks correct. Nobody adds pf_enable to rc.conf. Three months later, a kernel update forces a reboot. The host comes back with no packet filter at all, and every jail service bound to the shared IP is exposed. Only an external scan would catch that before an attacker does.
Rules that only ever change while the system is running also deserve a regular check. The approach in validating firewall rules with external port scans applies to pf the same way it does to iptables.
Service Binding and Jail-Specific Pitfalls
On BSD, the firewall is your second layer. The first is making daemons listen only where they’re needed. For syslogd on FreeBSD, set syslogd_flags=”-ss” so it opens no network socket. For rpcbind, set rpcbind_enable=”NO” unless you serve NFS. When NFS is on, port 111 should never be reachable from the internet, because it’s a common amplification and reconnaissance target.
Jails add a twist. In a shared-IP jail, a service that binds to “all addresses” binds to the jail’s IP. That IP is often the host’s public address. A developer who installs a database in a jail for testing may expose it publicly without realizing it. VNET jails with their own network stack and their own firewall rules avoid this, but they take more setup.
On FreeBSD, the sysctls net.inet.tcp.blackhole=2 and net.inet.udp.blackhole=1 make the kernel silently drop packets to closed ports instead of answering with RST or ICMP. External scans then show those ports as “filtered” rather than “closed.” That slows down casual scanners, but it hides nothing that’s actually listening. It can also confuse your own troubleshooting if you forget it’s turned on.
Common Mistakes on BSD Hosts
Trusting the base system and ignoring packages. On FreeBSD, pkg audit -F checks installed packages against the VuXML vulnerability database in a few seconds. On OpenBSD, run syspatch for the base system and pkg_add -u for packages. Schedule all of these. Admins who run them only after a security advisory miss the quieter fixes.
Exposing sshd on 22 with password authentication. Moving SSH to port 2222 cuts down log noise, not risk. Disable passwords, restrict access with AllowUsers, and use pf’s max-src-conn-rate with an overload table to slow down brute-force attempts. The SSH hardening guide covers this in more depth.
Checking only from inside. sockstat shows what’s listening. It doesn’t show what’s reachable. Upstream NAT, a hosting provider’s firewall, or a disabled pf can all make the real exposure differ from what you expect. Linux admins run into the same gap. The Linux port hardening guide explains the parallel process for mixed environments.
Edge Cases Where Standard Advice Changes
Appliances built on FreeBSD, such as pfSense and OPNsense, manage pf through a web UI. Hand-edited rules get overwritten. Harden those through the UI, and keep the admin interface off the WAN side completely.
On air-gapped or internal-only BSD hosts, external scanning doesn’t apply in the usual way. Internal segmentation testing matters more there. Small teams running a single OpenBSD relay or VPN gateway can often get by with a 15-line pf.conf. A complex ruleset that nobody understands is a risk of its own.
FAQ
Does OpenBSD need a firewall if it listens on almost nothing?
Yes. pf costs almost nothing to run, and a default-deny policy protects you from the next package that binds to 0.0.0.0 without telling you.
Should I use pf or ipfw on FreeBSD?
Both work well. pf has more readable syntax, and its rules are easier to share with OpenBSD hosts. ipfw integrates closely with dummynet for traffic shaping. Pick one and stay consistent. Running both makes debugging much harder.
How often should a public BSD server be scanned externally?
At minimum after every reboot, upgrade or pf change. Continuous monitoring catches drift between those events, such as the reboot that silently dropped pf.
Bottom Line for BSD Administrators
FreeBSD and OpenBSD give you a lean starting point and a capable packet filter. Your exposure depends on three things: which services you add, what they bind to, and whether pf actually loads at boot. Build a default-deny pf.conf, make it persistent, and audit packages every week. Then check the result from outside the host, because what the server reports and what the internet can reach are not always the same.
