8.4. Basic IPFW Configuration

 < Day Day Up > 

So, assuming you have chosen to build a FreeBSD-based firewall, where do you start? Well, first you need a machine to run the firewall on. The horsepower you require will depend on how much data you send through the firewall, what you do to the data, and any other activities you have the host perform. You could easily run a FreeBSD firewall with a DSL connection, and internal connection, and a wireless network connection on a 200MHZ Pentium with 64 megabytes of RAM. This is definitely not a state of the art machine, but for a simple home firewall, it doesn't break a sweat.

Obviously, you need enough network interfaces to suit your needs. Your setup will vary depending on the architecture you have chosen. For the basic IPFW setup here in this chapter, we will be using a bump in the wire architecture, unless otherwise specified.

8.4.1. Kernel Configuration

The first step in creating an IPFW firewall is to compile IPFW support into the kernel. The following kernel configurations control the functionality of IPFW.


options IPFIREWALL

This kernel option enables the firewalling capability. It is the only kernel option required by IPFW. The other firewall kernel options are optional.


options IPFIREWALL_VERBOSE

This kernel option enables IPFW to log packets to the syslog utility.


options IPFIREWALL_VERBOSE_LIMIT= N

This kernel option controls how many packets are logged per entry in the ruleset. For example, if you are logging all denied SMB traffic, the firewall will stop sending messages to syslog after N packets. This option prevents a rule from logging so much as to fill up the logging filesystem.


options IPFIREWALL_DEFAULT_TO_ACCEPT

This kernel option changes the default behavior of the firewall. Normally traffic is denied if it does not match any firewall rules. This inverts that logic and allows packets through that do not match any firewall rules.

The only option absolutely required for IPFW to function is IPFIREWALL. The others can be enabled if you need them. If you enable logging, we recommend that you compile in the logging limit because you never know when a rule will run away from you.

8.4.2. Startup Configuration

