A Cheat Sheet For Using pf in OS X Lion and Up

I’ve done plenty of writing on the Application Layer Firewall (ALF) and the IP FireWall (IPFW) in OS X over the years. There will be more on ALF coming in “July” but in the meantime, there’s something I hadn’t written much about in Lion and that’s the pf implementation. To get started, let’s look at the /etc/pf.conf configuration file that comprises pf: scrub-anchor "com.apple/*" nat-anchor "com.apple/*" rdr-anchor "com.apple/*" dummynet-anchor "com.apple/*" anchor "com.apple/*" load anchor "com.apple" from "/etc/pf.anchors/com.apple" Here, you can see that pf is configured with a number of anchors. An anchor is a collection of rules and tables. Basically, the anchor file being loaded is /etc/pf.anchors/com.apple. In here, we see some rules (without comments): scrub-anchor "100.InternetSharing/*" scrub-anchor "300.NetworkLinkConditioner/*" nat-anchor "100.InternetSharing/*" rdr-anchor "100.InternetSharing/*" anchor "100.InternetSharing/*" anchor "200.AirDrop/*" anchor "250.ApplicationFirewall/*" dummynet-anchor "300.NetworkLinkConditioner/*" anchor "300.NetworkLinkConditioner/*" anchor "400.AdaptiveFirewall/*" load anchor "400.AdaptiveFirewall/" from "/Applications/Server.app/Contents/ServerRoot/private/etc/pf.anchors/400.AdaptiveFirewall" These are mostly just allowing the Apple services to work with services enabled in the Sharing system preference pane, etc. The scrub options are pretty cool as it cleans dirty packets prior to passing them to their destination. To see how the rules are interpreted, let’s run pfctl with the -sa option, which shows all information/stats: sudo pfctl -sa Here we see information like stats on timeouts, limits to rules, etc. Let’s look at the rules specifically: sudo pfctl -sr Now let’s load a line below the previously called anchors in the first file: pass in quick on lo0 all pass out quick on lo0 all This is going to always allow local traffic, which we need for a few internal processes. Then let’s block some stuff (after all, if we’re not filtering, why use a packet filter). First add the following to the pf.conf file to block all otherwise allowed incoming sockets: block in all And this one for outbound traffic: block out all Or to knock the two above lines out with one: block all Then to do something pretty straight forward, like allow incoming icmp traffic for en0: pass in quick on en0 proto icmp One more rule, to show how we’re going to pass and log data for data coming into en0 for both tcp and udp from anyone to the IP on that interface running for port 548: pass in log quick on en0 proto { tcp, udp } from any to port 548 keep state Of the above, tables allow you to define ranges and basically alias IPs. Anything in this section of pf.conf in angled (<>) brackets is a table that has been defined. You can also build a list, which allows multiple criteria to be defined for a given rule and macros, which are essentially arrays of IPs, ports, etc, designed to reduce the amount of typing you have to do if you’re building out a big configuration file. Once we’ve edited our configuration file, let’s run a quick sanity check on it: sudo pfctl -v -n -f /etc/pf.conf Now, provided we don’t get any crazy errors, let’s load pf with our rules (which also loads the anchors): sudo pfctl -f /etc/pf.conf Then let’s set pf to be verbose while we’re testing (we’ll turn it off later): sudo pfctl -v Then let’s enable pf: sudo pfctl -e The return code should be something along the lines of the following: pf enabled You can also add information on the fly. For example, to add a table of call localsub: sudo pfctl -t localsub -T add If you want to flush your rules later: sudo pfctl -Fa -f /etc/pf.conf To clear your stats: sudo pfctl -z ; pfctl -si Once we feel good about the pf configuration, set it to be quiet to keep the logs small and make it a little quicker: sudo pfctl -q And to disable pfctl when you’re done tinkeratin’: sudo pfctl -d And to watch what it’s doing: ifconfig pflog0 Followed by sudo tcpdump -v -n -e -ttt -i pflog0 Overall, pfctl is pretty straight forward to use. There is a really good post (thanks to @sacrilicious for pointing it out) at http://ikawnoclast.com/2012/04/using-the-lion-pf-firewall-with-the-emerging-threats-list.html for syncing the Emerging Threats anchor from emergingthreats.net. And of course, OpenBSDs pf page is the best source of information on the project, available here. There are a few limitations. The pf command is limited to one processor, so running a dedicated pf host on an 8 core machine is pretty much overkill. RAM is important as pf doesn’t use swap space. The more you pay for a card, the better a card you get, for the most part. Check out the Small Tree cards as they’re pretty efficient… A few things I haven’t gotten working, the logging is kinda’ wonky. The antispoof protection seems odd (see the antispoof docs on the pf page), osfp (which might be other devices in my walled garden) and dummynet integration (which I have working w/ ipfw)… If I can get them working I’ll put together another post for that in my infinite amounts of free time. I also didn’t end up figuring out the upper limit for packets/rule lookups/table lookups per second… As I write more efficient tables I do more lookups and can therefore process packets faster. It’s annoying when I realize ***I*** am the bottleneck…

14 thoughts on “A Cheat Sheet For Using pf in OS X Lion and Up”

  1. Great stuff!

    One thing to note is that Apple has written over the /etc/pf.conf a few times with OS X updates. I gave up on relying /etc.pf.conf and instead decided to read it in when loading from my on configuration file. I’m also using a special set of files to allow centralized rules and macros + local, client specific rule files.

    Example setup is posted at:

    1. You guys seem to have an impressive knowledge of pf… could you give some advice to the like of me who struggle configuring rules to bridge an openvpn server on OS X Lion? Network bridging seems to be a major issue for Lion users…

  2. Do you have any idea if PF can be used to redirect all port 80 and 443 traffic to a proxy server? I am employed by a small K-12 school in the U.S. On student laptops, we’d like to force all web traffic through our proxy servers. I understand that this can be done via the ‘Proxies’ setting within the System Preferences Network Panel, but it can be disabled. By doing it with PF at system startup, studens will be unable to prevent(unless they are very sharp) the redirect.

    1. Richard,

      Sorry for the delay. I haven’t done that specifically. I know that most of our clients are using 3rd party appliances for this type of thing. But I haven’t looked at mass deploying pf to proxy. We did some stuff like that in ipfw using divert, awhile back, so I’d assume you can do it, just haven’t done it myself. We’ve also done some deployment work with Squid, using a parent squid box to do some of this (Dans Guardian as the filter). Anyway, hope that helps in some way.


  3. Nice.

    I guess the pfctl could solve my issue: forward packets with specific port to a specific network interface.


  4. In my yearly bout with pfctl I always return to this cheat sheet 🙂 It might be worth updating it with a notice that you need -E these days when loading your updated pf.conf, like so: pfctl -E -f /etc/pf.conf. See the man pfctl for more details.

Comments are closed.