pf is FreeBSD's built-in firewall — short for "packet filter." Unlike Linux's iptables/nftables, the entire ruleset lives in a single, readable text file. You write rules in something close to plain English, save, and reload.
What does a firewall actually do? It looks at every packet of network traffic going in or out of your machine and decides whether to allow it. With no firewall, every port your computer listens on is exposed to the network.
Heads up: firewall rules can lock you out of a remote machine. If you're configuring pf over SSH, make absolutely sure you allow port 22 before activating.
Step 1: Write the config file
sudo nano /etc/pf.conf
Replace whatever's there with this starter ruleset. It works for a single-network-card server — we'll walk through what each line does after.
# /etc/pf.conf
ext_if = "em0"
table <bruteforce> persist
scrub in all
block in all
pass out all keep state
pass quick on lo0 all
pass inet proto icmp icmp-type echoreq
pass in on $ext_if proto tcp to ($ext_if) port 22
keep state
(max-src-conn 10, max-src-conn-rate 5/60,
overload <bruteforce> flush global)
block quick from <bruteforce>
# uncomment for a web server:
# pass in on $ext_if proto tcp to ($ext_if) port { 80, 443 }
What each block means
ext_if = "em0"— a variable for your network interface name. Find yours withifconfig.table <bruteforce>— a list we'll fill with IPs that hammer our SSH port.scrub in all— normalises incoming packets (drops malformed ones).block in all— default policy: block everything inbound.pass out all keep state— your machine can talk out freely.pass quick on lo0 all— loopback traffic (programs talking to themselves) is allowed.pass inet proto icmp icmp-type echoreq— allow incoming ping. Helpful for debugging.- The big SSH block — allow port 22, but if any single IP opens more than 10 connections or 5 in 60 seconds, banish it to
<bruteforce>. block quick from <bruteforce>— block everything from banished IPs.
Step 2: Enable pf at boot
sudo sysrc pf_enable="YES"
sudo sysrc pflog_enable="YES"
pflog is the logging companion — it gives you a virtual interface (pflog0) you can run tcpdump against.
Step 3: Start it
sudo service pf start
sudo service pflog start
Step 4: Reload after edits
Once pf is running, every config change just needs a reload:
sudo pfctl -f /etc/pf.conf
If there's a syntax error, pf prints it and keeps the previous rules — it never strands you with no firewall.
Inspecting what's happening
sudo pfctl -s rules # show loaded rules
sudo pfctl -s states # show currently-allowed connections
sudo pfctl -t bruteforce -T show # who has been banished
sudo tcpdump -nettti pflog0 # watch blocked traffic in real time
The panic button
If you've locked yourself out and you're at a local console:
sudo pfctl -d
That disables pf entirely until you re-enable it. Use as a last resort.