Whilst Mac OS X includes the well-respected ipfw firewall (part of it's BSD-Unix heritage), said firewall is only as effective as its configuration. Sadly, the filtering rules set up by the OS X Firewall Preference Pane are woefully inadequate in this regard (see the References section if you want proof). Not only that, but the kernel is compiled to permit entry to all traffic not explicitly denied by a rule - the best analogy I can give you is that the door is swinging in the breeze - even Windows XP has a better firewall configuration!
Leopard introduced a new and separate application-level firewall, whose job is to control which applications on your machine are allowed to receive incoming connections from out on the 'net, but this new feature is unrelated to the lower level ipfw firewall; your GUI applications may now be better protected but the 'invisible' services remain as vulnerable as ever.
There are already many articles out there on the web that explain how to improve Apple's rudimentary ipfw configuration, however the procedure I present draws together the best of each article I've found plus some investigations made by myself and my colleagues at the CLIX forums. My article does therefore stand on the shoulders of giants, but at the same time is (I hope) concentrated and easy to follow.
- iApp Sharing
Arrange for the firewall to be initialised and configured at startup
- If you require iTunes (or iPhoto) sharing, you'll need to enable this first via the Sharing Preference Pane. See note 3 if you'd like the gory details of why the "iApps" are a special case.
- If you have not enabled iApp sharing, you may safely delete the file /Library/Preferences/com.apple.sharing.firewall.plist (if present on your version of OS X).
- The upshot of this is that the system will not boot the firewall for itself, resulting in a slightly faster bootup (see note 4 if you're interested in the details of the system's firewall startup procedure). It doesn't make a functional difference whether this auto-startup occurs or not, since the startup item installed by this HOWTO completely replaces the ruleset either way.
LaunchItem (most OS X versions).
- Open Terminal
- Create a new folder thus if it's not already present: sudo mkdir /Library/LaunchDaemons
- Download the LaunchItem property list and Firewall script from the Resource section above. Place the LaunchItem in this folder, and Firewall in /usr/local/bin.
- Set the ownership of these two files to root:admin thus:
sudo chown root:admin /Library/LaunchDaemons/com.ipfw.plist
sudo chown root:admin /usr/local/bin/Firewall
- Set the launch script to be executable by root only, thus: sudo chmod 544 /usr/local/bin/Firewall
- Open Terminal
- Create a new folder thus: sudo mkdir /Library/StartupItems/Firewall
- Download the StartupItem property list and Firewall script from the Resource section above, and place them in this new folder.
- Set the ownership of these two files to root:admin thus: sudo chown root:admin /Library/StartupItems/Firewall/*
- Set the launch script to be executable by root only, thus: sudo chmod 544 /Library/StartupItems/Firewall/Firewall
Here's the contents of the Firewall script, for those that are interested:
Install the ruleset configuration file
|# Clear down existing ruleset (including special set in ipfw2 on Tiger)
|# MUST use -q, else command output will cause termination of script processing
|/sbin/ipfw -q flush
|/sbin/ipfw -q delete set 31
|# Activate controlled ruleset
|/sbin/ipfw -q /etc/firewall.conf
|# Enable logging
|/usr/sbin/sysctl -w net.inet.ip.fw.verbose=2
|/usr/sbin/sysctl -w net.inet.ip.fw.verbose_limit=65535
- Before I start, I must stress that a ruleset is a somewhat personal thing - one size simply cannot fit all. On this basis, do not expect to be able to simply drop in the ruleset I've supplied as-is and expect it to work. I have commented the file heavily however, to aid you in deciding what rules to keep/change. Now read on.
- Open Terminal
- Download and rename the ruleset configuration file from the Resources section above, and place it in /etc
- Do not skip this step - you MUST edit the file or it will not work for you. Open the file in your editor of choice. Review the comments against each rule to decide if my choices work for you. As a minimum, you must replace all instances of X.X.X.X with the relevant address for your local network, which will usually be similar to either 10.0.0.0 or 192.168.0.0. You can work it out from your IP address in the Network System Preference - take the first three numbers you find there for the first three X's, then replace the last X with a 0. I stress again and for the last time - read the comments against every rule to be sure they match your needs.
- Set its ownership to root:admin thus: sudo chown root:admin /etc/firewall.conf
- Set its permissions to root read/write only thus sudo chmod 600 /etc/firewall.conf
- I say again, make sure the port setup in the file matches your configuration. If you do not do this some of your services may stop working.
- If you're running Panther keep your eye on the window displayed during startup since you may (very briefly) see "Configuring Firewall" shortly before you get your login prompt, though it's processed so quickly you may not see the message. More recent versions of OS X do not display messages from the services that are starting.
- Regardless of OS X version, to confirm absolutely that it has worked open Terminal and type sudo ipfw show. If all has worked as it should you'll now see your brand spanking new ruleset listed. If you do, you can rest easy in the knowledge that your box is now safe from all those worms now being written to target our favourite platform ;o>
- Improve the security of Apple's own firewall configuration tool (only necessary prior to Snow Leopard). This tool is a potential weakness since it is poorly written and does not have the correct permissions. See note 4 below for details of how to correct this.
- Enable the secure firewall configuration before the services boot up (this method works for Panther only). As things stand, the secure firewall ruleset I've described is installed after the network services it protects have booted up. This leaves a small window of weakness where the system might be compromised. This can be resolved by setting a dependency in the StartupItem that starts the network services, called SuperServer in Applespeak (xinetd in reality):
- Open Terminal
- cd /System/Library/StartupItems/IPServices/
- sudo pico StartupParameters.plist
- Insert the following between "Provides" and "Uses": Requires = ("Firewall");
- Reboot and the job's done - SuperServer will not be started until the Firewall StartupItem has run.
- Re-route the firewall log output (only necessary for Panther). When you're tweaking your rule setup it is very useful to know what is and isn't being allowed in (note the use of the 'log' keyword on rule #65534 needed to log a rule's effects). By default, Panther sends ipfw log output to secure.log where it'll be lost in the noise; it is however trivially easy to re-route it to a file all its own. N.B. This step is unnecessary for more recent OS X versions since they already log to the correct location.
In Tiger, this reconfiguration is unnecessary since the ipfw logs correctly to ipfw.log. In Leopard, ipfw logging is incorrectly routed to appfirewall.log. In Snow Leopard and above, despite appearances to the contrary in /etc/syslog.conf, ipfw logging is still incorrectly routed to appfirewall.log. I have been unable to reroute it to ipfw.log.
Notes and Limitations
- The ruleset is ipfw compliant, not taking advantages of new features in ipfw2 that was introduced with OS X 10.4. As such this guide will work with all versions of OS X from 10.3 upwards.
- As soon as the slightest change is made to the ipfw ruleset, the Firewall Preference Pane will become disabled, complaining that another firewall has been installed. This is of course untrue, we're just doing a better job of configuring the existing firewall!
- Prior to Snow Leopard the 'iApps' (iTunes, iPhoto) do not read the ipfw ruleset in order to learn whether the ports are open that they use to share libraries. Instead, this information is stored in /Library/Preferences/com.apple.sharing.firewall.plist, which is written to by the Firewall Preference Pane. Combine this with the limitation no. 2 above and a problem arises - you cannot add/remove the iTunes and iPhoto firewall ports once you've inserted a new ruleset. The solution is to enable iApp sharing via the Sharing Preference Pane before enabling the custom ruleset; this is safe since you can close the relevant ports via ipfw if you later wish to disable sharing.
- Prior to Snow Leopard OS X uses an Apple-written tool called FirewallTool (located at /System/Library/PrivateFrameworks/NetworkConfig.framework/Versions/A/Resources on Panther and, I understand, at /usr/libexec on Tiger and Leopard) to en/disable the firewall and set up it's rules during bootup. If the file /Library/Preferences/com.apple.sharing.firewall.plist exists, the firewall is started and configured with the rules in that plist plus those in /System/Library/PrivateFrameworks/NetworkConfig.framework/Versions/A/Resources/DefaultFirewallInfo.plist. Since this tool is open source thanks to the Darwin project, it's code has been reviewed and found to be an ATROCIOUS mess and as such is ripe for exploitation:o( As a minimal security measure, you are recommended to set its permissions to 0550. If you don't need iApp sharing, a better solution is to delete it entirely!