Skip to Content

macOS — Firewall Setup

Rabtly uses pf (packet filter) via pfctl to enforce port-level access control rules on macOS. Rules are loaded into a named anchor (cloud.rabtly) that only filters traffic on the WireGuard interface — your regular network traffic is never touched.

Requirements

  • macOS 13 (Ventura) or later is recommended
  • Works on macOS 10.7+ (pf has shipped since Lion)
  • pfctl is always present — no installation needed

Check if pf is enabled

sudo pfctl -s info 2>/dev/null | grep Status

Expected output: Status: Enabled

If it shows Status: Disabled, enable it:

sudo pfctl -e

Check if the Rabtly anchor is wired

The daemon auto-wires the anchor into /etc/pf.conf on first run. Verify:

grep 'anchor.*cloud.rabtly' /etc/pf.conf

If you see anchor "cloud.rabtly", you’re good.

Manual setup (if auto-wire failed)

In rare cases (read-only filesystem, unusual permissions), the auto-wire fails. Fix it manually:

1. Add the anchor to /etc/pf.conf:

sudo sh -c 'echo "anchor \"cloud.rabtly\"" >> /etc/pf.conf'

2. Reload the main ruleset:

sudo pfctl -e -f /etc/pf.conf

3. Restart the daemon:

sudo launchctl kickstart -k system/cloud.rabtly.daemon

Verify

# Check daemon status rabtly status # Should show firewall_enforced=true # Inspect active rules in the anchor sudo pfctl -a cloud.rabtly -sr

You should see output like:

pass out quick on utun4 all keep state pass in quick on utun4 inet from 100.64.0.2/32 to any keep state # rabtly: rule#0 all pass in quick on utun4 inet proto tcp from 100.64.1.1/32 to any port 5432 keep state # rabtly: rule#1 tcp block drop in quick on utun4 all

Troubleshooting

”pf is disabled; rules will load but not filter”

pf is turned off system-wide. Enable it:

sudo pfctl -e

To make it persist across reboots, ensure /etc/pf.conf exists (macOS loads it at boot via com.apple.pfctl launchd job).

“could not auto-wire pf anchor into /etc/pf.conf”

The daemon couldn’t edit /etc/pf.conf. Common causes:

  • SIP (System Integrity Protection) does NOT protect /etc/pf.conf — it’s a regular root-owned file.
  • Read-only boot volume on macOS Big Sur+ — /etc is on the signed system volume but /etc/pf.conf is symlinked to the writable data volume. If this symlink is broken, fix it:
# Check the symlink ls -la /etc/pf.conf # If it's pointing nowhere, recreate it sudo ln -sf /private/etc/pf.conf /etc/pf.conf

Rules loaded but traffic isn’t filtered

This happens when the anchor isn’t wired into the main ruleset:

# Verify the anchor reference exists grep 'cloud.rabtly' /etc/pf.conf # If missing, add it and reload sudo sh -c 'echo "anchor \"cloud.rabtly\"" >> /etc/pf.conf' sudo pfctl -f /etc/pf.conf

Legacy “com.rabtly” anchor

Early builds used the anchor name com.rabtly. The daemon automatically migrates to cloud.rabtly and removes the old reference. If you see both in /etc/pf.conf, remove the old one:

sudo sed -i '' '/anchor.*com\.rabtly/d' /etc/pf.conf sudo pfctl -f /etc/pf.conf

Conflict with Little Snitch / Lulu / other pf-based firewalls

Rabtly uses a named anchor which is completely isolated from the main ruleset and from other anchors. It does not conflict with:

  • Little Snitch (uses its own Network Extension, not pf)
  • Lulu (uses its own anchor)
  • Murus/Vallum (use their own anchors)

These applications coexist safely because each operates in its own namespace.

”pfctl: Use of -f option, could not obtain token”

Another process holds the pfctl lock. Wait a moment and retry — this usually resolves within seconds. If persistent, check for stuck pf-management tools:

ps aux | grep pfctl

How Rabtly uses pf

  • Anchor: cloud.rabtly (isolated from the main ruleset)
  • All rules are quick: First-match-wins semantics, so macOS’s built-in rules can’t override our anchor’s verdict
  • Stateful outbound: pass out quick on <iface> all keep state — replies to connections you initiate always flow back
  • Per-rule inbound passes: Each ACL rule becomes a pass in quick with keep state
  • Default deny: block drop in quick on <iface> all at the end catches anything not explicitly allowed
  • Syntax: OpenBSD 4.7 (macOS’s frozen pf version) — no 4.8+ features like match or set skip
  • Atomicity: Loaded via pfctl -a cloud.rabtly -f - (all-or-nothing within the anchor)

Interface names

On macOS, WireGuard typically uses utun3, utun4, utun5, etc. The exact number depends on how many tunnel interfaces are active. Rabtly scopes all rules to the interface it creates, so other utun interfaces (VPN clients, iCloud Private Relay) are unaffected.