Introduction to IPFilter for FreeBSD

A guide to protecting a stand-alone system

by Mark Foster
March 7th, 2003

Welcome! This guide is intended to help you get better protection from the unruly Internet for your FreeBSD system. Computer systems that are directly connected to the Internet, meaning they have a publicly reachable IP address and possibly a published DNS hostname, are sitting ducks without some sort of protection. Another common situation is having a server sitting in a DMZ with little or no protection.

As time marches on, the operating system and software running on top of it become more and more likely to be susceptible to known exploits. In other words, the system becomes more and more likely to be infiltrated the longer it goes unprotected. There are a couple of ways to combat this inevitable degradation in system security. These are generally NOT mutually exclusive, either.

  • You can choose to keep the system updated, voraciously [1].
  • You can harden the system using various tools, such as TITAN, or use Trusted BSD
  • You can install reactive (portsentry, laBrea) and/or passive (snort, tripwire, snare) monitoring mechanisms
  • You can restrict access to the system, using tcp-wrappers, portsentry, IPFilter or ipfw.

This article focuses on IPFilter, which returns plenty of bang for the buck in terms of protecting your FreeBSD system. It is a TCP/IP port filter with NAT capabilities, stateful-ness(sp) and logging.

I have been using IPFilter for over two years to protect about 15-30 servers, including my home network. It can also be using with bridging, but not on FreeBSD, just OpenBSD, which now uses pf in place of IPFilter (learn more)..

Prerequisites

FreeBSD 4.8 RELEASE secure branch, but stable or current is fine.

Customize your kernel

To use IPFilter you will need to rebuild your kernel. In most cases the FreeBSD system is installed with a GENERIC kernel which does not include the IPFILTER capability. Don't be afraid! Rebuilding your kernel is not as daunting as it sounds.

Here is what to do, logged in as root.

  1. Change directory to /usr/src/sys/i386/conf/[2]
  2. Copy the file named GENERIC to MYKERNEL (or whatever name suits your taste).
  3. Open the file named MYKERNEL in your favorite editor. Change the line that says
    ident    GENERIC
    
    to say
    ident 	MYKERNEL
  4. Now at the the bottom of the file, and paste in these lines
    options  IPFILTER          #ipfilter support
    options  IPFILTER_LOG      #ipfilter logging
    
  5. Save and quit.

Compile and Install the new Kernel

Now you are going to recompile and install your new kernel MYKERNEL. Perform these steps, again as root.

cd /usr/src
make buildkernel KERNCONF=MYKERNEL

You will now observe gobs of output the the compile does it's thing.

make installkernel KERNCONF=MYKERNEL
reboot

Upon reboot, monitor the screen output. You should see your new kernel boot, and you should see a line similar to this...

IP Filter: v3.4.29 initialized. Default = pass all, Logging = enabled

Congratulations! You have completely replaced your kernel and are ready to configure IPFilter for your needs. There are no cookie-cutter rules to this, but we can use a real world example to get you going.

Define your filter rules based on your requirements

Let's say you are setting up an e-mail server for a friend. He wants it to be able to send and receive e-mail for his family and friends. So this server needs to allow incoming connections to port 25 (SMTP). It also needs to allow outbound connections to other mail servers. Both of these activities will require DNS traffic to pass, but we only want this traffic to be exchanged with our locally configured name servers (eg. whatever is in /etc/resolv.conf). For this example we will call these 216.149.21.2 and 216.149.21.3.

Furthermore, the mailboxes on the server will need to be accessed from arbitrary Internet addresses - preferably using SSL enabled IMAP(port 993) or POP3 (port 995) [3]. You will also want SSH access to the server from another location (eg. 216.70.142.13) for administrative purposes.

Here is the contents of our rule set, which would live in /etc/ipf.rules. Note that dc0 is our Internet-facing interface. [4]

# Default Deny
block in log on dc0 from any to any

# Malicious packets - drop 'em
block in log quick on dc0 proto icmp from any to any icmp-type redir
block in log quick on dc0 proto tcp/udp all with short

