ONLamp.com    
 Published on ONLamp.com (http://www.onlamp.com/)
 See this if you're having trouble printing code examples


FreeBSD Basics

BSD Firewalls: IPFW

04/25/2001

Your FreeBSD system comes with two built-in mechanisms for inspecting IP packets: ipfw and ipfilter. Both have their own peculiar syntax for creating rulesets to determine which packets to allow and which packets to discard, so I'd like to demonstrate the usage of both. Since you can only run one or the other, I'll start with ipfw; once we've had a good look at it, I'll switch gears and move on to ipfilter.

Before you can use ipfw, you'll have to add some options to your kernel config file and recompile your kernel. If you're a bit rusty on compiling kernels, you'll want to take a look at that section in the handbook.

There are several options that can be used by ipfw, so let's start by taking a look at LINT. I'll do a search using "/" to quickly find the correct section:

cd /usr/src/sys/i386/conf
more LINT
/IPFIREWALL

# IPFIREWALL enables support for IP firewall construction,
# in conjunction with the 'ipfw' program.  IPFIREWALL_VERBOSE
# sends logged packets to the system logger.  
# IPFIREWALL_VERBOSE_LIMIT limits the number of times a 
# matching entry can be logged.
#
# WARNING:  IPFIREWALL defaults to a policy of "deny ip 
# from any to any" and if you do not add other rules during 
# startup to allow access, YOU WILL LOCK YOURSELF OUT.  It 
# is suggested that you set firewall_type=open in /etc/rc.conf 
# when first enabling this feature, then refining the firewall 
# rules in /etc/rc.firewall after you've tested that the new 
# kernel feature works properly.
#
# IPFIREWALL_DEFAULT_TO_ACCEPT causes the default rule (at boot)
# to allow everything.  Use with care, if a cracker can crash 
# your firewall machine, they can get to your protected machines.
# However, if you are using it as an as-needed filter for 
# specific problems as they arise, then this may be for you.  
# Changing the default to 'allow' means that you won't get stuck 
# if the kernel and /sbin/ipfw binary get out of sync.

As a minimum, you need to include the option IPFIREWALL to enable ipfw; this tells your kernel to examine every IP packet and compare it to a ruleset. It is always a good idea to include logging support, which you do by adding the option IPFIREWALL_VERBOSE. You should also limit the amount of packets the kernel will log for the same reasons we saw last week in limiting the amount of ICMP packets that were logged. You limit the logging of IP packets with the IPFIREWALL_VERBOSE_LIMIT option.

Also in FreeBSD Basics:

Fun with Xorg

Sharing Internet Connections

Building a Desktop Firewall

Using DesktopBSD

Using PC-BSD

Note that the default is for ipfw to throw away all IP packets except those you've specifically allowed in your ruleset. I prefer this default as it gives a finer control over which packets are being accepted; I'd hate to think my kernel was accepting packets I wasn't aware of. I will definitely notice if packets I want aren't being accepted and can change my ruleset to allow them; I'll never know the difference if packets I hadn't thought of are slipping through my firewall because I didn't make a rule to explicitly deny them. Accordingly, I won't override the default by including the option IPFIREWALL_DEFAULT_TO_ACCEPT.

# IPDIVERT enables the divert IP sockets, used 
# by ''ipfw divert''

This option is used in conjunction with natd. Since I'm only building a firewall to protect a single machine, I won't need this option.

# IPSTEALTH enables code to support stealth forwarding 
# (i.e., forwarding packets without touching the ttl).  
# This can be useful to hide firewalls from traceroute 
# and similar tools.

This sounds like an interesting option, so I'll include it in my firewall and see how it works when I test my firewall.

# Statically Link in accept filters
options            ACCEPT_FILTER_DATA
options            ACCEPT_FILTER_HTTP

I'm not running a web server on this computer, so I won't compile in these two options.

# The following options add sysctl variables for controlling 
# how certain TCP packets are handled.
#
# TCP_DROP_SYNFIN adds support for ignoring TCP packets with 
# SYN+FIN. This prevents nmap et al. from identifying the 
# TCP/IP stack, but breaks support for RFC1644 extensions 
# and is not recommended for web servers.
#
# TCP_RESTRICT_RST adds support for blocking the emission 
# of TCP RST packets. This is useful on systems which are 
# exposed to SYN floods (e.g. IRC servers) or any system 
# which one does not want to be easily portscannable.

Again, I'll include these options and watch for the results when I test my firewall.

# ICMP_BANDLIM enables icmp error response bandwidth 
# limiting.   You typically want this option as it will 
# help protect the machine from D.O.S. packet attacks.
#
options 	ICMP_BANDLIM

This option comes enabled with the default kernel; we saw its behaviour last week when we used the nmap utility.

# DUMMYNET enables the "dummynet" bandwidth limiter. 
# You need IPFIREWALL as well. See the dummynet(4) 
# manpage for more info. BRIDGE enables bridging between 
# ethernet cards -- see bridge(4).

I won't include these two options as I don't need to do any traffic shaping on this stand-alone computer.

Before I rebuild my kernel, I'll add the following lines to my kernel config file:

#To enable IPFW with default deny all packets
options	  IPFIREWALL
options	  IPFIREWALL_VERBOSE
options	  IPFIREWALL_VERBOSE_LIMIT=10

#To hide firewall from traceroute
options	  IPSTEALTH

#To hide from nmap, remove if create web server
options	  TCP_DROP_SYNFIN

#To hide from portscans
options	  TCP_RESTRICT_RST

While my kernel recompiles, I'll take a look at the options I should add to /etc/rc.conf. Again, I'll use the "/" to search for the correct section in the manpage:

man rc.conf
/firewall

firewall_enable
   (bool) Set to NO if you do not want have firewall rules
   loaded at startup, or YES if you do.  If set to YES, and
   the kernel was not built with IPFIREWALL, the ipfw kernel
   module will be loaded.  See also ipfilter_enable.

firewall_script
   (str) If you want to run a firewall script other than
   /etc/rc.firewall, set this variable to the full path to
   that script.

firewall_type
   (str) Names the firewall type from the selection in
   /etc/rc.firewall, or the file which contains the local
   firewall ruleset.  Valid selections from /etc/rc.firewall,
   are ''open'' - unrestricted IP access; ''closed'' - all IP
   services disabled, except via lo0; ''client'' - basic pro-
   tection for a workstation; ''simple'' - basic protection
   for a LAN.  If a filename is specified, the full path must
   be given.

Since I want my firewall rules to be loaded at bootup, I'll set firewall_enable to YES. I'll be creating my own ruleset, so I'll specify the path to the file I'll create using firewall_type.

firewall_quiet
   (bool) Set to YES to disable the display of ipfw rules on
   the console during boot.

This is a good option to set to YES as it'll show each rule being loaded; if you have a typo in your ruleset, all the rules following the typo will NOT be loaded. If you're watching the screen at bootup, you'll see an ipfw syntax message following the last rule that was successfully loaded; you'll want to look for a typo in the line following that rule, then reboot to ensure that all your rules successfully load.

firewall_logging
   (bool) Set to YES to enable ipfw event logging.  This is
   equivalent to the IPFIREWALL_VERBOSE kernel option.

tcp_extensions
   (bool) Set to NO by default.  Setting this to YES enables
   certain TCP options as described by RFC 1323.  If you have
   problems with connections randomly hanging or other weird
   behavior of such nature, you might try setting this back to
   NO and seeing if that helps.  Some hardware/software out
   there is known to be broken with respect to these options.

log_in_vain   (bool) Set to NO by default.  Setting to YES 
   will enable logging of connection attempts to ports that have 
   no listening socket on them.

tcp_keepalive
   (bool) Set to YES by default.  Setting to NO will disable
   probing idle TCP connections to verify that the peer is
   still up and reachable.

tcp_drop_synfin
   (bool) Set to NO by default.  Setting to YES will cause the
   kernel to ignore TCP frames that have both the SYN and FIN
   flags set.  This prevents OS fingerprinting, but may break
   some legitimate applications.  This option is only avail-
   able if the kernel was built with the TCP_DROP_SYNFIN op-
   tion.

Since I added the option TCP_DROP_SYNFIN in my kernel, I'll set this corresponding value to YES. I'll include a remark to remove this line if I enable a web server on this computer.

tcp_restrict_rst
   (bool) Set to NO by default.  Setting to YES will cause the
   kernel to refrain from emitting TCP RST frames in response
   to invalid TCP packets (e.g., frames destined for closed
   ports).  This option is only available if the kernel was
   built with the TCP_RESTRICT_RST option.

icmp_drop_redirect
   (bool) Set to NO by default.  Setting to YES will cause the
   kernel to ignore ICMP REDIRECT packets.

icmp_log_redirect
   (bool) Set to NO by default.  Setting to YES will cause the
   kernel to log ICMP REDIRECT packets.  Note that the log
   messages are not rate-limited, so this option should only
   be used for troubleshooting your own network.

I end up adding the following lines to my /etc/rc.conf file:

#required for ipfw support
firewall_enable="YES"
firewall_script="/etc/rc.firewall"
firewall_type="/etc/ipfw.rules"
firewall_quiet="NO"	#change to YES once happy with rules
firewall_logging_enable="YES"

#extra firewalling options
log_in_vain="YES"
tcp_drop_synfin="YES"	#change to NO if create webserver
tcp_restrict_rst="YES"
icmp_drop_redirect="YES"

A few notes before I reboot into my new kernel. The LINT file really means it when it says "YOU WILL LOCK YOURSELF OUT." Until I start creating a ruleset that allows the IP packets I want, no IP packets will be allowed to leave or enter my computer. If I want to check my e-mail or download any last bits of information regarding rulesets from the Internet, now is the time to do it, before I reboot.

Creating a good ruleset is a bit of a fine art; if you're creating a firewall for the first time, wait until you have a block of time where it's not essential to have a network connection and you have the time to try things and reboot and try something else and reboot, etc. You'll find that the logic used by ipfw will not necessarily be the same logic you use.

Also, a firewall isn't something you just install and then forget about. Plan on spending some time tweaking it and scratching your head when it doesn't seem to do things the way you expected it would. Once you've rebooted into your new firewall-enabled kernel, you'll want to plan on completing the following three tasks:

  1. Methodically add rules to your ruleset and test each new rule to ensure that you have indeed allowed only the packets you wish to allow.
  2. Decide what you wish to log and watch the resulting log. You'll probably end up modifying your rules as you discover that you have inadvertently allowed or denied some packets that you didn't wish to.
  3. Once you're satisfied that your firewall is dropping and accepting the packets you want, test your firewall to ensure it is behaving as you would expect.

Okay, I'll reboot my computer to load the new kernel. I'll also watch my boot screens carefully; I should see the following messages right after my NIC is loaded:

Flushed all rules.
00100 allow ip from any to any via lo0
00200 deny ip from any to any to 127.0.0.0/8
Firewall rules loaded, starting divert daemons:.
Additional routing options: tcp extensions=NO ignore ICMP redirect=YES TCP keepalive=YES restrict TCP reset=YES drop SYN+FIN packets=YES.

<additional output snipped>
Additional TCP options: log_in_vain=YES.

You may wonder why I received those first three lines as I haven't yet created the file that contains my ruleset. When I edited /etc/rc.conf, I added the line:

firewall_script="/etc/rc.firewall"

The file /etc/rc.firewall was read at boot time and it contains the following lines:

############
# Flush out the list before we begin.
#
${fwcmd} -f flush

############
# Only in rare cases do you want to change these rules
#
${fwcmd} add 100 pass all from any to any via lo0
${fwcmd} add 200 deny all from any to 127.0.0.0/8

Since it clearly states that rules 100 and 200 should be used, I'll start with rule 300 when I create my ruleset. Before doing so, I want to double-check that ipfw is indeed denying all packets by default. I can do this using the ipfw show command.

ipfw show
ipfw: socket: Operation not permitted

Looks like only the superuser is allowed to see the firewall rules, so I'll try again as the superuser:

su
Password:
ipfw show
00100   0     0 allow ip from any to any via lo0
00200   0     0 deny ip from any to 127.0.0.0/8
65535 115 14092 deny ip from any to any

Then, to be really sure, I'll try to use my network connection:

ping www.freebsd.org
ping: cannot resolve www.freebsd.org: Host name lookup failure

traceroute www.freebsd.org
traceroute: unknown host www.freebsd.org

lynx www.freebsd.org
Alert!: Unable to access document.

Well, name resolution definitely isn't working; let's try pinging by IP address:

ping 24.141.116.1
PING 24.141.116.1 (24.141.116.1): 56 data bytes
ping: sendto: Permission denied
ping: sendto: Permission denied
^C
--- 24.141.116.1 ping statistics ---
2 packets transmitted, 0 packets received, 100% packet loss

Since I ran that last ping as the superuser, it looks like ipfw is indeed discarding all IP packets. Now that I've successfully locked myself out of my network connection, it's time to create a ruleset that allows me to send and receive the IP packets I want.

There are two ways to create the rules that are read by ipfw:

Since I'm the only person using my home computer, I'll add my rules directly to a file and reboot. I've already added this line to /etc/rc.conf

firewall_type="/etc/ipfw.rules"

so I'll be creating a file called /etc/ipfw.rules.

It'll take a whole article to create and test the ruleset, so let's wait til next week to do so. In the meantime, you may want to take a look at the following so you have a feel for the syntax of ipfw rules and what sort of rules should be included in a ruleset:

man ipfw

http://www.robertgraham.com/pubs/firewall-seen.html

http://www.defcon1.org//html/Networking_Articles/Firewall-Ipfw/firewall-ipfw

http://www.interhack.net/pubs/fwfaq/

http://www.freebsddiary.org/firewall.html

http://www.bsdtoday.com/2000/December/Features359.html

http://www.freebsd-howto.com/HOWTO/Ipfw-HOWTO

http://www.daemonnews.org/200103/firewall.html

As you can see, there's no shortage of resources available, so you don't have to reinvent the wheel when you create your ruleset. You'll also notice that rulesets tend to be specific to an individual's needs; you'll find that you'll end up borrowing good ideas from a variety of sources. Til next week, happy reading.

Dru Lavigne is a network and systems administrator, IT instructor, author and international speaker. She has over a decade of experience administering and teaching Netware, Microsoft, Cisco, Checkpoint, SCO, Solaris, Linux, and BSD systems. A prolific author, she pens the popular FreeBSD Basics column for O'Reilly and is author of BSD Hacks and The Best of FreeBSD Basics.


Read more FreeBSD Basics columns.

Return to the BSD DevCenter.

Copyright © 2009 O'Reilly Media, Inc.