The Transition from IPFW to PF
For many years, network security on UNIX-like operating systems relied heavily on traditional firewall implementations like ipfw. However, starting with Mac OS X Lion (10.7), Apple deprecated ipfw in favor of the OpenBSD Packet Filter (pf). Highly regarded in the BSD community for its performance, clean syntax, and advanced stateful tracking, pf is the underlying engine that powers macOS network security today.
While macOS includes a graphical “Application Firewall” in System Settings, it is extremely rudimentary—only capable of blocking inbound connections on a per-application basis. To implement robust network security, such as blocking specific IP ranges, dropping outbound telemetry, or port forwarding, administrators must configure pf directly via the Terminal.
Step 1: Understanding the pf Configuration Files
The primary configuration file for the macOS packet filter is located at /etc/pf.conf. By default, this file contains fundamental Apple-specific anchor points that the system uses to manage basic network sharing and graphical firewall rules.
It is strongly recommended that you do not directly overwrite or heavily modify the default /etc/pf.conf file, as macOS updates may reset it. Instead, you should create a custom ruleset file and instruct pf to load it alongside the defaults.
Step 2: Writing Custom PF Rules
Let’s create a custom ruleset designed to block all outgoing traffic to a specific malicious subnet, while allowing all standard web traffic.
Open a new text file using nano (requires root):
sudo nano /etc/pf.custom.conf
PF rules are processed sequentially from top to bottom. The last matching rule wins, unless the quick keyword is used, which terminates evaluation immediately.
Add the following configuration:
# /etc/pf.custom.conf
# Define Macros for easy management
bad_network = "192.168.100.0/24"
web_ports = "{ 80, 443 }"
# Default deny policy for everything
block all
# Allow all traffic on the loopback interface
pass quick on lo0 all
# Explicitly block the malicious network (using quick to stop evaluation)
block drop quick from any to $bad_network
# Allow outbound web traffic, maintaining state
pass out proto tcp from any to any port $web_ports keep state
# Allow outbound DNS queries
pass out proto udp from any to any port 53 keep state
This strict ruleset drops everything by default, allows local loopback traffic, explicitly blocks the 192.168.100.0/24 subnet, and only allows outgoing HTTP, HTTPS, and DNS traffic.
Step 3: Testing the Ruleset Syntax
A syntax error in your pf configuration can lock you out of your machine entirely if you are connected via SSH. Before loading the rules, you must validate the syntax using the -n (no action) flag.
sudo pfctl -n -f /etc/pf.custom.conf
If the command returns nothing, your syntax is flawless. If there are errors, pfctl will indicate the exact line number where the issue occurred.
Step 4: Activating the Packet Filter
By default, the pf firewall is disabled on macOS. To load your custom ruleset and enable the firewall simultaneously, use the following command:
sudo pfctl -e -f /etc/pf.custom.conf
-e: Enables the packet filter.-f: Loads the specified configuration file.
You can verify that the rules are actively loaded in memory by running:
sudo pfctl -s rules
To flush all rules and disable the firewall (the panic button if you break your network):
sudo pfctl -d
Step 5: Making PF Persistent Across Reboots
The command in Step 4 only activates the firewall for the current session. If you reboot your Mac, pf will remain disabled.
To ensure pf starts on boot with your custom rules, you must modify the macOS LaunchDaemon that controls it. The plist file is located at /System/Library/LaunchDaemons/com.apple.pfctl.plist.
Because this file resides on the Read-Only System Volume (SSV) in modern macOS versions, you cannot edit it directly. The safest, most modern approach is to create a new custom LaunchDaemon (e.g., /Library/LaunchDaemons/com.custom.pf.plist) that simply executes pfctl -e -f /etc/pf.custom.conf during the system startup phase.
Conclusion
While the macOS graphical firewall is sufficient for basic user security, the underlying OpenBSD Packet Filter (pf) offers enterprise-grade network control. By mastering pfctl and writing custom configuration files, administrators can secure macOS endpoints with granular rulesets, stateful packet inspection, and powerful macro-based logic.