Quick and dirty fix for 10.2

October 2nd, 2002: 10.2 changes a few things. I don't have time to update the web page, but David Mackler provided me with a new set of rules that include modification to get the IP correctly, use the new state-aware firewall, allow rendezvous to work properly and a new StartupItem. Read the present web page, but use this file instead of the one provided below.

Setting up firewall rules on Mac OS X 10.1

Preamble

This page describes how I set up firewall rules on my Mac OS X 10.1 home computer using the built-in firewall (ipfw).
You need to use a terminal. If you want something simpler, go get the excellent Brick House or Firewalk which both have simple options and a nice interface. However, if you have a dynamic IP, you can't use them (at least the last time I checked): the IP address changes and you need to change the rules to reflect that. Moreover, I have a tiny private network to share files with an iBook which means I actually have two IP addresses: the outside IP (on interface pppoe0, dynamic IP) and an internal IP (inside interface en0, fixed IP 192.168.0.1). This configuration allows me to plug in an iBook at anytime at the back of my G4 and log in to kill jobs if I ever lose the console (not that I ever did, but it's nice to have it there).

With a dynamic IP, you must make sure that the firewall is always synchronized with your IP. For instance, if you disconnect, then reconnect, your IP might have changed, but your firewall rules have not. You must then change the firewall rules to reflect the change of your IP. A script is available at the bottom of this page to do just that.

It might come to your attention at that point that your DNS Agent seems to timeout pretty often, especially when you set up the rules. I don't know why that is.

The layout for the code (black outline, dark background) is identical to the one used on the very useful Stepwise. I thought it looked good.

Removed the "divert natd" rule for those who don't run natd. If you do (for instance if you have a second interface set up for Appletalk, you must uncomment it). Most people will not require natd. The rule is simple: if you don't know what it is, you highly likely do not need it. If you do though, make sure you start natd before activating the firewall with the divert rule. If you don't you will be locked, simply start natd (which in these instructions is in /Library/StartupItems/NAT/) by typing /Library/StartupItems/NAT/NAT

Adapted the syntax of a few rules because they did not work on OS X 10.1. For instance, the dynamic rules options keep-state and check-state do not work on OS X. Here is an explanation of what they are.

Modify the file to suit your needs: outside interface (pppoe? ppp?) and inside interface (necessary?) as well as mail, ns1, ns2 and ntp. I left all of the original comments in and commented out the lines I modified. I found a few interesting tutorials on Firewalls at OnLamp.com if you want more info. Hopefully, my modifications are correct. Feel free to email me for corrections.

Download

Dynamic IP: The files ip-up and ip-down (to set up the firewall with a dynamic IP adresse i.e. modem, see below)

Static IP: A file called Firewall and StartupParameters.plist which you copy into /Library/StartupItems/Firewall to start firewall at boot time if you have a static IP address (see below)

The firewall rules

The rules are described below.
To execute it, do sh rc.firewall.current as root, or
sudo sh rc.firewall.current. Once you know it works, you can set up the computer to start the firewall automatically. The log is found at /var/log/system.log. If you make a mistake
and it locks up, wait or type ipfw flush, look at the system.log and start over. You can look up every denied connection by typing grep "Deny" /var/log/system.log. I highlited my changes in blue.

You need to modify the document in order to provide some information about your configuration, right after the line that says: # Define your variables.

oif: your outside interface. This can be obtained with the following command in the terminal: netstat -r | grep default. The last word (pppoe0, en0, ppp0, etc) is your outside interface.

oip: your outside ip address. If you have a dynamic IP address, leave the line as is to obtain it every time your run the script. This tiny script should do it. You can test it on the command line.

onwr: your outside network range. $oip/8 is highly likely correct, but it could also be $oip/12 or $oip/16.

iif, inwr and iip: your internal interface, internal network range and internal ip. If you do not have any, you should leave everything as is.

rc.firewall.current:

#!/bin/sh
# Originally found at http://www.bsdtoday.com/2000/December/Features359.html
# By Peter Brezny
# Modifications done to support dynamic IP and default OS X configuration
# Available at: http://www.novajo.ca/firewall.html
#
# Simple stateful network firewall rules for IPFW with NAT v. 1.01
# See bottom of file for instructions and description of rules
# Created 20001206206 by Peter Brezny, pbrezny@purplecat.net (with a great
# deal of help from freebsd-security@freebsd.org). Specific questions
# about the use of ipfw should be directed to freebsd-ipfw@freebsd.org or
# more general security questions to freebsd-security@freebsd.org.
# Use this script at your own risk.
#
# if you don't know the a.b.c.0/xx notation for ip networks the ipsubnet
# calculator can help you. /usr/ports/net/ipsc-0.4.2
#
###########################
# Note: This does not apply to Mac OS X
#
# Brief Installation instructions
#
# Name this script /etc/rc.firewall.current
# Edit /etc/rc.conf to include
# gateway_enable="YES"
# firewall_enable="YES"
# firewall_script="/etc/rc.firewall.current"
# natd_enable="YES"
# natd_interface="***" #replace with your external ifX
# natd_flags="-dynamic"
# Make sure your kernel is configured to handle ipfw and natd
# See the FreeBSD handbook on how to do this.
#
############################
# Make sure logging is enabled (disabled by default)
if [ `/usr/sbin/sysctl -n net.inet.ip.fw.verbose` == 0 ] ; then
/usr/sbin/sysctl -w net.inet.ip.fw.verbose=1
fi
#
# Define your variables
#
fwcmd="/sbin/ipfw" #leave as is if using ipfw
oif="ppp0" #set to outside interface name: for DSL ppp0 in 10.1.x, pppoe0 in 10.0.x
#set to outside ip address
oip=`/sbin/ifconfig $oif| grep inet | awk '{ print $2 }'`;
onwr="$oip/8" #set to outside network range
iif="en0" #set to internal interface name
inwr="192.168.0.0/16" #set to internal network range
iip="192.168.0.1" #set to internal ip address
mail="your.mail.server" # mail server sometimes requires 113
ns1="your.name.server" #set to primary name server best if = oif
ns2="your.name.server" #set to secondary name server best if = oif
ntp="your.time.server" #set to ip of NTP server or leave as is
#
# End of required user input if you only intend to allow ssh connections to
# this box from the outside. If other services are required, edit line 96
# as necessary.
#
###
# Rules with descriptions
#
# Basic rules: there is no need to modify anything in this first section.
# This is the bare minimum to block simple spoofing.
###
#
# Force a flush of the current firewall rules before we reload
$fwcmd -f flush
# Allow your loop back to work
$fwcmd add allow all from any to any via lo0
# Prevent spoofing of your loopback
$fwcmd add deny log all from any to 127.0.0.0/8
# Allow DNS traffic from internet to query your DNS (for reverse
# lookups etc).
# (Seems to be necessary to put it here to make sure lookups are allowed ASAP)
# $fwcmd add allow udp from any 53 to $ns1 53 via $oif (Good with dynamic rules)
# $fwcmd add allow udp from any 53 to $ns2 53 via $oif (Good with dynamic rules)
$fwcmd add allow udp from any 53 to $oip via $oif
$fwcmd add allow udp from $oip to any 53 via $oif
#
# Stop spoofing of your internal network range
$fwcmd add deny log ip from $inwr to any in via $oif
#
# Stop spoofing from inside your private ip range
$fwcmd add deny log ip from not $inwr to any in via $iif
#
# Stop private networks (RFC1918) from entering the outside interface.
$fwcmd add deny log ip from 192.168.0.0/16 to any in via $oif
$fwcmd add deny log ip from 172.16.0.0/12 to any in via $oif
$fwcmd add deny log ip from 10.0.0.0/8 to any in via $oif
$fwcmd add deny log ip from any to 192.168.0.0/16 in via $oif
$fwcmd add deny log ip from any to 172.16.0.0/12 in via $oif
$fwcmd add deny log ip from any to 10.0.0.0/8 in via $oif
#
# Stop draft-manning-dsua-01.txt nets on the outside interface
$fwcmd add deny log all from 0.0.0.0/8 to any in via $oif
$fwcmd add deny log all from 169.254.0.0/16 to any in via $oif
$fwcmd add deny log all from 192.0.2.0/24 to any in via $oif
$fwcmd add deny log all from 224.0.0.0/4 to any in via $oif
$fwcmd add deny log all from 240.0.0.0/4 to any in via $oif
$fwcmd add deny log all from any to 0.0.0.0/8 in via $oif
$fwcmd add deny log all from any to 169.254.0.0/16 in via $oif
$fwcmd add deny log all from any to 192.0.2.0/24 in via $oif
$fwcmd add deny log all from any to 224.0.0.0/4 in via $oif
$fwcmd add deny log all from any to 240.0.0.0/4 in via $oif
#
###
# User rules: Some of the rules below are dependent on your configuration.
# They might require some adjustments. They are emphasized with the
# word "ADJUST".
###
# ADJUST: If you use NATD (for your 192.168.0.1 interface for instance)
# you must uncomment the following:
# Divert all packets through natd
# $fwcmd add divert natd all from any to any via $oif
#
# Allow all established connections to persist (setup required
# for new connections).
$fwcmd add allow tcp from any to any established
#
# ADJUST: Allow incoming requests to reach the various services.
# To allow multiple services you may list them separated
# by a coma, for example ...to $oip 22,25,110,80 setup
# If you have an internal interface (e.g. if you do not run NATd)
# uncomment the second line to enable AppleTalk on it.
# $fwcmd add allow tcp from any to $oip 22 setup
$fwcmd add allow tcp from any to $oip 21,22,80,548 setup
# $fwcmd add allow tcp from any to $iip 548 setup via $oif
#
# NOTE: you may have to change your client to passive or active mode
# to get ftp to work once enabled, only ssh, ftp and appletalk enabled by default.
# 21:ftp enabled by default
# 22:ssh enabled by default
# 23:telnet
# 25:smtp
# 110:pop
# 143:imap
# 80:http
# 443:ssl
# 548:appleshare enabled by default
#
# Allow icmp packets for diagnostic purposes (ping traceroute)
# you may wish to leave commented out.
$fwcmd add allow icmp from any to any
#
# Allow required ICMP
$fwcmd add allow icmp from any to any icmptypes 3,4,11,12
#
# Allow time update traffic
$fwcmd add allow udp from $ntp 123 to $oip 123
# Politely and quickly rejects AUTH requests (e.g. email and ftp)
$fwcmd add reset tcp from $mail to $oip 113
#
# Checks packets against dynamic rule set below.
# $fwcmd add check-state (Does not work in OS X)
#
# Allow any traffic from firewall ip to any going out the
# external interface
# $fwcmd add allow ip from $oip to any keep-state out via $oif (Does not work in OS X)
$fwcmd add allow ip from $oip to any out via $oif
#
# Allow any traffic from local network to any passing through the
# internal interface
# $fwcmd add allow ip from $inwr to any keep-state via $iif (Does not work in OS X)
$fwcmd add allow ip from $inwr to any via $iif
#
# Deny everything else
$fwcmd add 65435 deny log ip from any to any
#
#####################################################
#
# End firewall script.

