IP Filter on Solaris
Ron McCarty
If you are responsible for host or network security within your organization, then you should consider adding ipf to your network tool kit. IP filter, or ipf (http://coombs.anu.edu.au/~avalon/ip-filter.html), is an advanced packet filter with an easy to understand configuration, but it has some nuisances that may seem strange to those who have used other packet filtering systems. However, ipf should not be avoided because of its quirks, since they can easily be overcome. This article will give you an introduction to ipf and its rule sets, as well as its logging and administration capabilities. Ipf can also act as a Network Address Translator (NAT), but that isn't covered in this article.
Ipf supports filtering on incoming and outgoing packets. Incoming packets are packets that are destined for the packet filters interface, and outbound packets are packets that have received a routing decision by the node where the packet filter is running, and would normally be placed on the outgoing interface destined to some other node.
I usually prefer to filter incoming packets. The major advantage to filtering on incoming packets is increased performance. Packets that are dropped at the incoming interface do not create additional load by making a routing decision before the packet is dropped, whereas a packet that is dropped by an outgoing rule has unnecessarily used CPU processes to determine the proper interface out of which to route the packet.
However, there are environments where filtering outgoing packets makes sense, such as using ipf to harden hosts that are not multi-homed, such as Web servers or workstations connected directly to the Internet. This is becoming more common with the availability of broadband in small office and home office environments.
Ipf supports packet filtering for the common protocols in the TCP/IP family, including IP, TCP, UDP, and ICMP. Additionally, ipf supports advanced options, including state tables, (NAT), and fragmentation.
State table support was first made popular with Checkpoint's Firewall-1. State tables maintain lists of active sessions to ensure sessions were actually generated before allowing the packet to pass. This keeps the packet filter from passing traffic that would normally allow TCP packet replies through, based on the TCP flags that can easily be spoofed by programming, or set by any number of security and hacker tools. Additionally, state tables can be used to allow extra security to be added to UDP, since UDP does not have support of established sessions.
NAT support allows internal (RFC 1918) addresses to be translated to a globally unique address or multiple unique addresses. NAT is often used to connect enterprise networks to the Internet, but are also useful for connecting corporate extranets to other corporations, or to translate addresses for organizations with overlapping addresses due to corporate takeovers. My March 2000 Sys Admin column covered NAT in depth. Although not covered here, ipf makes a good NAT.
Fragmentation support is important in environments with various frame sizes caused by having different media types, such as Ethernet and Token Ring. Some attacks are based on small fragment sizes that are not likely to be normal fragmented packets, and ipf, with the use of a special shor parameter, can also drop these special fragmented packets.
Installation
Solaris 2.6 was used for this article, but support for Solaris 7 was added with version 3.3 of ipf, and support for Solaris 8 was added with version 3.4. At the time of this article, the latest version of ipf is version 3.4.9 and can be downloaded via anonymous ftp from:
ftp://coombs.anu.edu.au/pub/net/ \
ip-filter/ip-fil3.4.9.tar.gz
These instructions assume the GNU C compiler gcc, as well as the GNU make; however, the Sun compiler and make are supported.
Place the ip_fil3.4.9.tar.gz compressed archive in your source directory. On Solaris, I usually use the /usr/share/src/. As root, move to that directory for installation:
cd /usr/share/src/
Uncompress the archive with:
gzip -d gzip-d ip_fil3.4.9.tar.gz
Untar the file with:
tar xvf ip_fil3.4.9.tar
which will create a directory structure under ip_fil3.4.9 in the /usr/share/src/ directory.
Move to ipf source directory structure:
cd ip_fil3.4.9
If you have both the GNU version of make and Sun's version, then be sure to read the COMPILE.Solaris2 file on using the Sun version of make stored in /usr/ccs/bin/:
make solaris
After make has completed, move into the SunOS5 directory:
cd SunOS5
and run:
make package
which will create and install the Solaris ipf package. Additionally, it will create /etc/init.d/ipfboot, which is linked to /etc/rc2.d/S65ipfboot. The /etc/rc2.d/S65ipfboot script runs when the system boots. The package installation routine installs the software in /opt/ipf/. This directory includes the bin, examples, and man page subdirectories. If you wish to copy the man pages to the standard directory in Solaris, use:
cd /opt/ipf/man/
cp -r * /usr/share/man/
At this point, you should be able to view the ipf man page that covers ipf and its configuration with:
man -s 5 ipf
(Running catman to recreate windex is also a good idea.)
To quickly test ipf, you can use a block all rule and fire up ipf to ensure that packets are being dropped. If you are remotely accessing the box, ensure you have access to the console to deactivate the rules to allow the remote access until configuration is complete.
Edit the ipf configuration file ipf.conf with:
vi /etc/opt/ipf/ipf.conf
and add the following lines:
block in all
block out all
Then activate the rules with:
/etc/init.d/ipfboot start
Any access to the host should now be blocked. Pings to the host should not be successful, and any access from the box will not work.
To deactivate these rules, use:
/etc/init.d/ipfboot stop
Configuration and IPF Rules
As mentioned, ipf can block traffic based upon its direction, which makes rules easier for hosts with single network interfaces (since the interface is not necessary). This means that:
block in all
is the same as block in all on le0 on a single NIC host. However, on a multi-homed box, this would block all incoming traffic, which would mean that incoming traffic on le0, le1, le2, and so on would be blocked.
The major difference between ipf and other packet filtering products is its philosophy on rules -- by default, it reads through the whole set of rules before deciding to discard or allow the OS to route the packet. For anyone new to packet filtering, this may seem acceptable and easy enough to learn; however, use caution with this method because an intentional block can be allowed through by a later rule that is intended to allow other services, but inadvertently allows the bad traffic through. To bypass this default behavior, the parameter quick can be used with a rule, which tells ipf to execute the rule immediately, as opposed to making further decisions. The quick parameter makes the rule act like the packet filters that most administrators with packet filtering experience are used to seeing. The quick parameter follows the direction of the packet -- in or out in the packet filter rule, as I will show later.
The block primitive was covered previously, and its counterpart, pass functions, is the opposite and allows traffic to pass. Assuming a single NIC system, create a rule that allows Web access to anywhere. Using vi, edit /etc/opt/ipf/ipf.conf, remove the previous entries, and add these:
# Allow outgoing web traffic
# This rule also allows the replies, too
# the www requests
pass out quick on le0 proto tcp from \
10.1.1.1 to any port = 80 keep state
# Drop all other outgoing traffic
block out quick all
# block all incoming traffic
block in quick all
Let's cover the syntax in detail using the first rule. The # represents comments. Use plenty of them, especially on systems with complicated rules, such as systems acting as firewalls. The first rule:
pass out quick on le0 proto tcp from \
10.1.1.1 to any port = 80 keep state
can be interpreted as:
pass -- Allow the traffic to pass if it matches this rule.
out -- Apply this rule to outgoing traffic. (At this point we've not specified an interface.)
quick -- Apply the rule immediately. Do not continue reading rules (as the normal algorithm would require) to determine if there is a match later in the rule set.
on le0 -- Apply the rule to the Ethernet interface le0.
proto tcp -- This is first portion of the filtering, based upon layer three and above. This rule applies to tcp traffic only (i.e., UDP and ICMP would not be matched at this point in the algorithm, so the filter would continue with the next rule).
from 10.1.1.1 -- Defines the source address for matching against the rule. On a single NIC system, this will be its own IP address, and on a multi-homed system it can be a complete network using the prefix notation such as 0.0.0.0/8.
to any -- Defines the destination address for this rule. In this case, traffic to any IP address is allowed.
port = 80 -- Because it follows the to any destination address, this indicates the destination port: 80.
keep state -- Maintain a table of requests so that the replies to the request will be handled in the same way that in this case is to allow (pass) the packets.
This rule can be read as: Allow traffic going out Ethernet le0 with a source of address of 10.1.1.1, AND any destination address, AND a tcp destination port of 80, through. Additionally, allow any replies to the requests through.
The rules before the previous rule drops all other traffic with block out quick all dropping the outgoing, and block in quick all dropping the incoming.
As you can see, the keep state primitive allows rules to be quickly built without the normal hassles of having to define the matching replies to create the reply. Also, as previously mentioned, this allows simulated sessions for ICMP and UDP, which makes getting through the packet filter much more difficult since the packet will be dropped if there is not a matching request for the reply.
You can now test the rule with:
/etc/init.d/ipfboot reload
(With the current rule, you will have to test the rule using the IP address of the Web server, since we've not allowed DNS queries.)
The keep state is so flexible, that it is very easy to match the security given by many Internet firewall appliances that simply allow connections initiated by the user, and drops incoming connects. Here is such an example:
# Allow all outgoing traffic and replies \
to those requests
pass out quick on le0 proto tcp from any \
to any keep state
pass out quick on le0 proto udp from any \
to any keep state
pass out quick on le0 proto icmp from any \<
to any keep state
# Drop all other outgoing traffic
block out quick all
# block all incoming traffic
block in log quick all
The results of nmap (http://www.insecure.org/ \
nmap/) run against the host (which received no attempts at hardening) without ipf active:
10.1.1.1
Starting nmap V. 2.12 by Fyodor \
(fyodor@dhp.com, www.insecure.org/nmap/)
Interesting ports on freddy (10.1.1.1):
Port State Protocol Service
7 open tcp echo
9 open tcp discard
13 open tcp daytime
19 open tcp chargen
21 open tcp ftp
23 open tcp telnet
25 open tcp smtp
37 open tcp time
79 open tcp finger
111 open tcp sunrpc
512 open tcp exec
513 open tcp login
514 open tcp shell
515 open tcp printer
540 open tcp uucp
1103 open tcp xaudio
4045 open tcp lockd
6112 open tcp dtspc
7100 open tcp font-service
nmap run completed -- 1 IP address (1 host up) scanned in 8 seconds.
And the results with the ipf rules:
nmap 10.1.1.1
Starting nmapV. 2.12 by Fyodor \
(fyodor@dhp.com, www.insecure.org/nmap/)
Nmap run completed -- 1 IP address \
(0 hosts up) scanned in 30 seconds
As shown, this immediate hardening can be very effective, and is the minimum I recommend to anyone connecting a host to the Internet on their small office network. (This policy, however, has some major weaknesses since all protocols and their appropriate weaknesses are still active on the host.)
Logging and Administration
ipf support logging of filtering using the log parameter in the filter rule. From the example above, the rule:
block in log quick all
told ipf to log the entries that were block by this rule, which, in that case, was all outside initiated sessions. The ipf packages uses the ipmon program to support actual logging. By default, ipmon is started in the startup script and is configured to log to the syslog facility (ipmon -sn). As compiled, ipmons syslog support logs to the local0 facility, with the following levels based upon the rule:
info -- Logged packets that are only logged, not passed or dropped. (This is useful for logging a large rule such as a large network, and then using other rules for more granular support. For example, ipf could log all access to 10.0/8, without passing or dropping the packet, and then allow other rules to allow Web access, but deny ftp.)
notice -- Logged packets within a pass rule.
warning -- Logged packets within a block rule.
error -- Logged packets that are so short in length that they are suseptible to an attack based upon incorrect IP fragmentation.
If these standard levels are not granular enough, the ipf supports additional control of the logging. For example, you need additional logging based upon certain ports or IP addresses, and you can specify the facility and level with the log level facility.level parameter. Here's an example to log all traffic from the 192.168.1.0/24 network to the auth facility at error level:
block in log level auth.error quick \
on le0 from 192.168.1.0/24 \
to 10.1.1.1
Remember that you have to configure syslog accordingly using the syslog.conf file for the logging to take place. Also, don't forget to build in rotation for any specific logging files that are not normally rotated with your standard syslog logging.
In addition to syslog support, ipmon can also be used to view the logging in real time to the terminal. ipmon can be directed to display packets logged (ipmon -o I), logged state tables changes (ipmon -o S), and NAT table activity (ipmon -o N), or all three with ipmon -a.
Examine the output of /opt/ipf/bin/ipmon -a I based upon a telnet session against a host running earlier port scan against the network appliance, like security policy covered above:
13/08/2000 21:11:54.378242 \
le0 @0:1 b 192.168.1.1,1025 -> \
10.1.1.1,23 PR tcp len 20 60 -S IN
As shown, the date, time, and interface are recorded, as well as the interface (le0). The @0:1 identifies the group (0), and 1 identifies the rule number in the particular pass or block rules. This is a block rule, as shown by the b, which follows the @0:1. This means this rule was the first rule that blocks and logs the traffic. The 192.168.1.1,1025 is the source address and the destination, and the 10.1.1.1,23 is the destination address and port (23 = telnet). The length of the payload (20) and the packet (60) follows. Since this is a TCP packet, the final field is the flags that are set.
I've not covered all the logging possibilities here, so consult the man page for ipmon (man -s 8 ipmon) -- it does a very good job of documenting the logging functions.
Summary
Although ipf rule base takes some getting used to, its excellent configuration file support, flexibility in filtering points in the routing process, strong logging support, and wide flavors of UNIX support, make it a good tool to add to your net admin tool box. If you support security administration across multiple flavors of UNIX, you probably won't find a better cross-platform packet filter.
About the Author
Ronald McCarty received his bachelor's degree in Computer and Information Systems at the University of Maryland's international campus at Schwaebisch Gmuend, Germany. After completing his degree, Ronald McCarty started his network career as network administrator at the Schwaebisch Gmuend campus. Ronald McCarty works for Lucent Technologies as a senior systems engineer on a customer team responsible for a major telecommunications carrier. He spends his free time with his two best friends in the world: his daughter, Janice, and his wife, Claudia. Ron can be reached at: ronald.mccarty@gte.net.
|