# Block and log any packets claiming to originate from suspicious origins
# RFC 1918
block in log quick on dc0 from 192.168.0.0/16 to any
block in log quick on dc0 from 10.0.0.0/8 to any
block in log quick on dc0 from 172.16.0.0/12 to any
# Class D: multicast addresses.
block in log quick on dc0 from 224.0.0.0/3 to any
# Class E: reserved for future use.
block in log quick on dc0 from 241.0.0.0/4 to any
# localhost should be using the lo0 interface
block in log quick on dc0 from localhost to any
block in log quick on dc0 from 0.0.0.0/32 to any
block in log quick on dc0 from 255.255.255.255/32 to any

# Here's the TCP traffic I want to allow in
pass in quick on dc0 proto tcp from any to any port = smtp keep state
pass in quick on dc0 proto tcp from any to any port = imaps keep state
pass in quick on dc0 proto tcp from any to any port = pop3s keep state
pass in quick on dc0 proto tcp from 216.70.142.13 to any port = ssh keep state

# Default deny on outbound
block out log on dc0 from any to any
# Allow outbound mail delivery
pass out quick on dc0 proto tcp/udp from any to any port = smtp keep state
# Allow outbound DNS queries and inbound replies
pass out quick on dc0 proto tcp/udp from any to 216.149.21.2 port = 53 keep state
pass out quick on dc0 proto tcp/udp from any to 216.149.21.3 port = 53 keep state

Now certainly everyone's situation will be different. A good way to determine what to allow through with your filters is the sockstat command. By seeing what is LISTENING, and indeed what is ESTABLISHED (TCP anyway), you can build your rules based on what is already there, and avoid cutting off services.

Activate the rule set

Now you can activate the rules using

ipf -F a -f /etc/ipf.rules

If all goes well there will be no noticeable change. Have a look at monitoring and reporting to see what's happening behind the scenes.

In all likelihood, you will want IPFilter to start upon (re)boot, which means you'll need to add the entries to /etc/rc.conf. Mine look like this:

ipfilter_enable="YES"                    # Set to YES to enable ipfilter functionality
ipfilter_program="/sbin/ipf"
ipfilter_rules="/etc/ipf.rules"             # rules definition file for ipfilter, see
ipmon_enable="YES"                     # Set to YES for ipmon; needs ipfilter, too!
ipmon_program="/sbin/ipmon"          # where the ipfilter monitor program lives
ipmon_flags="-oI -D /var/log/ipflog"   # typically "-Ds" or "-D /var/log/ipflog"

An easy way to do this is to run egrep 'ipmon|ipfilter' /etc/defaults/rc.conf >> /etc/rc.conf then edit /etc/rc.conf and change NO to YES, among other things.

Monitoring

There are a couple of other commands will be useful in checking on the status of your filter rules.

ipfstat -i : this will show you the active inbound filters

ipfstat : this will show comprehensive statistics about the traffic seen

ipfstat -t : this will show an active status display ala top(1)

ipmon -n -oI : show logged TCP/IP packets (flagged with log in the rule set)

ipmon -n -oI -Ds : same as above but directed to the syslog (Daemon mode)

ipmon -n -oI -D /var/log/ipflog : same as above but directed to a different log file (Daemon mode)

Reporting

There are at least a couple of reporting tools out there that will analyze the IPFilter log created by ipmon. These are described in more detail below.

plog

plog is a Perl script that produces plain text activity reports fromipmon's log. This is the program which I use myself by emailing the output of a nightly cronjob. Here is a sample of it's output: [5]

~ mdf@c7613176-a>/home/mdf/scripts/plog.pl -AF block,log & /var/log/ipflog	
###
### Traffic by destination address:
###
c7613176-a [12.132.123.xxx]
    dc0      block    2  tcp         ms-sql-s <- 12-210-22-175.client.attbi.com.<high> (S)
    dc0      block    3  tcp     microsoft-ds <- h24-87-217-240.ed.shawcable.net.<high> (S)
    dc0      block    1  udp          loc-srv <- 61.181.211.21.<high>
    dc0      block    2  tcp              ftp <- 61-221-116-236.HINET-IP.hinet.net.<high> (S)
    dc0      block    3 icmp                ? <- 63-216-14-194.sdsl.cais.net
    dc0      block    3 icmp                ? <- vdslp240.phnx.uswest.net
    dc0      block    3 icmp                ? <- 64.158.79.2
    dc0      block    1 icmp                ? <- 65.211.112.6
    dc0      block    2  tcp           <high> <- vrp1.lax.xpc-mii.net.http (A,AR)
    dc0      block    3 icmp                ? <- 66.28.47.162
    dc0      block    1  tcp     microsoft-ds <- 66.237.35.195.<high> (S)
    dc0      block    1  udp         ms-sql-m <- 208.158.2.79.<high>
    dc0      block    1  udp         ms-sql-m <- 209.61.228.7.<high>
    dc0      block    3  tcp     microsoft-ds <- 196.c58.ethome.net.tw.<high> (S)
    dc0      block    3 icmp                ? <- 211.169.245.98
    dc0      block    7  tcp           <high> <- vrp1.lax.xpc-mii.net.http (A)
