As with many of the Mac OS X GUI system tools (Web, FTP, Windows Sharing, and so forth) the Firewall and Internet Sharing functions are nothing more than a GUI on top of BSD command-line utilities ” ipfw and natd , respectively.
You can gain far better control over your system by using the command-line tools directly. Firewall functions can be built into scripts, or used with intrusion detection tools such as Snort and Guardian (see Chapter 18, "Alarm Systems: Intrusion Detection") to dynamically protect against attacks as they occur. Although the GUI tools are probably enough for most users, they lack the flexibility that many high-end users and network administrators require.
The ipfw tool is used to create, modify, and remove firewall rules from the built-in Mac OS X firewall. The first step is to list the active firewall rules with ipfw list (you will need to be root or use sudo to access ipfw ). By default, your Mac already has a single rule, even if you aren't blocking traffic:
# ipfw list 65535 allow ip from any to any
Each line starts with an arbitrary number between 1 and 65535, followed by the rule. Packets are matched against rules in the order of the line numbers . When a match is found, that rule is interpreted, and the proper action is carried out (the packet is dropped, accepted, and so on). In most cases, no further matching is carried out. For the default rule ( 65535 ), IP packets from any machine to any machine are allowed.
Try configuring a firewall by using Apple's GUI, allowing Remote Login (SSH, port 22) and denying all others. Then use ipfw list to display the results:
# 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 188.8.131.52/3 to any in 02040 deny tcp from any to 184.108.40.206/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 22 in 12190 deny tcp from any to any 65535 allow ip from any to any
Although you can certainly use the GUI and command-line tools simultaneously , you should choose one or the other when creating custom configurations. Firewall rulesets usually start by flushing any existing ruleset. If you attempt to write your own scripts and use Apple's, you may find that one is being replaced by the other, depending on the startup order.
A number of rules are added automatically to the firewall in addition to the rule for allowing Remote Login. This is a line-by-line breakdown:
02000 . Allow any IP packets to/from any destination to be passed by the loopback interface ( lo ).
02010 . Disallow any packets purporting to be from 127.0.0.0/8 (loopback).
02020 . Disallow any incoming IP packets to 127.0.0.0/8 (loopback).
02030 . Deny incoming TCP packets from 220.127.116.11/3 (multicast).
02040 . Deny incoming TCP packets to 18.104.22.168/3 (multicast).
02050 . Allow any TCP packet to be sent to any outside machine.
02060 . Allow any TCP packets to pass as long as they are part of an established connection.
02070 . Allow any incoming TCP packets to port 22 (SSH).
12190 . Deny all other TCP packets.
65535 . Allow all other IP packets.
The brunt of the firewall's work is performed by the rule in 12190, which disallows all TCP packets from anywhere to anywhere . The rest of the rules are simply exceptions that "poke holes" in the firewall for various services. Apple's ruleset explicitly denies values that should not be seen on your interface (lines 02010-02040). Typically firewalls include rules to deny incoming packets from all IANA (http://www.iana.org/) “reserved addresses (see RFC 3330: http://www.potaroo.net/ietf/rfc/rfc3330.txt), and you may wish to add these addresses to your firewall.
Firewall rules are typically composed of two things: packet matching criteria (source, destination, protocol, interface, and so on) and an action (allow, drop, and so on). For example, if I wanted to set up a firewall ruleset to allow incoming requests for my personal Web server from the subnet 192.168.0.0/16, I could add a rule like this:
ipfw add allow tcp from 192.168.0.0/16 to any 80
Any Web traffic (port 80) from the subnet 192.168.0.0/16 would immediately be allowed to pass through the firewall.
As was mentioned earlier, the ordering of the rules makes a difference in how they are interpreted. If you're starting with a blank slate, adding the previous rule has no purpose as there is already the default rule to allow all packets to pass ( 65535 ). It is at this point that you must make the decision about how your firewall will be configured moving forward: Will you start with an entirely open system and close it off as the need arises, or will you begin with a closed system and open it as necessary? Assuming you choose to keep the system closed and open specific ports when appropriate, you'll need a set of rules very similar to what Apple provides to start the firewall correctly: The loopback interface must be left open, for example. If you take the opposite approach, rules can be added at will, because everything starts out open.
Typically, rules are stored in a file and read by ipfw all at once. This eliminates the need to invoke the command repeatedly. A perfectly legitimate script to accomplish the previously defined task of opening only port 80 for the 192.168.0.0/16 subnet looks like this:
flush add allow ip from any to any via lo add allow tcp from 192.168.0.0/16 to any 80 add deny ip from any to any
These rules can be stored sequentially in a file (such as /etc/firewall.rules ), then loaded with ipfw /etc/firewall.rules .
# ipfw /etc/firewall.rules Are you sure? [yn] y Flushed all rules. 00100 allow log ip from any to any via lo0 00200 allow tcp from 192.168.0.0/16 to any 80 00300 deny ip from any to any
The most common use of ipfw that you'll encounter is adding a new rule. The syntax for this is add [Rule number 1-65535] <action> [log [logamount <number> ] <protocol> from <source> to <destination> [interface spec] .
Rule numbers are optional and, if not assigned manually, will be automatically added in increments of 100, starting at 00100.
Actions choose what to do with a packet that matches. Common actions include:
allow . Allow the packet to pass, stop processing rules.
deny . Discard the packet, stop processing rules. No response is returned to the remote device.
unreach <code> . Discard the packet and attempt to send an ICMP response (0-254) to the remote device and stop processing rules.
reset . Discard the packet, send a TCP reset notice, and stop processing rules.
Immediately following an action is the optional keyword log . If specified, any matches to the rule are logged via syslogd with a facility of LOG_SECURITY . If you intend to use logging, however, you must first enable it in the kernel by typing /usr/sbin/sysctl -w net.inet.ip.fw.verbose=1 (as root):
# /usr/sbin/sysctl -w net.inet.ip.fw.verbose=1 net.inet.ip.fw.verbose: 0 -> 1
Logging must be enabled each time your computer restarts. If you intend to build a firewall script that runs at bootup , you should activate logging at the same time.
Logs should only be written to help debug firewall problems, or to document network violations. Logging all traffic, for example, is extremely hard on your system's resources and may result in dropped packets.
To limit logs to only a certain number of entries per rule, add logamount <number> after the log keyword. This limits the total number of messages a rule can generate. Setting the number to zero removes the limit.
Protocols can be given as a number or name (see /etc/protocols for a list of IP protocols by number): tcp , udp , icmp , and ip (all) are commonly used and constitute most traffic on a TCP/IP network.
The source and destination of a rule can be given in the form of <address/mask> (for example, 192.168.0.0/16), as a hostname, or if you use the shortcuts me or any to match the firewall box's address or any address. The not keyword can be included in front of a source or destination to match anything except that address. To further narrow down a match, one can add a port, port list (#,#,#), port range (#-#), port and mask pair (#:#), or service name ( http , ssh , and so on ”see /etc/services for details).
Finally, an interface can be specified in the rule to differentiate between multiple active network cards or the direction that traffic is flowing . The following is a list of commonly used interface specifications:
in . Match only incoming packets.
out . Match only outgoing packets.
via <interface> . Match packets going through the named interface.
recv <interface> . Match packets received by the named interface.
xmit <interface> . Match packets transmitted by the named interface.
For example, the rule to allow Web connections from the 192.168.0.0/16 subnet could be applied to only connections coming through en1 (the second ethernet interface ”often an AirPort card) if you were to modify it to read add allow tcp from 192.168.0.0/16 to any 80 via en1 .
As your firewall works, you can view what it has done (aside from logging) by checking the counter values for each rule. For each rule, internal counters are maintained for the number of packets that have matched the rule, the number of bytes matched, and a timestamp of the last match. You can view these counters by adding the switches -a and -t (counters and timestamp respectively) to ipfw list :
# ipfw -t -a list 00100 4215 555183 Mon Dec 30 23:07:55 2002 allow log ip from any to any via lo0 00200 0 0 allow tcp from 192.168.0.0/16 to any 80 00300 109 24884 Mon Dec 30 23:07:34 2002 deny ip from any to any 65535 0 0 allow ip from any to any
Rules can be referred to by the rule number for deleting rules or resetting rule counters. To delete a firewall rule, use ipfw delete <rule number> . You can reset a counter with ipfw reset <rule number> . Depending on whether you've configured a packet logging limit, you may need to reset the log counter after the maximum count is reached; this action can be performed with ipfw resetlog <rule number> .
For more information on ipfw , read the man page and consult the resource list at the end of this chapter. Misconfiguring a firewall can cause problems with your computer and your network, so it isn't a task to take lightly. Remotely configuring a firewall isn't advised as one can very easily block his/her own access and lose control of the firewall.
The ipfw man pages describe traffic-shaping facilities of the firewall that are currently unavailable in the shipping ipfw configuration. Traffic-shaping enables an administrator to control the network bandwidth available to a given process. Thankfully, a third-party tool, Carrafix (http://www.carrafix.com/) brings this feature to Mac OS X with an easy-to-use GUI.
Firewall configuration can be extremely complex, depending on your network. Although ipfw can be used directly, there are a number of third-party firewall tools that can make setting up a firewall easier, and even include prewritten rules for blocking a known network attack. Check out these tools for a more convenient way to set up your firewall.
BrickHouse , http://personalpages.tds.net/~brian_hill/brickhouse.html.
Firewalk X , http://www.pliris-soft.com/products/firewalkx/index.html.
Impasse , http://glu.com/products/impasse/.
sunShield , http://homepage.mac.com/opalliere/Menu3.html.
Norton Personal Firewall , http://www. symantec .com/sabu/nis/npf_mac/.
NetBarrier , http://www.intego.com/netbarrier/home.html.
Sharing your network connection to an entire LAN via NAT is an effective way to provide network access to multiple machines without allowing incoming network access. Apple enables you to do this, but their single-button solution falls short in several areas ”most notably in the ability to forward incoming connections to LAN-side computers. This, for example, allows an administrator to create internal Web servers, mail servers, and so forth that, although behind the NAT firewall, are still accessible from the Internet.
Let's walk through the steps necessary to create Internet Sharing by hand. To do this, assume that there are two interfaces being used: en0 , with a live Internet connection, and en1 , an interface that will feed the internal network.
To start, create a basic configuration file ( /etc/natd.conf ) for the natd daemon:
interface en0 same_ports yes use_sockets yes unregistered_only yes
If you do not have a static Internet connection, you should also add the line dynamic yes to the file as well; this enables the process to adapt to changes on the Internet-connected interface.
The same_ports and use_sockets directives increase the reliability of connections by attempting to maintain a specific port number for outgoing connections and allocating sockets for direct file transfers. The setting unregistered_only forces the server to alter only packets coming from private addresses (which is exactly what should be appearing on the LAN side of the connection).
If you would like to log packets that are blocked at the NAT device, add the line log_denied no .
Next, configure your internal interface (assumed en1 ) with a private address, such as 10.0.1.1 . Your internal network will be based on this address and will use it as the gateway to the rest of the Internet.
Now, start the natd process by using the /etc/natd.conf configuration file (this must be performed as root ):
# /usr/sbin/natd -f /etc/natd.conf
Before you can begin using NAT, IP forwarding must be enabled in the kernel:
# sysctl -w net.inet.ip.forwarding=1
To start forwarding at bootup, change the IPFORWARDING=-NO- line in /etc/hostconfig to read IPFORWARDING=-YES- .
Finally, using ipfw , add a divert rule that will route packets from the internal network to the external interface ( en0 ):
# ipfw add divert natd ip from any to any via en0
Internal computers based on this configuration should be set with the following network configuration
IP Address : 10.0.1.x (any node other than 10.0.1.1 )
Subnet Mask : 255.255.254.0
Router : 10.0.1.1
DNS : <same as ISP-assigned DNS>
If desired, you can enable a caching name server on your NAT router by changing the line DNSSERVER=-NO- in /etc/hostconfig to read DNSSERVER=-YES- . This, however, starts an additional network service that, if inappropriately configured, will be exposed on the Internet side of your connection.
To run publicly accessible services on a NAT-based network, incoming connections must be mapped from the single addressable network address ( en0 ) to any of the computers connected via en1 . The natd syntax for this operation is natd -n <Internet interface> -redirect_port <protocol> <internal address:port> <external port> . For example, to redirect incoming TCP connections on port 80 of the Internet-connected side of the NAT machine to a public Web server on the LAN address of 10.0.1.100 , one would issue the following command:
natd -n en0 -redirect_port tcp 10.0.1.100:80 80
Like the firewall configuration, you can easily add the natd setup to a script that runs at startup. See Chapter 11, "Introduction to Mac OS X Network Services," for examples of startup scripts.