Installing and starting the firewall

You need either option 1) or option 2), not both.

1) Dynamic IP address with ppp connection (most modems)

There used to be a shell script that would monitor the IP address and adjust the firewall accordingly when required. This broke with 10.1.x. However, I found out in the mean time that there is a much simpler way of doing that. With a ppp connection (i.e. modem), two scripts are automatically called when the connection is up and when the connection is taken down. They are /etc/ppp/ip-up and /etc/ppp/ip-down (see man pppd for more info in the terminal). Hence, one simply has to start the firewall in /etc/ppp/ip-up and flush the firewall in /etc/ppp/ip-down. I have provided two very simple ip-up and ip-down scripts. It assumes you will keep rc.firewall.current in /usr/local/sbin/. The script ip-up also works around a small bug in natd for 10.1 where it does not reset itself after a change of IP address (look at ip-up for more info, but it is pretty trivial).

You do not need anything in the /Library/StartupItems/ directory and if you do have a /Library/StartupItems/Firewall directory, you should delete it. You are done.

2) Static IP address without ppp

The best thing to do with a static IP address is to set up the firewall at boot time by adding an item in /Library/StartupItems/Firewall/. In the archive provided above, you will find Firewall and StartupParameters.plist which you will copy into /Library/StartupItems/. It assumes you will keep rc.firewall.current in /usr/local/sbin/.

Next time you reboot, the firewall will be up. For now, you should simply do: sudo /Library/StartupItems/Firewall/Firewall. No need to reboot, you are done.

Troubleshooting

There are a few problems when you have a firewall:

Alway check /var/log/system.log for denied connections if you have problems. Do grep "Deny" /var/log/system.log. The explanation is often there.

Is the firewall in sync with the IP address? In doubt, just run sudo sh /usr/local/sbin/rc.firewall.current again

Is NATd in sync with the firewall (if you use it) ? Check the IP address used in the divert rule sudo ipfw show | grep "divert" and the system.log. Run sudo sh /usr/local/sbin/rc.firewall.current to correct the problem.

Is NAT running ? If you have the divert rule set and NAT isn't running, you must start NAT

If you use NAT and you do not have the divert rule activated, then NAT will never see the packets.

ftp can give you trouble with a firewall if the FTP transfer is done in active mode instead of passive mode. An FTP connection is done on various ports > 49152. If the FTP server you are connectiong to is in active mode, the server will initiate the connection. With the firewall in place, this will be seen as an unauthorized incoming connection to your computer and will be rejected. On the other hand, if the FTP transfer mode is passive, your client will initiate the connection and since any outgoing connection is allowed, you will connect without a problem to the FTP server. Once the connection is established (i.e. the SYN bit of the TCP packet is set), connections are always allowed. Not every server supports it, but most do. Therefore, you might see (in your system.log) a server you know trying to connect from its port 21 to one of your ports >49152 until it times out. You must issue a passive command or configure your ftp software to do so (Interarchy , Fetch and Transmit all have an option for that: uncheck use passive; the command-line utility ftp usually defaults to active). I had some problems with Internet Explorer and I ended up changing the ftp helper application to Fetch. If you are not too paranoid, you can always just flush the firewall rules for the time of your download with the command ipfw flush. Any time you have a problem with your connection, go take a look at your system logs. If the connection is legitimate, modify the firewall rules to allow it.

You might see denied connections on port 113 (authentication) and experience long delays when connecting to an FTP server or checking your mail. Some mail servers or FTP servers try to query your computer when you connect. The Authentification connection must time out before the real connection can go on. I added a rule to allow the mail server to connect to 113, but you don't really want to do that for every FTP server on the planet. You can explicitly reject connections to port 113 (using reset is better, thanks Bob K). This would not happen if the dynamic rules (keep-state and check-state) options worked under OS X.