255.255.255.255 [255.255.255.255]
    dc0      block  780  udp           bootpc <- 10.132.128.1.bootps

###
### Traffic by source address:
###
10.132.128.1 [10.132.128.1]
    dc0      block  780  udp           bootps -> 255.255.255.255.bootpc
12-210-22-175.client.attbi.com [12.210.22.175]
    dc0      block    2  tcp           <high> -> c7613176-a.ms-sql-s (S)
h24-87-217-240.ed.shawcable.net [24.87.217.240]
    dc0      block    3  tcp           <high> -> c7613176-a.microsoft-ds (S)
61.181.211.21 [61.181.211.21]
    dc0      block    1  udp           <high> -> c7613176-a.loc-srv
61-221-116-236.HINET-IP.hinet.net [61.221.116.236]
    dc0      block    2  tcp           <high> -> c7613176-a.ftp (S)
63-216-14-194.sdsl.cais.net [63.216.14.194]
    dc0      block    3 icmp                ? -> c7613176-a
vdslp240.phnx.uswest.net [63.227.249.240]
    dc0      block    3 icmp                ? -> c7613176-a
64.158.79.2 [64.158.79.2]
    dc0      block    3 icmp                ? -> c7613176-a
65.211.112.6 [65.211.112.6]
    dc0      block    1 icmp                ? -> c7613176-a
vrp1.lax.xpc-mii.net [65.216.116.114]
    dc0      block    2  tcp             http -> c7613176-a.<high> (A,AR)
66.28.47.162 [66.28.47.162]
    dc0      block    3 icmp                ? -> c7613176-a
66.237.35.195 [66.237.35.195]
    dc0      block    1  tcp           <high> -> c7613176-a.microsoft-ds (S)
208.158.2.79 [208.158.2.79]
    dc0      block    1  udp           <high> -> c7613176-a.ms-sql-m
209.61.228.7 [209.61.228.7]
    dc0      block    1  udp           <high> -> c7613176-a.ms-sql-m
196.c58.ethome.net.tw [210.58.58.196]
    dc0      block    3  tcp           <high> -> c7613176-a.microsoft-ds (S)
211.169.245.98 [211.169.245.98]
    dc0      block    3 icmp                ? -> c7613176-a
dsl231-038-184.sea1.dsl.speakeasy.net [216.231.38.184]
    dc0      block    4  tcp           <high> -> c7613176-a.auth (S)

fwanalog

This reporting tool converts the ipmon log into something analog can read. The reports look very nice (and familiar!) as shown here.

Click on over to http://tud.at/programm/fwanalog/ for more information. You can install this from the ports directory /usr/ports/security/fwanalog.

Resources

You may find additional help in installing or configuring IPFilter at:

As of 4.8-RELEASE, the periodic script /etc/periodic/security/510.ipfdenied will provide some reporting as well.

Footnotes & References

  1. This is possible using a number of different methods. man cvsup to get started.
  2. The path may be different depending on your architecture (eg. alpha instead of i386)
  3. If SSL is not possible, then IMAP would use port 143 and POP3 would use 110. I do not recommend this.
  4. Some rules were gleaned from http://www.onlamp.com/pub/a/bsd/2000/07/05/OpenBSD.html. This rule set is by no means comprehensive, and is not intended for protecting a full-fledged network.
  5. Some addresses have been changed to protect the innocent. Also this was generated from a log using a different ruleset than is presented in this article, thus no correlation should be attempted.

Thanks to Brian Hatch and Dan Ebert for their contributions to this article.


Head shot © 2003 Mark Foster

If I was helpful, please let me know.

$Id: ipfilter.html,v 1.4 2005/11/12 16:36:00 mdf Exp $