Configuring IPFW firewalls on OS X

OS X comes with a unix firewall program called IPFW, and there are a variety of programs you can use to access it from the GUI. As of 10.2 Apple includes a tool in system preferences to enable the firewall, but it's a very simple implementation that will only enable or disable a port for the entire internet. This isn't the best use of a firewall--the real strength of a firewall is to enable access to a service for a limited range of ip numbers. As of 10.5 the situation is much better, in that the firewall is application oriented and you can enable or disable connections based on the application used. But I find it useful to be able to manipulate the firewall directly, and this document covers some of the basics.

Also, this doc covers use of IPFW with the client versions of OS X. If you're using OS X server, the GUI tool provided allows for more customization. But the GUI tool (at least as of 10.3) doesn't build IPFW rules that track state, which makes getting things like AFS and Kerberos more difficult to work. I've put together some notes on how the firewall tools with OS X server works.

The best tool for managing IPFW out there is Brian Hill's Flying Buttress. In addition to allowing the user to manage the firewall, it will also help you setup connection sharing and Network Address Translation so you can use your Mac as a router. Another good GUI frontend to IPFW is WaterRoof. So if your needs are simple, those may be good ways to go. But if you're able to navigate via the command line and can use a text editor like vi or pico, it's not too difficult to manage your firewall with IPFW directly.

Getting Started

The first thing to figure out is whether you need a special firewall set up at all. The first thing to check is whether you have any services enabled. Check your System Preferences and look at the Sharing setup.

Sharing Preference Pane with Remote Login and Web Sharing enabled

In my case, I have ssh (Remote Login) and httpd (Web Sharing) enabled. So these are two services I've set up to allow remote machines to use to connect. Ssh is pretty secure since it requires a user id and password, and has some security built in. And httpd isn't inherently insecure.

Now, if you're running 10.5, check the Security preference pane, and see that the firewall is enabled. Click on Advanced and you'll likely see something like this:

Advanced Security Pane with Remote Login and Web Sharing enabled for all incoming connections through the firewall

Under 10.4 and earlier, the location of these settings is a bit different, and the panes are arranged differently, but the basic setup is similar. Notice that both Remote Sharing and Web Sharing are set to allow all incoming connections. This is the key thing to understand about the built in firewall--it doesn't offer you the chance to refine what machines are allowed to connect over a given protocol and which are not--it only allow you to open a port to the world, or to close a port to the world.

Let's take ssh as an example. I enable ssh so I can login to my laptop with a terminal connection, and to mount my laptop's filesystem via sshfs (eg. MacFusion). I don't need to be able to do this from anywhere on the internet, however. I'm usually very close to my laptop, so these connection are made at my house or from somewhere on the UNC Chapel Hill campus. So there's really no reason that I should enable ssh connection from, say, China or Bulgaria or Indiana. If I restrict ssh connections to my home network and the UNC campus, I'm still in good shape, and I've lowered the number of IP addresses that can try to connect via ssh to my laptop from a billion or so to just several thousand. That reduces my exposure considerably. The basic rule is that if you need to enable a service, think about who needs to connect to it, and if you don't need the entire world to have that opportunity, use a more complex firewall rule to restrict it a bit. If you're not running any services, you're not really that exposed in the first place, so you don't need to worry nearly so much.

Another consideration is whether you are using a Public IP address or a Private IP address. Most home users are connected via a cable modem or DSL connection that has a Network Address Translation (NAT) router. If you're not familiar with these terms, don't worry, it's pretty simple. A NAT router supports multiple computers in your home or office, and it does that by handing out Private IP addresses to those machines. These addresses are, according to networking conventions, not routable on the internet. That just means that someone out there on the internet cannot attempt to directly connect to your machines in your house--in effect, the NAT acts as a firewall for you.  Pretty much all home connections, and connections in coffee houses and the like use private addresses. Now this doesn't mean that no one can try to connect to your machine--if someone knows what they are doing and they are in the same coffee house as you, they can try to connect to your laptop from there. But again, the number of folks who can try this is really small compared to the total number of users on the internet. So if you're a laptop user, and you're not running services, and you connect only from your home and a couple of other locations, setting up a complex firewall isn't going to help you much.

Private addresses all fall in the following ranges:

For more information see the Wikipedia article on Private Addresses.

On the other hand, if you're using a public IP address, you're the world's oyster and anyone on the internet can try to talk to your machine. So keeping the shell closed to a narrow range makes sense. Public addresses are in wide use, for example, at universities and colleges in the USA. So if you have a desktop computer in your dorm room, and you don't use an address in the ranges above, and you enable some services like File Sharing, or Screen Sharing, or Remote Login, you definitely should look into using a more selective firewall than what's provided by the operating system out of the box.

