pf on OS X 10.7

Wednesday, 14. 09. 2011  –  Category: sw

Today’s the first day that my new laptop, which runs OS X 10.7 (Lion), will sit on an untrusted network so I figured it was time to port my firewall rules across from the old one, that ran OS X 10.6 (Snow Leopard).

I cut my UNIX teeth at a cryptoanarchist shop whose culture of paranoia makes me wary of Apple’s own firewall with its emphasis on letting all the hot shinyness Just Work rather than being overly fussy about inbound connections. Furthermore, with IPv6 tunnels like aiccu, you aren’t behind the warm fuzzy comfort of NAT, you’re just there on the net with all the fun that entails. So, worth having some extra protection I reckon.

10.6 (and earlier) came with ipfw, a packet filter that’s knocked around the BSD world for some time. It works but isn’t overly featuresome (for example, it doesn’t support NAT in-kernel, so you monkey about passing packets to an external daemon). But it was Good Enough for an end system so I supplemented the system’s “Application Firewall” with an additional ipfw ruleset to give an approximation of safety when out and about, and way more permissive when on networks I trust.

On 10.6 I used WaterRoof to sort-of manage the ipfw rules: in that I only really used its launchd loader and would hack on the rules by hand. In the spirit of decrufting I figured I’d sort that myself and went to remind myself how to load ipfw rules enmasse. I noticed at the top of man page:


NAME
     ipfw -- IP firewall and traffic shaper control program (DEPRECATED)
...
DESCRIPTION
     Note that use of this utility is DEPRECATED. Please use pfctl(8) instead

Deprecated? Use pfctl instead? Good news everyone – OS X 10.7 now comes with pf, another BSD packet filter that I’ve chosen of ipfw on BSD hosts for years off the back of its featureset (native NAT, state syncing between failover firewall pairs, traffic queing…).

Anyway, the point of this post is to point out a few things I noticed which are intriguing. Firstly, pf is not enabled by default. Further, Apple have added some moving parts around how it is enabled. From /etc/pf.conf:


# This file contains the main ruleset, which gets automatically loaded
# at startup.  PF will not be automatically enabled, however.  Instead,
# each component which utilizes PF is responsible for enabling and disabling
# PF via -E and -X as documented in pfctl(8).  That will ensure that PF
# is disabled only when the last enable reference is released.

These two flags, -E and -X, are absent from pf on BSD. Here’s how they’re documented on OS X:

     -E      Enable the packet filter and increment the pf enable reference count.
     -X token
             Release the pf enable reference represented by the token passed.

This suggests that different system components might choose to enable and disable pf, and this is the mechanism to coordinate that. There’s a clue about which components in /etc/pf.anchors/com.apple, which is loaded by the main /etc/pf.conf. It defines additional rule anchors:

anchor "100.InternetSharing/*"
anchor "200.AirDrop/*"
anchor "250.ApplicationFirewall/*"

Interestingly, this host’s ApplicationFirewall has a bunch of entries in when viewed in the Preferences GUI, yet the pf anchor of the same name is empty (and pf was disabled when I started out):


$ sudo pfctl -a com.apple/250.ApplicationFirewall -s rules
Password:
No ALTQ support in kernel
ALTQ related functions disabled

so I’m unsure what the status of this mechanism is. I’ve not had occasion to use AirDrop or connection sharing, but would be curious to see if either use these anchors and enable pf temporarily.

Finally, what’s the token that’s passed to -X? You can ask pfctl for the current tokens:


$ sudo pfctl -s References
No ALTQ support in kernel
ALTQ related functions disabled
TOKENS:
PID      Process Name                 TOKEN                    TIMESTAMP
17013    pfctl                        18446743524308110600     0 days 01:05:50

I enabled pf with pfctl, so that makes sense. When I did so it didn’t inform me of the token, but I suppose an enabling process would spelunk the token shortly after enabling pf by merit of its name and PID and pass it back when it’s finished with pf.

Now, on with the actual job of ruleset writing and puzzling out the launchd voodoo required to enable it at boot.

Minor whinge: Apple could do with updating /etc/protocols:


# $FreeBSD: src/etc/protocols,v 1.14 2000/09/24 11:20:27 asmodai Exp $

Why whinge? It doesn’t know icmp6 is a valid alias for ipv6-icmp. Yep, minor.

Tags: , , , , , ,

4 Responses to “pf on OS X 10.7”

  1. Ron Beloin Says:

    This post was a help.

    I have been struggling with getting port forwarding to work under Lion (not server) for my media mac to an AVR connected by wire.
    Getting the AVR to see the internet was no problem, just configuring the net interfaces (internal must be 192.168.2.0 until apple fixes that bug) and turning on InternetSharing.
    But port forwarding for the AVR’s control port was not working after trying all sorts of things.
    I realized that InternetSharing sets rules to PF once it starts, under the anchors that are predefined in /etc/pf.anchors/com.apple.
    If you probe with pfctl after starting InternetSharing, you can see the rules and new anchors that it created.
    One of the anchors was ‘natpmp’.
    I used this command to set my rules and it worked!
    pfctl -a com.apple/100.InternetSharing/natpmp -f 910.onkyo

    (obviously rules are in 910.onkyo)

    So, I did not set my own anchor (which didn’t work). It only worked when I associated my rules with that anchor.
    This has to be done after InternetSharing is started, of course.

    I imagine an application such as InternetSharing enables pf, loads rules, then pf only responds to rules below the anchor for the application(s) that activated it.

    Ron

  2. Dan Caballero Says:

    Just wondering if you’ve made any progress on loading custom rules via 205.ApplicationFirewall ??

    I’m trying to set up a rule that restricts to a specific subnet.
    Something along the lines of this:

    pass in inet proto tcp from $subnet to port 22

  3. lemon Says:

    No, I haven’t – I’m using my own anchor and activating pfctl directly. See Ron’s comment above for an example of hooking into a pre-defined system anchor, which raises an interesting point about rule scope around these anchors.

  4. Dyr Says:

    Actually it’s a very strange idea to get pf from BSD instead of just do update ipfw to current BSD version (which is supports a bunch of features and, of cause, kernel NAT)