Since IPFW is not a process, it does not need to be started as a daemon would. However, it does need to be activated when the machine boots. Add firewall_enable="YES" to your /etc/rc.conf, to enable firewall capabilities at boot. However, if you need to turn the firewall off and on by hand (for instance, when you're testing) you can set the sysctl variable net.inet.ip.fw.enable to 1 or 0 depending on if you are turning the firewall on or off. If you are in secure level 3, once the firewall is enabled, it cannot be disabled.

By default, IPFW will use the /etc/rc.firewall shell script for its firewall configuration file. This stock firewall configuration file ships with FreeBSD and contains several different firewall behaviors. You can specify one of the following firewall_types in your rc.conf to use these canned rulesets:


open

Allows all traffic in and out of the firewall.


client

Basic ruleset for a host-based firewall architecture. Outbound connections are allowed, only a few select connections are allowed in.


simple

A bump in the wire firewall configuration for firewalls with one internal and one external network interface. It allows a few services through to the internal network and permits all outbound traffic.


closed

All traffic is denied.


/path/to/file

Use this alternate file or script as the firewall ruleset.

You will need to modify rc.firewall to for the right IP addresses and netmasks for your network.

8.4.3. Firewall Configuration

The default firewall configuration in rc.firewall will likely not be exactly what you want. You can choose to modify the existing rc.firewall, create your own, or configure the runtime firewall by hand. However if you choose to configure the runtime firewall by hand, you must understand the ipfw utility and the things you can make the firewall do.

The basic syntax for any ipfw command is the following:

ipfw [-N] command [index] action [log] protocol addresses [options]

8.4.3.1 Optional arguments

First, let's look at the optional parts of the ipfw command. The -N flag instructs ipfw to resolve IP addresses and services to names when displaying them to the user. This has no effect on the way the firewall works, however it may slow display down as the host needs to look up hostnames through DNS.

The index option specifies where in the firewall rules this action will be inserted. The index values range from 1 to 65534. Rules are processed in order of their index (starting at 1). Using an index lets you insert a rule into the middle of an already existing and running ruleset. The last rule, 65535, is the default rule for the firewall, generally denying all traffic.

The log option indicates that ipfw should send a message to syslog whenever this rule is matched. Be careful to use this only for rules that match infrequently or in conjunction with the firewall kernel logging limit. Without the limit, a rule with a lot of matches can easily fill up the logging filesystem.

ipfw has the capability to track the state of TCP and UDP streams. For TCP, it is a relatively straightforward process. The firewall, upon detecting a new TCP session via a SYN packet, will set up a dynamic rule that allows all other packets within that TCP session to be allowed. UDP does not have the same session construct as TCP, so the firewall must guess what packets are part of a UDP flow. For most installations, this guess works just fine.

Finally, the options control extra abilities to some ipfw commands. The following are some of the more important options.


in

Matches inbound packets.


out

Matches outbound packets.


via interface

Matches packets passing through a specified interface.


keep-state

If a packet matches this rule, the firewall creates a state entry for this session. Any time a packet from this session (inbound or outbound) arrives at the firewall, the state entry will allow the packet through the firewall without having to run the packet through the ruleset again.


setup

This will match a TCP packet that only has the SYN bit set, for instance, the first TCP packet of a TCP connection.

There are many other options that are documented in the ipfw(8) manpage.

8.4.3.2 Required arguments

ipfw accepts the following commands:


add

Adds a rule to the existing ruleset. If an index is specified, the rule is inserted appropriately; otherwise, it is appended to the list.


delete

Deletes a rule from the existing ruleset.

There are many actions ipfw can take on a packet. Chances are, if there's something you want to do based on matching a specific packet, there is an existing ipfw action that will do it. Here are some of the common actions.


allow /accept/pass/permit

The packet is permitted to pass through the firewall.


deny /drop

The packet is blocked by the firewall.


reset

This action is only valid on TCP packets. The packet is blocked by the firewall. Further, the firewall sends a RST packet to the originating host in an attempt to stop further packets.


skipto number

When a packet matches a skipto rule, ipfw jumps to the rule at index number.


divert port

This action causes the packet to be diverted to the specified local port. This is often used for NAT support.

For the sake of simplicity, we will examine a simple small office firewall as shown in Figure 8-8. This is a DMZ architecture with a web and mail server running on the server network. The clients on the internal network are standard office workstations. The rules presented in this section could be used as a replacement for the rc.firewall in the default install.

Figure 8-8. Example DMZ network


There are many philosophies on how a firewall should be configured. Some believe the absolute minimum traffic that can be allowed should be allowed. Others feel that this idea makes administration difficult and there is no reason to be that restrictive. The rules in this chapter are designed with the default deny principle in mind and are meant to be illustrative. Your firewall rules should be constructed in a way that makes sense for your environment.


First, to make the rules readable, define each network, network mask, interface, and firewall IP address as a variable in rc.firewall. Servers and other machines that will have specific rules should have variables assigned to them as well. For instance, the DMZ interface can be defined as follows:

dmz_if="xl0" dmz_net="10.0.0.0" dmz_mask="255.255.255.0" dmz_ip="10.0.0.1"

To further simplify maintenance, a variable should be defined for the ipfw command itself. This allows you to manipulate the command used on all rules by only making one change (for instance, to use the -q flag for quiet output).

fwcmd="/sbin/ipfw"

For the first rule, make sure that traffic across the loopback interface is allowed. Further, make sure that traffic destined for localhost does not arrive on any external interface. Traffic to 127.0.0.0/8 should always go through the loopback interface. By definition, this is what the loopback interface is for. Anything addressed to the localhost subnet received on a physical interface is completely bogus.

${fwcmd} add pass all from any to any via lo0 ${fwcmd} add deny all from any to 127.0.0.0/8

If a rule indicates that state should be tracked, then a dynamic rule is created for each session. Normally, these dynamic rules aren't checked until a packet reaches the first keep-state rule. However, if you'd like the firewall to check the dynamic rules first (that is, if you are going to have many state-aware sessions), then specify the check-state action early in the ruleset. In this case, we will be using state checking for the servers on the DMZ. Since we assume a fair bit of traffic to these servers, we utilize check-state.

${fwcmd} add check-state

Next, we need to prevent spoofing attacks and packets that should never be seen on the Internet. Traffic from the netblocks specified in RFC 1918: Address Allocation of Private Internets should never be received on the public Internet interface:

# anti-spoofing ${fwcmd} add deny all from ${int_net}:${int_mask} to any via ${dmz_if} ${fwcmd} add deny all from ${ext_net}:${ext_mask} to any via ${dmz_if} ${fwcmd} add deny all from ${dmz_net}:${dmz_mask} to any via ${int_if} ${fwcmd} add deny all from ${ext_net}:${ext_mask} to any via ${int_if} # RFC 1918 address ${fwcmd} add deny all from any to 10.0.0.0/8 via ${ext_if} ${fwcmd} add deny all from any to 172.16.0.0/12via ${ext_if} ${fwcmd} add deny all from any to 192.168.0.0/16via ${ext_if}

These anti-spoofing rules are not perfect. Ideally, the rule would state "allow traffic through an interface if and only if its source IP address belongs to a network that exists on that interface." However, with ipfw, this logic can be difficult to enforce. Using the skipto command, rules can be created that enforce proper anti-spoofing logic. This, unfortunately, can make the rules difficult to understand and maintain, so generally the above construct is used.

There are many more networks that can be blocked from entering the public interface. For instance, there are many netblocks that are not currently allocated for use in the global routing table. These networks can be added to your ipfw ruleset and logged. Hits against the rules are interesting because these packets should never occur on the Internet in normal situations. They are almost always signs of an attack. A good starting point to build a list of networks to deny traffic from is RFC 3330, Special Use IPv4 Addresses, and IANA's list of allocated address space at http://www.iana.org/assignments/ipv4-address-space.


Traffic to the servers on the DMZ should be allowed from anywhere. Regardless of whether the client is on the internal network or on the Internet, the web server should be reachable. Also, the SMTP port of the mail server should be reachable from everywhere while the IMAP port should only be accessible from the internal network. In this case, we will use state tracking to make the firewall more efficient.

# Server rules ${fwcmd} add pass tcp from any to ${websrv} http keep-state ${fwcmd} add pass tcp from any to ${mailsrv} smtp keep-state ${fwcmd} add pass tcp from ${int_net}:${int_mask} to ${mailsrv} imap keep-state

We use protocol names for readability here. You could also specify ports 80, 25, and 143, respectively. As long as the service is listed in the file /etc/services, however, the name and the port number are interchangeable.


Now for the internal clients, we allow all outbound TCP and UDP connections from our internal network. Whether or not this is an acceptable ruleset is a matter of your local policy. However, for the sake of maintainability and minimizing user impact in our relatively low security environment, these rules are reasonable.

${fwcmd} add pass ip from ${int_net}:${int_mask} to any

Next, the firewall itself should be protected. There's generally no reason for anyone except for administrators to communicate directly with the firewall. The exception is traffic used for network troubleshooting such as ping and traceroute. Allowing ping and TRaceroute packets in your firewall ruleset is another one of those religious wars that firewall administrators and security experts like to wage. Some say that allowing this traffic provides attackers with critical information that can be leveraged for an attack. Others feel that ping and tracerouting is a key part of maintaining a network. The authors, who have been responsible for running networks in the past, feel that concise troubleshooting ability outweighs the security consideration.

${fwcmd} add pass tcp from ${admin_host} to ${int_if} ssh keep-state ${fwcmd} add pass icmp from any to any

Finally, we explicitly deny all other traffic and log it.

${fwcmd} add deny ip from any to any log

The idea of logging all denied traffic may sound good, but in reality, this is really unworkable. Depending on your network, you may find you have certain types of traffic constantly being dropped by your firewall. For instance, the router to which you are attached may be constantly probing the firewall. By examining your logs, you're able to determine the types of benign traffic that will do nothing but fill your logs. You can then create specific rules before your deny all rule that silently drops this annoying traffic.

This basic set of rules should be enough to get you up and running with a few site-specific modifications. As with any active open source project, the features and operation of IPFW will change periodically. Further, IPFW is very feature rich, and much of it's functionality is out of the scope of this book. For the latest news and complete configuration options, see the FreeBSD web site and the ipfw(8) manpage.

8.4.4. Using the Firewall

Now that the firewall has rules, there is still the pesky aspect of actually running the firewall. Turing the firewall on and off, viewing and modifying the active ruleset, and resetting the log counters are all key administrative activities for any firewall. IPFW makes these processes intuitive and easy.

First, as was stated before, the firewall can be turned on and off by setting the sysctl variable net.inet.ip.fw.enable to 1 or 0 respectively. Depending on your setup, especially if you have NAT running, turning the firewall off can completely isolate the internal network. If NAT isn't running and forwarding is enabled (net.inet.ip.forwarding is set to 1), then your firewall just became a router and the internal network is completely exposed. Be aware of what you're doing if you turn your firewall off.

To see the currently running firewall rules, issue the show command to ipfw. For example:

% sudo ipfw show 00100      36       1986 allow ip from any to any via lo0 00200       0          0 deny ip from any to 127.0.0.0/8 ...

This list has been cut short for the sake of example, but all your firewall rules will be listed. The first column is the rule number of a particular rule. By default, ipfw will increment the rule number by 100 from the previous rule. So, a basic ruleset will iterate as follows: 100, 200, 300, 400, etc.

The next column indicates how many packets have matched the rule and the following column shows the number of bytes. For instance, the first rule has been matched 36 times for a total of 1986 bytes. The final column is the rule itself.

If you're using keep-state to track connections, ipfw is creating dynamic rules in the background to handle state-based traffic. To see what dynamic rules have been created through stateful sessions, use the -d flag:

% sudo ipfw -d show ... ## Dynamic rules (3): 00400  172  24784 (296s) STATE tcp 192.168.0.78 49783 <-> 192.168.0.10 22 00400   16   1456 (1s) STATE tcp 192.168.0.78 49761 <-> 192.168.0.10 22

The first column is the rule number to which this dynamic rule is associated. The number in parenthesis specifies how long that dynamic rule has until it is aged out of the state table. The rest is similar to standard show output.

Rules can also be added to the ruleset on the fly. For instance, if you wanted to put a rule allowing all UDP port 53 traffic immediately following the loopback rules, you could do the following:

% sudo ipfw add 250 pass udp from any to any 53

We've chosen 250 arbitrarily; the important thing is that the number you specify must fall in between the two adjacent rule numbers.

Rules can also be deleted on the fly as well. If we realized that inserting a rule to allow all UDP port 53 traffic before our anti-spoofing rules was a really bad idea, we could do the following.

% sudo ipfw delete 250

Voìla, the rule is gone.

The ability to add and delete rules on the fly is a great administrative feature. However, it's critical to update the static ruleset with the rule as well. That way, if the firewall were to reboot for some reason, the new rule would be read out of your firewall configuration file automatically at boot time. Rebuilding the "correct" ruleset can be a time consuming process.

Also, be careful when modifying rulesets remotely. One typo, and you can lock yourself out of the host completely. Direct console access is now the only way to change the firewall configuration. There are various tricks to prevent a typo from causing this problem. One solution is to have the first rule in your firewall ruleset explicitly allowing traffic from your workstation to the firewall. Another trick is to schedule a reboot at some point in the future using the at(1) command (echo reboot | at now + 10 minutes) and then modify the firewall. If things go badly, the firewall will reboot automatically. If things go well, you can use atrm(1) to cancel the reboot.

Finally, you may find that you've reached your logging (verbose) limit on a rule. Either through inspection of ipfw show output (hey, I see my rule has more hits than my limit) or through syslog output (messages about the log limit being exceeded showing up in syslog), you discover that the rule is no longer logging. The counter on a rule can be reset by using the zero command and the rule number.

% sudo ipfw zero 100 Entry 100 cleared

Now all of rule 100's accounting as been cleared:

% sudo ipfw show 100 00100   0      0 allow ip from any to any via lo0

If you would rather get a clean slate for the entire ruleset, you can zero out all statistics by leaving off the rule number.

     < Day Day Up > 


    Mastering FreeBSD and OpenBSD Security
    Practical Guide to Software Quality Management (Artech House Computing Library)
    ISBN: 596006268
    EAN: 2147483647
    Year: 2003
    Pages: 142
    Authors: John W. Horch

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net