The Rules

The first thing to do is to play with looking at the firewall from the command line. Open the System Preferences, and under Sharing, enable the firewall. Then open a terminal session, and type:

sudo /sbin/ipfw list

In 10.4 and prior, you might get back something like this if you have services running like Remote Login and Web Sharing:

[gilgamesh:~] hays% sudo ipfw list
02000 allow ip from any to any via lo*
02010 deny ip from 127.0.0.0/8 to any in
02020 deny ip from any to 127.0.0.0/8 in
02030 deny ip from 224.0.0.0/3 to any in
02040 deny tcp from any to 224.0.0.0/3 in
02050 allow tcp from any to any out
02060 allow tcp from any to any established
02070 allow tcp from any to any 80 in
02080 allow tcp from any to any 427 in
02090 allow tcp from any to any 548 in
02100 allow tcp from any to any 427 in
02110 allow tcp from any to any 22 in
02120 allow tcp from any to any 139 in
12190 deny tcp from any to any
65535 allow ip from any to any
[gilgamesh:~] hays%

In this case, my configuration is set up to allow personal file and web sharing, remote login, and windows filesharing. Your list may well vary. Compare the output from the list command to the entries in the firewall configuration of the Sharing preferences.

Now how this works is that packets both inbound and outbound are compared to the list of rules in order, and if a match is found, that action is taken. For example, in the list above, rule 02050 is processed first, and it allows any tcp packets that are outbound, so your machine can try to contact any other machine, so outbound packets are allowed out before rule 12190 is processed.

In 10.5, you'll probably get a single rule back, since the firewall is oriented to the applications.

Getting your feet wet

WARNING! We're going to be playing with a fairly deep and archane portion of the OS, so you may render your machine unusable on the internet for a while. Do not do any of this unless you're sitting in front of the machine and have some spare time. The first part of this is pretty safe since all you have to do to get back to "normal" is to reboot and check your sharing settings.

If you run the Probe My Ports option from Gibson's Shield's Up , you can see what your machine looks like from the internet with your firewall on (assuming, of course, that you aren't behind another firewall or nat--for example, if you're on the UNC campus, the NetBios ports always show as Stealth since access to those is blocked by the main campus router).

The first thing we're going to do is enable Web Sharing in the System Preferences, under Sharing. Just click that checkbox, and save the changes. Then use your browser to connect to http://127.0.0.1/. A web page should load, the default page for the OS X web server. (Just so you'll know if you don't already, 127.0.0.1 means "ME!" in networking, so all connections of any kind to 127.0.0.1 stay on your local machine.

Now, use your browser to do to Gibson's Shield's Up, the shields up link is down about halfway on that page. Click on that link, and then click on the Proceed button, and on the next page, click on the Common Ports link. This will scan your machine and see what ports are open. If port 80, HTTP, shows as "stealth", it's likely you're on a Private IP address and protected by a NAT. If that's the case, there's not much else you can do here. But if port 80, http is marked as open, then you're on a Public IP address and anyone on the internet can connect to your web service.

To play with the firewall settings, you'll use the command line in a terminal window. First, we'll clear all of the rules. In the terminal window, run:

sudo /sbin/ipfw -f flush  

And then reenter:

sudo /sbin/ipfw list  

again to list the rules. Go back and reload the Probe My Ports page, so you can see the effect of disabling the firewall.

Now type

sudo /sbin/ipfw add 02070 deny tcp from any to any 80 in

This will should have the effect of blocking access to the web server, which runs on port 80. Even if you're not running a web server, you can see the effect of the command by running Probe My Ports on the Shield's Up site again--the port should go from Closed to Stealth.

The Script

Ok, so far so good. We'll start with a simple firewall. Open a terminal window and run the following commands:

