IPFilter on OpenBSD

Have a new wireless gateway set up for your community, but don't want the local script kiddies on your network? Maybe you've just set up your new broadband connection and want to protect your LAN. Or maybe you simply want to deny access to all but a few ports on your PC. There are many potential solutions, ranging from an expensive Cisco PIX firewall to the cheap-and-scary, hope-and-pray solution. A great compromise that provides security and peace of mind while going easy on the pocketbook is OpenBSD running IPFilter.

What is IPFilter? Very simply, a package for permitting (or passing) and denying IP packets based on a range of criteria. It can also provide Network
Address Translation (NAT) services, if desired.
The IPFilter web site has more
details.

The available rules range from quite simple -- for example, deny all traffic on any interface; to relatively complex -- pass and log all ICMP traffic of type ICMP-QUERY from the 199.232.41.24/29 subnet on interface fxp0. Each rule is one-way only, meaning you can block inbound HTTP requests without disrupting outbound requests. When bridging two cards together, OpenBSD's bridge device only checks incoming filter rules. Bidirectional rules are easily created by setting the inbound rules on the other NIC.

How do you start?

First, get a machine running OpenBSD. Any ol' 486 or even 386 will do. The IPFilter package was removed from the source code on May 30th, 2001, so you'll need an earlier build than that. However, it was replaced by PF, the OpenBSD packet filter, which was designed to be syntactically compatible to IPFilter so most of this document will still apply. Although IPFilter has been removed from OpenBSD, it is still in use by FreeBSD and NetBSD.

You'll also need two network cards. A secure firewall or bridge cannot be built with only one network card. However, you are not limited to just two network cards. IPFilter scales to meet your needs.

When building the OpenBSD kernel, be sure to include option IPFILTER in the configuration file. If you wish to log actual IP packets, also include option IPFILTER_LOG. This is useful when combined with the ipmon utility, which logs packets from /dev/ipl. If you want to build your own Carnivore, this is where to start.

With OpenBSD, it's possible to create an invisible bridging firewall. It will act as a filter and firewall, yet won't decrement the TTL on packets passed through it. This makes it invisible to outsiders. It doesn't even have to have IP addresses assigned to its network cards.

Next, edit /etc/rc.conf to include the line ipfilter=YES. For NAT support, you'll also need ipnat=YES. The example below does not address NAT, which is generally evil and bad.

Your network cards will not need to have an IP address, but you can set this up, if desired. For example, on a
system with two Intel EtherExpress cards, fxp0 and fxp1,
the files may look like:

Now set up a bridge between the two NICs. Do this by creating /etc/bridgename.bridge0

add fxp0 add fxp1 up

thus linking the two devices, creating a transparent firewall that you are able to access remotely. This is very convenient if you don't have a spare monitor or a KVM switch.

You're now ready to start configuring your firewall! We'll discuss the rules and options
available to you below.

Setting up the rules

By default, your firewall rules live in /etc/ipf.rules. There are a number of example configurations provided in /usr/share/ipf/examples/. We'll look line-by-line at a slightly modified version of /usr/share/ipf/examples/firewall.2. The modifications allow access to the firewall box via SSh, and will be noted below.

pass out from any to any
pass in from any to any

IP filter rules work on a last-matched basis (unless quick is specified -- more on that below). Therefore, any packet that does not match an upcoming rule is accepted by these two rules.

block in log quick on fxp0 proto icmp from any to any icmp-type redir
block in log quick on fxp0 proto tcp/udp all with short
block in log quick on fxp0 from any to any with ipopts

Any packet coming in on interface fxp0 (which is connected to the Internet) is immediately
blocked and logged if it is an ICMP redirect request, an IP fragment that won't allow the
filter to examine the header, or if it contains IP options.

block in log quick on fxp0 from 199.232.41.24/29 to any
block in log quick on fxp0 from localhost to any
block in log quick on fxp0 from 0.0.0.0/32 to any
block in log quick on fxp0 from 255.255.255.255/32 to any

Block and log IP spoofing attempts to match packets coming from outside the firewall that claim to be "from" a machine that's behind this firewall, localhost, 0.0.0.0, or 255.255.255.255.

block in log quick from 10.0.0.0/8 to any group 100
block in log quick from 192.168.0.0/16 to any group 100
block in log quick from 172.16.0.0/12 to any group 100

Further IP spoofing rulesets match any packet "originating" from a reserved address block.

block in log quick on fxp1 from any to 199.232.41.24/29

Don't allow the firewall to talk to any of the machines it protects, or them to talk to it. This provides less of a beachhead if one machine is cracked, and won't prevent other machines
behind the firewall from talking to each other, as they're connected by a switch or hub.

block in on fxp0 proto udp from any to any
block in log on fxp0 proto udp from any to any port = sunrpc
block in log on fxp0 proto udp from any to any port = 2049
pass in on fxp0 proto udp from any to any port = domain

Block all UDP traffic from the Internet except DNS. If the UDP traffic is an NFS or portmap request, block it and log it. Again, the last matched rule wins, so the pass...domain line ensures that DNS traffic gets through.

block return-rst in log on fxp0 proto tcp from any to any flags S/SA
block return-rst in on fxp0 proto tcp from any to any port = auth flags S/SA

You will also need to lock and log all incoming TCP setup requests. Because ident requests are so frequent,
they are blocked but not logged.

pass in on fxp0 proto tcp from any to any port = domain
pass in on fxp0 proto tcp from any to 199.232.41.27 port = smtp
pass in on fxp0 proto tcp from any to 199.232.41.27 port = www
pass in on fxp0 proto tcp from any to 199.232.41.27 port = ssh
pass in on fxp0 proto tcp from any to 199.232.41.26 port = ssh

Finally, allow DNS traffic to any machine; web and SMTP traffic to 199.232.41.27; and SSh to both 199.232.41.27 and 199.232.41.26.

Summary

IPFilter on OpenBSD provides a simple, effective method of building a firewall. The hardware requirements are minimal, and the protection provided is well worth the already low costs. The rulesets are very easy to read and look very much like English.

Mike DeGraw-Bertsch
is a security and Unix system administration
consultant in the Boston, Mass. area. When he's not at a job, writing,
hacking with Perl, or playing with his wireless network, he can usually be
found playing goal in ice hockey.