Enable IPFW on Mac OS X

Apple's OS X includes two firewalls, but only one, the application firewall, is often utilized by end users.  When you examine the firewall settings though the Settings panel, what you see is actually just the application firewall, which prevents access to the network for specific applications.  This is not a true "firewall" however as it does not actually manage network traffic to block or allow specific ports.  OS X does, however, include IPFW, which is a fully functioning BSD firewall similar to iptables for Linux. 

Configuring and enabling the IPFW firewall on OS X is a bit of a hassle.  Sadly there is no graphical user interface (GUI) that can assist in the task.  Additionally, the firewall will prevent network access to ports without any prompt or notice to the user.  This can become problematic if the user forgets that the IPFW firewall is running, or if the firewall blocks ports to prevent expected functionality.  A good example is if the firewall is not configured to allow UDP ports 67 and 68 then DHCP will fail to function, with no indication to the user about the nature of the problem.  A side effect of this behavior is the firewall will not allow the user to unintentionally open ports for remote access.

In order to make the task of installing and configuring an IPFW firewall on OS X we have developed the following shell script.  This should be run at the command line.  You can download the script in the form of a text file (using the link at the bottom of this article) or simply by copying and pasting the below code into a text file.  Once complete, open a terminal window and navigate to the file location.  Be sure to modify the file like so:

$ chmod +x ipfw.sh

In order to allow the file to be executed.  Then run the script using:

$ sudo ./ipfw.sh

And follow the onscreen instructions.  The file is as follows:

# Mac OS X IPFW firewall installation script
# Author: Justin C. Klein Keane <jukeane@sas.upenn.edu>
# Last modified: 24 August, 2012
echo This script will set up a rudimentary IPF firewall on
echo your machine.  By default this firewall will allow
echo only SSH connections, udp ports 67 and 68 for DHCP.  
echo You can modify the configuration scripts at any time
echo after this installer is complete.
echo Configuration file at /etc/firewall.conf
echo Press enter to continue...
echo Writing config file...
if ! touch /etc/firewall.conf ; then
    echo Sorry, I think you may need to be running as root.
    echo Try using the sudo command.
    echo Cannot touch the /etc/firewall.conf file.
    echo Exiting due to the previous error.
echo -e "# Sample IPFW config" > /etc/firewall.conf
echo -e "# by Justin C. Klein Keane <jukeane@sas.upenn.edu>" >> /etc/firewall.conf
echo -e "# NB: This script is initiated by /usr/local/sbin/firewall.sh" >> /etc/firewall.conf
echo -e "#     firewall.sh is in turn controlled by launchd from the " >> /etc/firewall.conf
echo -e "#     config at /Library/LaunchDaemons/com.apple.firewall.plist" >> /etc/firewall.conf
echo -e "# " >> /etc/firewall.conf
echo -e "# Permit loopback" >> /etc/firewall.conf
echo -e "add 100 allow ip from any to any via lo*" >> /etc/firewall.conf
echo -e "# Permit outbound connections to maintain state" >> /etc/firewall.conf
echo -e "add 120 allow tcp from any to any out keep-state" >> /etc/firewall.conf
echo -e "add 130 allow udp from any to any out keep-state" >> /etc/firewall.conf
echo -e "# Permit incoming SSH" >> /etc/firewall.conf
echo -e "add 140 allow tcp from any to any dst-port 22" >> /etc/firewall.conf
echo -e "add 150 allow udp from any to any dst-port 67" >> /etc/firewall.conf
echo -e "add 160 allow udp from any to any dst-port 68" >> /etc/firewall.conf
echo -e "# Default deny with logging (final rule)" >> /etc/firewall.conf
echo -e "add 65534 deny log logamount 1000 ip from any to any in " >> /etc/firewall.conf
echo done.
if ! chown root:admin /etc/firewall.conf ; then
    echo "Failed to set the firewall.conf permissions."
    echo Exiting due to the previous error.

echo "Setting up firewall control script at /usr/local/sbin/firewall.sh"

if [ ! -d /usr/local ]; then
  mkdir /usr/local
if [ ! -d /usr/local/sbin ]; then
  mkdir /usr/local/sbin

if ! touch /usr/local/sbin/firewall.sh ; then
    echo Cannot touch the /usr/local/sbin/firewall.sh file.
    echo Exiting due to the previous error.

echo -e "#!/bin/sh" > /usr/local/sbin/firewall.sh
echo -e "# Flush existing rules" >> /usr/local/sbin/firewall.sh
echo -e "/sbin/ipfw -q flush" >> /usr/local/sbin/firewall.sh
echo -e "# Run IPFW and load custom rules" >> /usr/local/sbin/firewall.sh
echo -e "/sbin/ipfw -q /etc/firewall.conf" >> /usr/local/sbin/firewall.sh
echo -e "# Enable detailed logging to syslog" >> /usr/local/sbin/firewall.sh
echo -e "/usr/sbin/sysctl -w net.inet.ip.fw.verbose=1" >> /usr/local/sbin/firewall.sh

# Set permissions
chown root:admin /usr/local/sbin/firewall.sh
chmod 544 /usr/local/sbin/firewall.sh

# Set up the plist file finally
echo "Setting up plist file at Library/LaunchDaemons/com.apple.firewall.plist"

if ! touch /Library/LaunchDaemons/com.apple.firewall.plist ; then
    echo Cannot touch the Library/LaunchDaemons/com.apple.firewall.plist file.
    echo Exiting due to the previous error.

echo -e "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" > /Library/LaunchDaemons/com.apple.firewall.plist
echo -e "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0 //EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">" >> /Library/LaunchDaemons/com.apple.firewall.plist
echo -e "<plist version=\"1.0\">" >> /Library/LaunchDaemons/com.apple.firewall.plist
echo -e "<dict>" >> /Library/LaunchDaemons/com.apple.firewall.plist
echo -e "    <key>Label</key>" >> /Library/LaunchDaemons/com.apple.firewall.plist
echo -e "    <string>com.apple.firewall</string>" >> /Library/LaunchDaemons/com.apple.firewall.plist
echo -e "    <key>ProgramArguments</key>" >> /Library/LaunchDaemons/com.apple.firewall.plist
echo -e "    <array>" >> /Library/LaunchDaemons/com.apple.firewall.plist
echo -e "        <string>/usr/local/sbin/firewall.sh</string>" >> /Library/LaunchDaemons/com.apple.firewall.plist
echo -e "    </array>" >> /Library/LaunchDaemons/com.apple.firewall.plist
echo -e "    <key>RunAtLoad</key>" >> /Library/LaunchDaemons/com.apple.firewall.plist
echo -e "    <true/>" >> /Library/LaunchDaemons/com.apple.firewall.plist
echo -e "</dict>" >> /Library/LaunchDaemons/com.apple.firewall.plist
echo -e "</plist>" >> /Library/LaunchDaemons/com.apple.firewall.plist

chown root:admin /Library/LaunchDaemons/com.apple.firewall.plist

echo "Complete."
echo "Starting the new firewall..."
launchctl load /Library/LaunchDaemons/com.apple.firewall.plist
echo "Firewall now set with the following settings:"
ipfw show

echo Done.

osxipfw.sh4.82 KB