curl -o Firewall.simple http://www.ibiblio.org/macsupport/ipfw/Firewall.simple
chmod a+x Firewall.simple
sudo ./Firewall.simple start
The first line should download the firewall, the second mark it as executable, and the third activates the firewall. The output from the last line should be this:
Starting Firewall
Flushed all rules.
00100 allow ip from me to me
00200 allow ip from 192.168.0.0/16 to 224.0.0.0/3 in
00300 deny tcp from any to any dst-port 0 in
00400 check-state
00500 allow tcp from me to any keep-state
00600 allow udp from me to any keep-state
25000 deny tcp from any to any established
25100 allow udp from any 67 to any dst-port 68 in
25200 allow ip from 192.168.0.0/16 to any in
25300 allow icmp from any to any in
25400 allow tcp from any to any dst-port 80
25500 allow tcp from any to any dst-port 443
25600 allow tcp from 192.168.0.0/16 to any dst-port 22 in
25700 allow tcp from 192.168.0.0/16 to any dst-port 548 in
25800 allow tcp from 192.168.0.0/16 to any dst-port 427 in
25900 allow tcp from 192.168.0.0/16 to any dst-port 3689 in
26000 allow ip from 192.168.0.0/16 to any dst-port 5353 in
26100 allow udp from 192.168.0.0/16 5353 to any dst-port 1024-65535 in
65000 reject udp from any to any in
65100 deny icmp from any to any in
65200 deny ip from any to any in
This simple firewall opens the following ports:
Now, open Filewall.simple with a text editor (I like TextWrangler). Then open your Network preference pane in System Preferences, and look at the Advanced tab for your active network connection (Ethernet or Airport, most likely).

Network Settings showing IP set to 192.168.1.100 and subnet mask set to 255.255.255.0

In this case, I'm using 192.168.1.100, and my subnet mask is set to 255.255.255.0. That means that my local area network consists of all ip numbers between 192.168.1.0 through 192.168.1.255, or in CIDR notation, 192.168.0.0/24. The subnet mask determines how large my computer thinks my local area network is, and generally speaking, your local area network is considered to be a trusted network space (esp. if it's in your house).

CIDR notation can be complicated, but the basic rules are these--start with your IP number:
Now, with your text editor, find in Firewall.simple the line:
TrustedHosts="192.168.0.0/16";
And change that so that the network matches whatever your network range is. Now, save the file and run:
sudo ./Firewall.simple start
Next, go to Shield's Up and run another test. You'll most likely find that port 80 is open, port 443 is closed, and the others are marked as stealth. Closed means that the grc site can contact a port, but there's no service there.



Some examples

Here are some more complex examples, I recommend you play with the first one. These files are pretty well commented.

Making A Real Change

Once you have a script that defines a firewall you like, you'll want to put that script up in such a way that it runs when you start the computer. First make a directory named Firewall in a StartupItems location:

sudo mkdir /Library/StartupItems/Firewall

Then, move one of the sample script files to that subdirectory: These examples are for the 2008 version.

sudo curl -o /Library/StartupItems/Firewall/Firewall \
http://www.ibiblio.org/macsupport/ipfw/Firewall

This script is oriented to UNC Chapel Hill, but should be easily adaptable to other settings. But before you run it, you need to mark it as executable:

sudo chmod ug+x /Library/StartupItems/Firewall/Firewall
sudo chmod o-rwx /Library/StartupItems/Firewall/Firewall

Now, try running the script manually:

sudo /Library/StartupItems/Firewall/Firewall

You should see a list of rules process, and then you can check the results with both the ipfw list command and Probe My Ports. Then try to use your machine to see how well things work. You can make modifications to the script and rerun it to institute the changes without restarting your machine, but you may have to restart some of the network applications.

Lights, Camera, Action

Once you're happy with the firewall's configuration, you'll want to set the script up to run each time the machine is started. To do this, you need to add a StartupParameters.plist file in the Firewall subdirectory, with the following lines:

{
Description = "Firewall";
Provides = ("Firewall");
Requires = ("Network");
OrderPreference = "None";
Messages =
{
start = "Starting NAT/Firewall";
stop = "Stopping NAT/Firewall";
};
}

You can either cut and past these lines, or move the file via commandline thusly:

sudo curl -o /Library/StartupItems/Firewall/StartupParameters.plist \
http://www.ibiblio.org/macsupport/ipfw/StartupParameters.plist

Also, make sure this file is writeable by root only, you don't want anyone changing your firewall startup. When you next boot, the system may tell you that the setup for these files isn't safe--if that happens, let the system change the file permissions for you and reboot.

Once this is done, the firewall script should run on boot. Of course, if you're using a custom script, you shouldn't run the System Preferences Firewall, as that could mess up your settings (although it won't overwrite your script file).

Dynamic Configuration

Note that you can add firewall rules while the machine is running. This opens up some interesting possibilities for dynamic security. For example, there's a patch for ssh called timelox that tracks connection failures by ip number--if threshhold of failures from a single IP is reached, it modifies the firewall to block all traffic from that IP number. For examples of this with IPFW and Iptables, see: http://wwwx.cs.unc.edu/~hays/dev/timelox_and_TheHand/

Acknowledgements

Most of this isn't stuff I came up with on my own, I'm indebted in particular to the following folks who have provided tools or examples that went into this page:

I recommend brian's brickhouse for purchase if you're going to do a lot of firewall configuration, it's a great product.