Monday, November 13, 2006

A Firewall Script

In an earlier post I talked about Apple's simple firewall controls. These are just a front-end for the BSD-derived ipfw utility. To have more control over the firewall configuration, I'm now using a script (run as a StartupItem) to configure the firewall. I stole the script and associated configuration file from this site at the University of Washington, then modified the script slightly. The end result follows:

#!/bin/sh
#
# Based on work done by Stefan Arentz:
# http://wopr.norad.org/articles/firewall/
#
# And other random information sources, e.g. ipfw(8), "Building
# Internet Firewalls" by O'Reilly, and random sample ipfw scripts.
#
######################################################################
#
# CONSTANTS

IPFW=/sbin/ipfw
SYSCTL=/usr/sbin/sysctl

# attempt to dynamically set the interface to use
# in case Apple changes the driver name at some point
# (everything I have seen thus far has been en#)
#INTERFACE=`ifconfig -a | egrep -i '^[A-Za-z0-9]+: ' | grep -v lo | cut -f 1 -d :`
INTERFACE=en0

#MYADDR=`ifconfig $INTERFACE | grep inet | awk '{print $2}'`

######################################################################
#
# MAIN

# required startup script statements
. /etc/rc.common
ConsoleMessage "Configuring Firewall"

# for diagnostics
#logger "Firewall: INTERFACE=$INTERFACE; MYADDR=$MYADDR"

# Turn on logging of packets for debugging/auditing.
if [ `$SYSCTL -n net.inet.ip.fw.verbose` == 0 ] ; then
$SYSCTL -w net.inet.ip.fw.verbose=1
fi

# limit is how many packets will be logged for a specific rule
# good for avoiding fill-the-disk logging attacks
if [ `$SYSCTL -n net.inet.ip.fw.verbose_limit` != 1000 ]; then
$SYSCTL -w net.inet.ip.fw.verbose_limit=1000
fi

######################################################################
#
# RULES

# JAM 2001-10-02 new 10.1 bug (?) stalls boot when starting up not
# connected to any network, with system.log entries of:
#
# mach_kernel: ipfw_load
# mach_kernel: IP packet filtering initialized, divert enabled,
# rule-based forwarding enabled, default to accept, logging disabled
# /sbin/ipfw: NetInfo timeout connecting to local domain, sleeping
#
# therefore, wrap script as background subshell until figure out why
# stall is taking place

{
# flush all the rules
$IPFW -f flush

# Local loopback interface is open
$IPFW add allow ip from any to any via lo0
# however, we otherwise ban loopback address traffic
$IPFW add deny all from any to 127.0.0.0/8
$IPFW add deny all from 127.0.0.0/8 to any

# ip-options (per FreeBSD Security Advisory: FreeBSD-SA-00:23.ip-options)
$IPFW add deny log ip from any to any ipoptions ssrr,lsrr,ts,rr


# Allow TCP through if setup succeeded
$IPFW add pass tcp from any to any established

# Allow IP fragments to pass through
#$IPFW add pass log all from any to any frag

######################################################################
#
# SERVICES

# allow DHCP & DNS
$IPFW add allow udp from any 67 to any 68 in via $INTERFACE
$IPFW add allow udp from any 53 to any in via $INTERFACE

# send RST back on auth to timeout remote requests faster
$IPFW add reset tcp from any to any 113 in via $INTERFACE setup

# allow NTP server responses
$IPFW add allow udp from any 123 to any in via $INTERFACE
$IPFW add allow udp from any 123 to any in via $INTERFACE

# let SSH, WWW
$IPFW add allow tcp from any to any 22,80 in via $INTERFACE setup

# allow AFP services to work (ICMP ping also required)
$IPFW add allow tcp from any to any 427 in via $INTERFACE setup
$IPFW add allow udp from any to any 427 in via $INTERFACE
$IPFW add allow tcp from any to any 548 in via $INTERFACE setup
$IPFW add allow udp from any to any 548 in via $INTERFACE

# Allow Zebedee
$IPFW add allow tcp from any to any 11965 in via $INTERFACE setup

# block all other incoming TCP/UDP traffic
$IPFW add 65435 reset tcp from any to any in via $INTERFACE
$IPFW add 65435 unreach port udp from any to any in via $INTERFACE

######################################################################
#
# ICMP

# allow certain ICMP through (allows ping, traceroute, plus
# the required source quence and similar)
$IPFW add allow icmp from any to any icmptypes 0,3,4,8,11,12

# silent block on router advertisements
$IPFW add deny icmp from any to any icmptypes 9

# drop all other ICMP
$IPFW add 65435 deny icmp from any to any

} &

I've added in a rule allowing zebedee (which I use for tunneling VNC traffic) and I've hard-coded the interface name as "en0", since under OS X 10.4 on my test machine the original code returns a long list of interfaces, rather than a single interface name:

gif0
stf0
en0
en1
wlt1
fw0
It would be useful to modify the script so that it sets the same firewall rules for all of these interfaces.

No comments: