Anti-Hacker Tool Kit, Third Edition

 < Day Day Up > 


Many Unix systems, including Linux, FreeBSD, OpenBSD, and the like, come with packet filtering packages. They require special kernel options and modules to be built in, but because security has become so important, these options and modules are almost always built into the default system install. First, we’ll discuss Linux’s ipchains and iptables software. Then we’ll take a look at FreeBSD’s IPFW tool kit.

Note 

It’s important to note that ipchains, iptables, and IPFW are all “userspace” programs. That means that the programs run and reside in memory designated for programs run by the users as opposed to those run by the system kernel. The actual packet filtering is done by the system kernel itself. These programs should be thought of as user interfaces to the kernel’s packet filtering capabilities.

Ipchains

Ipchains is the first packet filter we’ll discuss. Ipchains was originally based on a tool called ipfwadm, a Linux spawn of BSD’s IPFW, which we’ll discuss shortly. The idea behind ipchains was to create chains of rules that a packet had to pass through. At any point in a chain, the packet could be passed to another point in another chain. Ipchains has all but been replaced by iptables; however, it is still included in most Linux distributions along with iptables. It is a good starting point for our first look at firewall packages, and it also gives us an example of a stateless firewall.

To use ipchains, all you need is a Linux box with the proper options compiled into the kernel. You can tell if your kernel has ipchains support if the file /proc/net/ip_fwchains exists. Most current Linux installs will have these options built in, but earlier Linux installs may need to be modified. Unfortunately, we can’t go into detail here about how to build a Linux kernel; numerous Internet resources are available on building kernels for firewall support. You can check out the ipchains man page or visit the following web pages for more details:

Tip 

You’ll also need to make sure that the file /proc/sys/net/ipv4/ip_forward contains the value 1 if you want to be able to forward packets from one network to the other. You can type echo 1 > /proc/sys/net/ipv4/ip_forward as root to make sure that your firewall is ready to forward packets.

Ipchains is a user interface to the kernel’s packet filtering capabilities. All of the actual packet examination gets done in the kernel’s memory space. The ipchains program simply dictates the rules to the kernel. Unfortunately, this means the kernel will forget your rules any time the system is rebooted. Thankfully, you can use the tools ipchains-save and ipchains-restore to make a dump of the current ipchains rules in use and restore them after a reboot.

Implementation

If your Linux box has ipchains support, the first thing you can do is run the command ipchains –L.

[root@originix /root]# ipchains –L Chain input (policy ACCEPT): Chain forward (policy ACCEPT): Chain output (policy ACCEPT):

This command lists the current packet filtering rules in use. As you can see, ipchains defines three chains by default: input, forward, and output. The default policy for each chain is that packets are accepted. This means that if a packet passes through the entire chain and doesn’t match any of the rules, the default policy action is taken on the packet (in this case, it is accepted or passed). You can choose from six common built-in targets: ACCEPT (accept the packet), REJECT (block the packet with acknowledgement), DENY (drop the packet altogether), MASQ (perform NAT on the packet), REDIRECT (send the packet to a local port), and RETURN (skip to the end of the chain). We’ll discuss these targets in more detail shortly.

It’s important that you understand how each of the default chains is used. Any packets coming in to the system (from either an internal or external interface) first go to the input chain. Packets that aren’t destined for the firewall machine itself will need to pass through the forward chain before continuing on to their proper destination. Finally, any packets going out of the system (from either interface) will have to pass through the output chain before they make it out. This means that any traffic passing between a machine on the private network (off interface eth0) and a machine on the public network (off interface eth1) will have to pass through all three chains. It’s also important to note that “input” and “output” are not the same as “inbound” and “outbound.” Packets hit the input chain first whether they’re coming from the internal network to eth0 or from the external network to eth1. This means we’ll need to make sure we specify network interfaces in our chain rules to avoid any ambiguity between inbound and outbound traffic.

Using Ipchains Rules   Ipchains doesn’t do us much good unless we can define rules on each of our chains. Here we’ll discuss the ipchains commands that are used to manipulate rules as well as the rule syntax.

First, let’s take a look at the basic commands that allow you to add, remove, and modify rules and chains in Table 13-1.

Table 13-1: Ipchains Chain and Rule Manipulation Commands

Command

Description

ipchains –A <chain>

<rule>

Add the specified rule at the end of the specified chain

ipchains –I <chain>

<rulenum> <rule>

Insert the specified rule at position <rulenum>of the specified chain

ipchains –R <chain>

<rulenum> <rule>

Replace the rule at position <rulenum>with the new specified rule

ipchains –D <chain>

<rulenum>

Delete the rule at position <rulenum>of the specified chain

ipchains –D <chain>

<rule>

Delete the first rule that matches the specified rule in the specified chain

ipchains –P <chain>

<target>

Change the default policy for a chain (such as ACCEPT,REJECT, or DENY)

ipchains –L <chain>

List all the rules in the specified chain

ipchains –F <chain>

Delete all the rules in the specified chain

ipchains –Z <chain>

Reset the byte counters on all rules in the specified chain

ipchains –N <chain>

Create a new chain

ipchains –X <chain>

Delete an empty chain (you can’t delete the default input, output, and forward chains)

The commands you will use most often are for adding rules to a chain, listing rules on a chain, and changing the policy on a chain. Additionally, you can create user-defined chains that can be used as targets. For example, if you want to test a particular packet against a certain set of rules but only if it first matches some initial criteria, you can have the packet jump to the user-defined chain. User-defined chains have no default policy. If a packet reaches the end of a user-defined chain, it “falls off” that chain and lands back on the chain from whence it came.

First, let’s look at an example rule. Let’s say we wanted to block any incoming Pings (ICMP echo requests) at our external interface. How would we do this?

ipchains –A input –p 1 –i eth1 –s 0.0.0.0/0 8 –d 0.0.0.0/0 –j DENY

Let’s break this down. First, we can see that we’re adding this rule to the input chain. The –p flag is used to specify the type of IP protocol. In this case, we’re looking at IP protocol number 1, which is ICMP. The –i flag allows us to define which network interface we’re examining. In this case, we’re concerned with incoming Pings on our external interface, so we specify eth1. The –s and –d flags are used to specify the source and destination of the packet. In this case, the 0.0.0.0/0 will match any and all IP addresses, so we’re actually saying “from any to any.” The 8 at the end of the source address refers to the particular ICMP code for Pings (echo requests). Finally, the –j flag says that any packets matching these criteria should be sent to the specified target. The target could be another chain or one of the built-in targets. In this case, we’ve said that any packets matching this rule should be denied or dropped.

Tip 

Ipchains will let you use symbolic names for protocols such as “icmp” and “tcp,” ports such as “www” and “telnet,” as well as ICMP codes. We use the numbers here simply for educational purposes.

That wasn’t so bad, eh? Let’s take a look at a rule that involves TCP. Let’s say we wanted to allow web traffic only to our web server at 192.168.1.50 and deny everything else. How would we do it?

ipchains –A input –p 6 –i eth1 –d 192.168.1.50 80 –j ACCEPT ipchains –P input DENY

We’ve taken a bit of a shortcut here. Notice that we didn’t specify a source address. In this case, we don’t really need to do this. We want to accept any traffic that is heading to 192.168.1.50 on port 80, regardless of where it’s coming from. We then lock down the input chain so that any packet not matching this rule will be dropped.

Do you see any problems with this example? If that rule is the only rule we have on our input chain, the web server will never be able to respond to anyone who talks to it! We’re saying that we allow only traffic inbound on eth1 to 192.168.1.50 on port 80. When 192.168.1.50 tries to talk back, it will be sending traffic inbound on eth0. Since there’s no matching rule and the policy is to DENY, the response won’t be passed. Let’s try this:

ipchains –A input –p 6 –i eth0 –s 192.168.1.50 80 –j ACCEPT

That’s better. We’ve appended a second rule to the chain that will allow inbound responses from 192.168.1.50’s web server. Assuming the packet made it through the forward and output chains, the external web client would receive its response.

You can use an exclamation mark to invert nearly any possible value. For example, let’s say you wanted everyone to have access to this web server except the guys over on the 192.168.69.0 class C network. You could use inversion to rewrite the first rule.

ipchains –R 1 input –p 6 –i eth1 –s ! 192.168.69.0/24 –d 192.168.1.50 80 j ACCEPT

This command replaces the first rule so that only traffic not coming from the 192.168.69.0 network to our web server will be accepted. We don’t need to rewrite the second rule because the first rule should prevent 192.168.1.50 from ever receiving a packet from 192.168.69.0 Inversion is a handy feature, as you can use it with protocols, ports, addresses, interfaces, and even TCP flags.

Before we move on, let’s take special notice of how IP addresses and ports should be formatted for ipchains. If you’re dealing with a single host or IP address, you can simply use the hostname or IP. When you’re dealing with a network or subnet, you need to be familiar with CIDR (Classless Inter Domain Routing) notation. For example, the class C network in the preceding example contained IP addresses from 192.168.69.0 to 192.168.69.255. The subnet mask for that address range is 255.255.255.0. That means that for all addresses on that subnet, the first 24 bits (or three octets) of the 32-bit IP address will always stay the same. Therefore, to specify a class C 192.168.69.0 network as a source or destination for ipchains, you can specify the network number followed by a slash and either the subnet mask (192.168.69.0/255.255.255.0) or the number of network bits (192.168.69.0/24).

TCP and UDP ports can also be specified in a number of ways. You can use symbolic names for the ports (defined in /etc/services) or the number. You can specify a range of ports (such as 6000:6010) or an open range of ports (:1023 specifies all ports under 1024). And, of course, ports can be preceded with the inversion operator (!) to negate the value.

Table 13-2 shows some of the different command-line flags you can use in your rules and what they do.

Table 13-2: Ipchains Command-Line Flags for Defining Packet Matching Rules

Characteristic

Example Command

Description

IP protocol

-p <proto>

Match packets with the specified IP protocol. <proto> can be a protocol number or a symbolic name for the protocol number defined in /etc/protocols. The most popular symbolic names include TCP and UDP.

Source

-s <address> <port>

Match packets with the specified source address and port. For ICMP, <port> is interpreted as ICMP type number. If <port> is omitted, any packet with a matching source address will match regardless of the port.

Destination

-d <address> <port>

Match packets with the specified destination address and port. For ICMP, <port> is interpreted as ICMP code number. If <port> is omitted, any packet with a matching destination address will match regardless of the port.

Network interface

-i <interface>

Match packets that are passing on the specified network interface. On Linux systems, you can type ifconfig to determine the interface names for your internal and external interfaces.

TCP SYN flag

-y

Match TCP packets with only the SYN flag set. Because ipchains has no stateful capabilities, you'll often need to allow most return TCP traffic in, but use this rule to deny any incoming connections (those with only the SYN flag set).

Fragments

-f

Matches fragmented packets that also match the rest of the rule.

Logging

-l

By adding –l at the end of a rule, any packets matching the rule will be logged to syslog using the kernel.info facility and level.

Now that you’re familiar with the rule syntax, let’s take a look at a set of rules running on a system and see if we can interpret what they’re doing. The following is taken from an ipchains rules file generated using the ipchains-save utility. It could be loaded back into the kernel using the ipchains-restore utility.

1):input DENY 2):forward DENY 3):output ACCEPT 4):unkwn-in – 5)-A input -s 0.0.0.0/0.0.0.0 -d 0.0.0.0/0.0.0.0 -i eth1 -j unkwn-in 6)-A input -s 0.0.0.0/0.0.0.0 -d 0.0.0.0/0.0.0.0 -i eth0 -j ACCEPT 7)-A input -s 0.0.0.0/0.0.0.0 -d 0.0.0.0/0.0.0.0 -i lo -j ACCEPT 8)-A forward -s 192.168.1.0/255.255.255.0 -d 0.0.0.0/0.0.0.0 -i eth1 -j MASQ 9)-A unkwn-in -s 192.168.1.0/255.255.255.0 -d 0.0.0.0/0.0.0.0 -j DENY 10)-A unkwn-in -s 0.0.0.0/0.0.0.0 -d 0.0.0.0/0.0.0.0 22:22 -p 6 -j ACCEPT 11)-A unkwn-in -s 0.0.0.0/0.0.0.0 -d 0.0.0.0/0.0.0.0 80:80 -p 6 -j ACCEPT 12)-A unkwn-in -s 0.0.0.0/0.0.0.0 20:20 -d 0.0.0.0/0.0.0.0 1024:5999 -p 6 -j ACCEPT 13)-A unkwn-in -s 0.0.0.0/0.0.0.0 20:20 -d 0.0.0.0/0.0.0.0 6010:65535 -p 6 -j ACCEPT 14)-A unkwn-in -s 0.0.0.0/0.0.0.0 0:0 -d 0.0.0.0/0.0.0.0 -p 1 -j ACCEPT 15)-A unkwn-in -s 0.0.0.0/0.0.0.0 3:3 -d 0.0.0.0/0.0.0.0 -p 1 -j ACCEPT 16)-A unkwn-in -s 10.3.0.6/255.255.255.255 53:53 -d 0.0.0.0/0.0.0.0 -p 17 -j ACCEPT 17)-A unkwn-in -s 10.3.0.7/255.255.255.255 53:53 -d 0.0.0.0/0.0.0.0 -p 17 -j ACCEPT 18)-A unkwn-in -s 0.0.0.0/0.0.0.0 -d 0.0.0.0/0.0.0.0 -p 6 -j ACCEPT ! –y 19)-A unkwn-in -s 0.0.0.0/0.0.0.0 -d 0.0.0.0/0.0.0.0 -j DENY –l

Note 

The line numbers in the preceding code listing are not part of the code; they are merely for reference during the remainder of this section.

Let’s see if we can make sense of this. The first three lines indicate the default policies for each of the built-in chains. The input and forward chains will drop any packets that don’t match a rule while the output chain will accept any packets that don’t match a rule. No rules have been defined for the output chain, so we can immediately determine that this firewall will pass all outgoing traffic.

The fourth line is creating a chain called unkwn-in. Because user-defined chains cannot have a default policy, it is followed by a dash. The fifth line tells us that any packet coming in on eth1 (our external interface) should immediately jump to our unkwn-in chain, where it will be handled. The following two lines tell us that any traffic coming in on eth0 (our internal, trusted interface) and lo (the loopback interface) should be accepted. Although every packet that comes in to the firewall should match one of these three rules, the policy for the input chain is to deny anything else.

The eighth line starts defining rules for our forward chain. It says that any traffic with a source address on the 192.168.1.0 network being forwarded to the external interface eth1 should jump to the built-in MASQ target. This is how ipchains handles Network Address Translation (discussed in depth in the “Firewalls and Packet Filters—the Basics” section). Any traffic from our internal network destined for the Internet will eventually get forwarded to the external interface eth1, putting it on the forward chain. The MASQ target will perform NAT on the packet and send it on to the output chain. Any other traffic that comes in on the forward chain will be denied.

Tip 

When a packet gets “de-masqueraded” by ipchains, it skips the forward chain and goes straight to the output chain. Since our firewall is doing NAT and no traffic from the external network should ever be forwarded to the internal network without having to be de-masqueraded first, we can safely deny all other traffic on the forward chain.

The remaining lines all define rules for our unkwn-in chain. Remember that for a packet to reach this chain in the first place, it had to be inbound on our eth1 interface. For that reason, it’s not necessary to provide the –i interface modifier on any of these rules—but you should remember that it’s implied.

The first rule on the unkwn-in chain protects our network from IP spoofing on the external interface. There’s no reason that a packet from our private 192.168.1.0 network should be inbound on the external interface, so we explicitly deny it before it has a chance to match any of our ACCEPT rules. This illustrates an important point when designing a firewall ruleset for ipchains. Even though we’ve made the default policy for the input chain (and therefore the unkwn-in chain as well) DENY, the packet won’t hit that default action until it’s passed through all the ACCEPT rules we’ve put in. This means we explicitly need to deny some things up front before we start accepting anything. Other firewalls deal with this differently.

The next two rules on lines 10 and 11 say to accept traffic coming in on TCP ports 80 and 22 (web and secure shell, respectively). This allows machines on the Internet to communicate with the web server and SSH server running on the firewall. Remember, even though we’ve specified a source and destination of “any,” because we’re using NAT, an Internet machine wouldn’t be able to view any web servers or secure shells running on our private network.

Note 

When the ipchains-save utility generates a rules file using the rules currently in the system, it writes the rules in great detail, specifying source and destination even when it isn’t actually necessary.

The rules on lines 12 and 13 are for accepting FTP data connections. These rules are an example of a horrific idea. We’re accepting traffic from anywhere with a source port of 20 and a destination port that is not in the reserved port range (1–1023) or in the range commonly used by X servers (6000–6009). The reason we need to do this is because active mode FTP has the server make a separate connection back to the client using a source port of 20. In a NAT environment, this won’t work at all unless the kernel has a special way of handling active mode FTP traffic. (Linux has an ip_masq_ftp kernel module that you can load at startup.) Even so, allowing any TCP traffic with a source port of 20 through your firewall is a bad idea. While discussing the difference between stateless and stateful firewalls in the “Firewalls and Packet Filters—the Basics” section, we showed that FPipe (Chapter 15) could be used to tunnel a user’s TCP traffic through a particular source port. In this case, at least we’re protecting the reserved ports and the X server. But if we were running a secret web server on our firewall at port 8888, anyone who forces his web traffic to use a source port of 20 will be able to access that secret web server because our firewall will assume it’s FTP data traffic! Use passive FTP (which doesn’t require a return connection from the server) and you don’t have to worry about these rules or any special modules to get it to work through NAT.

The rules on lines 14 and 15 deal with ICMP traffic. Basically we’re saying that ICMP echo replies (ICMP type 0) and destination unreachable messages (ICMP type 3) are allowed through. All other ICMP traffic will eventually get denied when it hits the end of the chain. This will allow machines on the internal network to Ping external machines and get a response, but it keeps external machines from Pinging our firewall.

Lines 16 and 17 deal with DNS traffic to DNS servers on the external network. If we don’t pass this traffic inbound from the DNS servers with a UDP source port of 53, we won’t be able to make any DNS queries.

Line 18 is extremely important. Remember how we said that ipchains is a stateless firewall? That means ipchains can judge only one packet at a time. Let’s take the following simplified example. Let’s say the machine 192.168.1.50 fires up its telnet client to an external machine. The source port is chosen by the telnet client at random from an available, nonreserved port. Let’s say it chose a source port of 1029. Ignoring NAT for this example, the connection would look something like this:

The problem is that when the telnet server on the external machine responds, the source port is now 23 and the destination port is 1029. Our firewall isn’t going to allow inbound traffic with a destination port of 1029. And we can’t possibly open up holes for every possible choice of client source port. Well, we could, but it certainly isn’t a good idea. One option, as mentioned in the “Firewall and Packet Filters—the Basics” section, would be to allow all incoming traffic with a source port of 23. However, now we’ve opened ourselves up to the same problem we encountered with the FTP data example earlier. So what else can we do? We can use the –y flag to examine the SYN flag on TCP packets. The SYN flag is set on any TCP packets that are used to initiate a connection. In the rule on line 18, we’re saying that we’ll accept all TCP packets that do not have only the SYN flag set. With this rule, our return packet from the external telnet server will be passed because the SYN flag will not be set without other flags (such as ACK) also being set. This is still dangerous, but it’s probably safer than any other option we have. By accepting only non-SYN TCP packets, we can ensure that no one is able to make a legitimate TCP connection to any services running on our firewall (except for port 22 and 80, which we explicitly opened on lines 10 and 11). You can make a legitimate TCP connection only using an initial SYN packet. The danger here is that we’re assuming that inbound TCP packets that don’t have the SYN flag set are just part of a previously established outgoing connection. In reality, we’ve seen tools (such as the port scanners in Chapter 4 and hping in Chapter 14) that can use non-SYN packets to gather information about services running on machines. Since our firewall will pass any non-SYN packets, it is not protecting us from these types of scans. Clearly, it would be better if ipchains could remember that we made this outgoing telnet connection using a source port of 1029, and then be smart enough to let response traffic from that connection through until the connection had terminated. Iptables, discussed later in this chapter, will allow us to do this.

Finally, the last rule tells us to drop any packet that makes it this far down the chain. Why do we need to do this? If we omit this rule, a packet reaching this point would fall off the end of the unkwn-in chain back to the input chain, where it would hit the input chain’s default DENY policy. Isn’t this last rule redundant? If you look closely, you’ll notice a –l at the end of the rule. This tells ipchains not only to drop the packet, but to log the details of the packet to syslog using the kern.info syslog facility. This allows us to see who’s been getting blocked at our firewall. Here’s a snippet from our log file showing someone trying to telnet into our firewall.

Dec 25 14:49:54 cc94653-a kernel: Packet log: unkwn-in DENY eth1 PROTO=6 10.252.214.178:1776 10.180.192.229:23 L=48 S=0x00 I=5170 F=0x4000 T=108 SYN (#12) Dec 25 14:50:00 cc94653-a kernel: Packet log: unkwn-in DENY eth1 PROTO=6 10.252.214.178:1776 10.180.192.229:23 L=48 S=0x00 I=5791 F=0x4000 T=108 SYN (#12)

We can see the date and time of the attempt, which chain the packet matched, the interface, the protocol, the source and destination information, packet details (length, type of service, IP ID, fragment offset, and time to live), and the rule number (#12) on the chain that matched the packet.

Debugging Ipchains   One of the hardest things to do is to get your firewall rules doing exactly what you want them to do. Many times you’ll find that a rule doesn’t do exactly what you intended. If you’re trying to set up your firewall remotely instead of from the system console, you may even find that you occasionally firewall yourself out of the system (don’t worry, we’ve all done it). Ipchains provides the logging feature that lets you see which rule packets are matching against. When debugging your firewall during its initial setup, you can temporarily put the –l flag at the end of every rule so you can see where packets are getting hung up. Also, ipchains provides a –C command-line option that you can use to check whether a particular packet would be accepted by the firewall chains currently in the system. For example, the following command would tell us what would happen if someone initiated (SYN flag set) a telnet connection to our firewall (10.180.192.229):

# ipchains –C input –p 6 –y –i eth1 –d 10.180.192.229 23 denied #

Network Address Translation and Port Forwarding   As mentioned in the previous section, NAT with ipchains is pretty easy. Simply set up the MASQ target in the forward chain on the external interface and you’re good to go. What about port forwarding? You might think that the REDIRECT target we mentioned earlier would give us this capability, but it doesn’t quite work like that. The REDIRECT target can redirect traffic only to a port on the local firewall machine itself. This is useful if you want to set up a transparent proxy, such as forcing any web traffic destined for the Internet to instead go through a proxy server you have running on port 3128 of the firewall. Port forwarding to a different machine actually requires a utility separate from ipchains.

The utility you’re looking for is called ipmasqadm, and it requires another set of kernel options to be compiled in. If you don’t have the ipmasqadm utility on our system, you can probably download it (as well as instructions on how to rebuild the kernel) from your Linux distribution’s web site.

Just like ipchains, ipmasqadm is an interface to the actual kernel components that do the port forwarding. To allow a connection to port 443 on your firewall to pass through to an internal web server of 192.168.1.50 on port 443, you would issue this command:

# ipmasqadm portfw –a –P tcp –L 10.180.192.229 443 –R 192.168.1.50 443

Of course, we would also need to make sure that ipchains would allow inbound traffic with a destination port of 443 on our external interface.

Summarizing Ipchains

Now that we’ve spent all this time on ipchains, let me make a suggestion: don’t use it. Ipchains is an excellent primer for getting your hands dirty with firewalling concepts, but it simply has too many technical limitations at this point (mainly its statelessness). As we mentioned earlier, most Linux systems will come with both ipchains and iptables. Now it’s time to graduate you from the former to the latter.

Iptables (Netfilter)

The Netfilter/Iptables project (referred to simply as iptables from here on out), is the successor to Linux’s ipchains. It has become the de facto packet filtering package for Linux kernels version 2.4.x and later. Although most Linux distributions will come with their own prebuilt iptables packages, you can find out more information and download the iptables source at http://www.netfilter.org/.

As with ipchains, iptables requires that your kernel have the necessary capabilities before you can use the tool. Your 2.4.x kernel will need to be compiled with a whole slew of options before iptables will work. Check out http://iptables-tutorial.frozentux.net/iptables-tutorial.html#KERNELSETUP for details on how to get your kernel ready for iptables.

If you haven’t already read through the ipchains section, you probably should do so. Due to the similarities between iptables and ipchains, we make several references to the ipchains section when discussing some of the details that are similar between the two.

Implementation

Iptables does many things similarly to ipchains. In iptables, the concepts of firewall chains still exist, but chains are now part of logical groupings called tables. There are three different tables of chains.

The filter table is the main table and consists of an INPUT, OUTPUT, and FORWARD chain, just like ipchains. However, how the packets get passed to these chains is different. With ipchains, traffic coming directly to the firewall would hit the input chain, traffic leaving the firewall would hit the output chain, and traffic passing through the firewall would hit all three chains (starting with input, to forward, and then to output). In iptables, traffic passing through the firewall hits only the FORWARD chain. Only traffic that is originating from or destined to the firewall device itself will hit the OUTPUT and INPUT chains. The filter table is the bread and butter of iptables; if you don’t specify a table on the command line when manipulating rules and chains, it defaults to the filter table.

Another important table is the nat table, which also consists of three different chains: PREROUTING, POSTROUTING, and OUTPUT. The PREROUTING and POSTROUTING chains are hit as packets are first entering and just leaving the firewall, respectively. They are hit only by packets passing through the firewall (from one network to another). The OUTPUT chain is for packets that come directly from the firewall. The nat table is meant for performing Network Address Translation on packets. No filter rules should be placed on any chains in this table.

The last table is the mangle table. The main use of the mangle table is to mark packets of interest for grouping as well as changing Time to Live and Type of Service options.

As with ipchains, when a packet matches a rule in iptables, it is sent to a target. Possible targets in the filter table are as follows:

In the nat table, possible targets are

The MASQUERADE and SNAT targets are nearly identical except that the MASQUERADE target doesn’t require you to specify a replacement source address when rewriting the packet header. The MASQUERADE target automatically uses the IP address on your external interface. The mangle table, which we won’t spend much time on in this chapter, uses targets such as MARK (for marking packets for future matching), TOS (for manipulating the Type of Service field in the IP header), and TTL (for modifying the packet’s Time to Live or expiration).

Now that we’ve talked about the tables, chains, and targets, let’s see how we write rules with iptables.

Using Iptables Rules   The basic iptables commands for manipulating rules and chains are identical to those used by ipchains from Table 13-1. Remember that unless you specify an alternative table on the command line using the –t option, iptables will assume you want to modify rules and chains in the filter table by default.

Let’s take a look at an example iptables rule that protects our firewall from incoming Ping requests:

iptables –A INPUT –i eth1 –p 1 –s 0.0.0.0/0 –d 0.0.0.0/0 -–icmp-type 8 –j DROP

If you refer back to the “Using Ipchains Rules” section, you’ll notice that this rule looks similar to the rule for ipchains. We’re saying that any packets coming in on our external interface (eth1) that are IP protocol number 1 (ICMP) from anywhere to anywhere with an ICMP type of 8 (echo request a.k.a. Ping) should be dropped. The big difference between this rule and the ipchains rule is the way the ICMP type number is specified. You’ll notice that iptables has separate flags for specifying ICMP numbers and TCP/UDP ports. Again, we’ve specified the source and destination here just for clarity’s sake. Because we’re looking at traffic from any to any, we could have omitted the –s and –d options.

Tip 

Remember that the preceding rule protects only the firewall from external Pings. If we weren’t using NAT and needed to protect an accessible network sitting on our internal interface, we’d want to append this rule to our FORWARD chain as well.

If we wanted to allow web traffic through to our internal web server (192.168.1.50) but block everything else, we would write this:

iptables –A FORWARD –p 6 –i eth1 –o eth0 –d 192.168.1.50 -–dport 80 –j ACCEPT iptables –P FORWARD DROP

Pay close attention to this rule. Notice that since we’re filtering traffic coming through our firewall, we need to use the FORWARD chain. Any packets with IP protocol number 6 (TCP) coming in (-i) on the external interface (eth1) and going out (-o) on the internal interface (eth0) destined for port 80 on 192.168.1.50 will be accepted. All other traffic will be dropped.

Now, we’ve intentionally made the same mistake in this rule that we did in the ipchains section. If this rule is the only rule on the FORWARD chain with a policy of DROP, no other traffic will ever pass through the firewall, including the web server’s response to a web connection! One way to remedy this problem is simply to allow the web traffic on the forward chain going the other way:

iptables –A FORWARD –p 6 –i eth0 –o eth1 –s 192.168.1.50 -–sport 80 –j ACCEPT

Notice that we’ve added a second rule that simply reverses the direction of the traffic on the interfaces and swaps the destination information to source information. Iptables offers another solution to this problem, connection tracking, that we’ll discuss further in the upcoming sections.

Just like ipchains, you can use the inversion operator (!) to invert practically any value. We can modify our first rule so that anyone except users on the 192.168.69.0 network can talk to our web server:

iptables –R FORWARD 1 –p 6 –i eth1 –o eth0 –s ! 192.168.69.0/24 –d 192.168.1.50 -–dport 80 –j ACCEPT

Before we move on, let’s take a look at how IP addresses and ports are formatted in iptables. Like ipchains, you can use a single hostname or IP. When you’re dealing with a network or subnet, you should use CIDR notation. To specify a class C 192.168.69.0 network as a source or destination for iptables, you can specify the network number followed by a slash and either the subnet mask (192.168.69.0/255.255.255.0) or the number of network bits (192.168.69.0/24). See the “Using Ipchains Rules” section or use your favorite search engine for more information on CIDR notation.

In ipchains, TCP and UDP ports can be specified after the source or destination IP address. For clarity’s sake, iptables requires you to specify ports using their own flags, such as –-sport and –-dport. When specifying a port argument, you can use symbolic names for the ports (defined in /etc/services) or the number. You can specify a closed range of ports (such as 6000:6010), an open range of ports (:1023 specifies all ports under 1024), or an inverse (! :1023 specifies ports 1024 and above). Iptables also has special capabilities for multiport matching. By specifying –m multiport first on the command line, you can use a comma-separated list of ports (up to 15) in your –-sport and –-dport options. It also allows you to use a special –-port option that requires that both source and destination ports match ports in the comma-separated list. The multiport capabilities are simply a convenience, allowing you potentially to combine several similar rules into one. We’ll talk more about other matching –m options in the "Explicit Matching" section a little later.

Table 13-3 details the basic available rule options you can use in iptables.

Table 13-3: Iptables Command-Line Flags for Defining Packet Matching Rules

Characteristic

Example Command

Description

IP protocol

-p <proto>

Match packets with the specified IP protocol number. <proto> can be a protocol number or a symbolic name for the protocol number defined in /etc/protocols.

Source

-s <address>

Match packets with the specified source address or range.

Destination

-d <address>

Match packets with the specified destination address or range.

Source port (TCP, UDP)

--sport <port>

Match packets with the specified source port or range.

Destination port (TCP, UDP)

--dport <port>

Match packets with the specified destination port or range.

TCP flags (TCP only)

--tcp-flags <flagmask> <setflags>

Match packets with certain TCP flags set. <flagmask> is a comma-separated list of flags that should be examined; <setflags> is a comma-separated list of the flags from <flagmask> that should be turned on (the other flags in <flagmask> should be off).

TCP SYN flag

--syn

Match packets with only the TCP SYN flag set. Same as specifying -–tcpflags SYN,RST,ACK SYN

ICMP type

--icmp-type <type>

Match ICMP packets with the specified type number.

Inbound network interface

-i <interface>

Match packets that are inbound on the specified network interface. On Linux systems, you can type ifconfig to determine the interface numbers for your internal and external interfaces.

Outbound network interface

-o <interface>

Match packets that are outbound on the specified network interface.

Fragments

-f

Matches fragmented packets that also match the rest of the rule.

In the “Using Ipchains Rules” section, we took a look at a set of sample rules. Let’s see how these same rules might look in iptables.

One of the first things we did in working with ipchains earlier in the chapter was create a chain called unkwn-in to deal with any traffic coming in to the firewall on eth1. Now, before we write this iptables rule, we need to think about whether we want to put it on the INPUT chain, the FORWARD chain, or both. Since we’re using NAT in this setup, all inbound traffic on eth1 will have the firewall’s external IP address as a destination anyway, so the INPUT chain should be fine.

iptables –N unkwn-in iptables –A INPUT –i eth1 –j unkwn-in

Next, we want to make sure that our internal network can talk to (and through) the firewall, and that the firewall can talk to itself (on the loopback interface):

iptables –A INPUT –i eth0 –j ACCEPT iptables –A FORWARD –i eth0 –o eth1 –j ACCEPT iptables –A INPUT –i lo –j ACCEPT

Now we’re ready to set up masquerading. Remember that all this time we’ve been operating on chains in the filter table. To set up masquerading, we need to use the nat table.

iptables –t nat –A POSTROUTING –o eth1 –s 192.168.1.0/24 –j MASQUERADE

Another nice feature with iptables is that many of the targets (such as MASQUERADE) can take arguments. If we append –-to-ports 1024-4999 to the end of the previous rule, we can force the firewall to use only source ports from that range when performing IP masquerading or NAT.

Now we’re ready to deal with our unkwn-in chain that will catch all traffic coming in from the external network to our firewall. First off, there shouldn’t be any traffic from our private range (192.168.1.0/24) hitting this chain.

iptables –A unkwn-in –s 192.168.1.0/24 –j DROP

Next, we want to allow incoming web and secure shell connections. For this, we’re going to use some of iptable’s stateful connection tracking features:

iptables –A unkwn-in –p tcp -–dport 80 –m state -–state NEW –j ACCEPT iptables –A unkwn-in –p tcp -–dport 22 –m state -–state NEW –j ACCEPT

You might look at this and wonder what’s going on. We’ve introduced a bit of new syntax here. First, you’ll notice we’re using that tricky –m option again. This flag is used for matching a packet against an extended characteristic, meaning something that isn’t part of the packet header. In this case, we’re using the state extension to gain access to the kernel’s connection tracking (or stateful) capabilities. The connection tracking machine has the ability to watch and remember packets, grouping them into connections or conversations. In this case, we’re specifying a state of NEW, meaning that we’ll accept packets inbound on these ports that aren’t already part of an existing connection. In other words, we’ll accept initial connections to our web server and secure shell ports.

Remember the last time we used the –m option? We used it to do multiport comparisons. We could use multiport here to combine the two rules into one.

iptables –R unkwn-in 2 –p tcp –m multiport -–dport 22,80 –m state -–state NEW –j ACCEPT iptables –D unkwn-in 3

We’ve combined rules 2 and 3 into rule 2 and deleted rule 3. Now, if we think about this rule for a moment, we realize it will allow only NEW traffic to these ports. We also want packets that are part of an existing, valid connection to be passed. We add this rule:

iptables –A unkwn-in –p tcp –m state -–state ESTABLISHED –j ACCEPT

At first glance, you might not realize the power of this rule. Remember in ipchains how you would have to make considerations for passing response traffic from outgoing connections through the firewall? You either had to allow all packets with the SYN flag turned off or allow all traffic using source ports of the services you wanted to access (such as 80, 22, 23, and so on). With the connection tracking capabilities, we can rely on its ability to recognize packets that are part of a previously established connection and simply pass them through. This is much more convenient and much more secure than anything we could do with ipchains. With this rule, we’ve not only allowed incoming established traffic for our web server and secure shell server, but we’ve also allowed incoming established traffic for any outgoing connections our private machines may have made.

Unfortunately, there’s still a caveat here. Due to the way connection tracking was implemented, it is possible for a packet without the SYN flag set to be labeled as part of a NEW connection. Therefore, our rule that allows only NEW inbound packets on ports 22 and 80 could still allow people to perform ACK scans against our firewall. We can combat this by adding a –-syn flag to our rule or inserting an explicit drop instruction just before our rule, such as this:

iptables –I unkwn-in 2 –p tcp ! -–syn –m state -–state NEW –j DROP

This places an explicit drop instruction in position 2, moving our other two rules back. This will make certain that non-SYN packets labeled as NEW by the connection tracking machine will never get through.

What about UDP and ICMP traffic? Neither UDP nor ICMP have concepts of a “connection” the way TCP does, but the connection tracking machine can still watch particular characteristics of the protocols to determine implicit connections or “conversations.” We don’t want any incoming UDP or ICMP traffic heading for our firewall, but we probably want to allow ESTABLISHED UDP and ICMP traffic in so that our private hosts can still Ping out and use UDP services such as DNS.

iptables –A unkwn-in –p udp –m state -–state ESTABLISHED –j ACCEPT iptables –A unkwn-in –p icmp –m state -–state ESTABLISHED –j ACCEPT

If we still want to receive ICMP unreachable messages (type 3), we’ll have to add an explicit rule for that:

iptables –A unkwn-in –p icmp -–icmp-type 3 –j ACCEPT

That covers nearly everything that we had in our firewall rules when we used the ipchains tool. However, we still haven’t dealt with active FTP. Do we need to open a load of port ranges to incoming packets with a source port of 20? The short answer is “yes.” But it’s nowhere near as ugly as it is with ipchains.

iptables –A unkwn-in –p tcp -–sport 20 –m state -–state ESTABLISHED,RELATED –j ACCEPT

We’ve used a new state keyword—RELATED. The connection tracking machine is able to recognize the separate reverse FTP data connection from port 20 as being related to the outgoing FTP control connection on port 21. It does this using a separate kernel module called ip_conntrack_ftp. This module will have to be loaded into your kernel for this rule to allow active FTP traffic to pass.

We’re about done. The last thing we want to do is make sure that we log and drop any packets that haven’t matched any of the ACCEPT rules on this chain.

iptables –A unkwn-in –j LOG iptables –P INPUT DROP

This does what we want. The packet gets logged to syslog if it reaches the end of the unkwn-in chain. At that point, the packet falls of the unkwn-in chain back to the INPUT chain. It will eventually reach the end of the INPUT chain and be dropped.

Explicit Matching   Explicit matches are made using that handy –m option we’ve already discussed. Its most common use is for accessing the connection tracking capabilities using the "state" extension, however it can also be used with other extensions to make other matches. The following list explains some of these extensions in detail:

Connection Tracking   We don’t want to get into a whole lot of gritty detail here, so we’ll keep this as brief as possible. The connection tracking module ip_conntrack (also referred to as the state machine) can keep track of packets and group them together into connections and conversations. It can track both connection-based protocols such as TCP as well as connectionless protocols such as UDP and ICMP. It does this by watching for responses with similar source and destination information and grouping them together. You can then access the state machine’s classification of each packet using the –m state explicit matching option discussed in the previous section. This makes iptables a stateful firewall, giving it an extreme advantage over the stateless ipchains.

Network Address Translation and Port Forwarding   We’ve already discussed how you can use the nat table and the MASQUERADE target in the POSTROUTING chain to replace the source IP address of the private machine with the IP address of the firewall’s public interface just before it goes out to the external network.

iptables –t nat –A POSTROUTING –o eth1 –j MASQUERADE

What do we do about port forwarding? With ipchains, we had to use a separate tool to configure port forwarding. With iptables, we can use the DNAT target in the PREROUTING chain of the nat table to handle port forwarding. If you think about it, port forwarding is just like NAT except in the reverse direction from how we’ve been using it. Instead of rewriting the source address of an outbound packet before it goes out to the Internet, we’re rewriting the destination address of an inbound packet just as it comes in from the Internet.

iptables –t nat –A PREROUTING –p tcp -–dport 443 –i eth1 –j DNAT -–to-destination 192.168.1.50:443

Here we’ve redirected port 443 on the firewall to port 443 on 192.168.1.50, our internal web server.

Port Forwarding Pitfalls

One thing to note about port forwarding is that it doesn’t work if you’re trying to access the box from the same internal network. For example, let’s say that we’re port forwarding 443 on our firewall’s external address (10.180.192.229) to our internal web server (192.168.1.50). From outside the firewall, the port forward works fine. The packet comes in on the external interface (eth1), gets DNATed in the PREROUTING chain, and goes to the internal box as expected. What happens if we try to access https://10.180.192.229/ from a box on our internal network (say 192.168.1.10)? We end up with two problems. Because we’re trying to access 10.180.192.229 from the internal network, we’re coming in on eth0. We won’t match the DNAT rule in the PREROUTING chain because it’s only watching the external interface eth1. We can solve that problem by not specifying an interface in our DNAT rule.

iptables –t nat –A PREROUTING –p tcp -–dport 443 –j DNAT -–to-destination 192.168.1.50:443

We still have another problem, however. When the packet gets forwarded to 192.168.1.50, 192.168.1.50 will respond directly to 192.168.1.10 instead of first passing back through the firewall to be de-masqueraded. 192.168.1.10 thinks it’s talking to 10.180.192.229, even though it’s being forwarded to 192.168.1.50, so when 192.168.1.50 responds directly, 192.168.1.10 has no idea what 192.168.1.50 is talking about. To fix this issue, you can use an internal DNS server that maps the web server’s hostname to its address on the internal network. Alternatively, you can set up an explicit source NAT rule that replaces the address of machines on the internal network with the internal IP address of the firewall (192.168.1.1). This forces 192.168.1.50 to send its responses back through the firewall so it can be properly directed back to 192.168.1.10.

iptables –t nat –A POSTROUTING –s 192.168.1.0/24 –o eth0 –j SNAT –-to-source 192.168.1.1

This says that any traffic from the internal network (192.168.1.0) that has come into the firewall but for some reason is exiting back through the internal interface (eth0) should have its source IP address replaced with the internal IP address of the firewall. With this rule in place, the steps will appear as follows:

  1. 192.168.1.10 contacts 10.180.192.229 on port 443.

  2. Packet goes from 192.168.1.10 to 10.180.192.229 via eth0 (the internal interface).

  3. Packet hits the PREROUTING chain of the nat table, and the destination IP address gets translated to 192.168.1.50 port 443. Source IP address remains 192.168.1.10.

  4. Routing decision is made, and the packet is destined to go back out eth0 (the internal interface) for 192.168.1.50.

  5. Packet hits the POSTROUTING chain of the nat table, and the source IP address gets translated to 192.168.1.1 (internal IP of the firewall). Destination IP address remains 192.168.1.50 port 443.

  6. Packet goes from 192.168.1.1 to 192.168.1.50 via eth0.

Return packets will go back through the firewall, get de-masqueraded, and proper communication will be established.

And just like ipchains, we can use the REDIRECT target to set up a transparent proxy server and force all outbound web traffic to get redirected to the proxy server running on port 3128 of our firewall. Keep in mind, however, that since redirecting has to be done in the PREROUTING chain, it will get done as the outbound packet first comes in on eth0 (the internal interface).

iptables –t nat –A PREROUTING –i eth0 –p tcp -–dport 80 –j REDIRECT -–to-port 3128

Summarizing Iptables

Iptables is a big step up from ipchains, giving you many new packet filtering capabilities (primarily stateful packet inspection). Believe it or not, we haven’t even covered all of the capabilities iptables has to offer. And developers are working on new features every day. We encourage you to visit http://www.netfilter.org/, where you can find things like tutorials, sample startup firewall scripts that do most of the hard work for you, and other packet filtering resources.

IPFW

IPFW is the default packet filtering system on FreeBSD. You can also find it on other BSD variants, as well as Mac OS X. Certain options will need to be compiled into your kernel to make IPFW work, primarily the IPFIREWALL and IPFIREWALL_VERBOSE options. If you want to use other popular IPFW features, you’ll need the IPDIVERT and DUMMYNET options.

Implementation

An important thing to note is that FreeBSD’s configuration files will automatically make use of IPFW whether you’ve explicitly told it to or not. By default, the system will parse the /etc/rc.firewall script as part of the boot-up process. The rc.firewall script is extremely user friendly as it allows novice users to specify single descriptive words to configure the firewall (such as “open” or “closed”). It also enables more advanced users to define startup firewall rules so they will be loaded every time the system boots. By default, rc.firewall will have a policy of denying all traffic. That means if you are first configuring and setting up IPFW from a remote location, there’s a good chance you will firewall yourself out. The solution to this is to compile your kernel with the IPFIREWALL_DEFAULT_TO_ACCEPT option or to specify an “open” firewall type in the /etc/rc.conf startup script by saying firewall_type=open.

Unlike ipchains and iptables, IPFW doesn’t have a concept of multiple chains that each packet travels. With IPFW, rules are added to a single chain. You can specify an index for each rule that places the rule in a certain point in the chain. The lack of multiple chains simplifies IPFW’s usage. You can, however, group rules into sets. This allows you to separate certain groups of related rules so that you can disable or enable certain groups at certain times.

IPFW has four main actions that can be taken on a packet. You can accept the packet (using keywords allow, pass, permit, or accept), you can block the packet with ICMP acknowledgement (reject), you can drop the packet altogether (deny or drop), or you can simply update the byte counter for that rule (count) allowing the packet to continue on down the chain. A log option can be added to any rule to have matching packets logged using the syslog facility.

IPFW also has stateful capabilities. You can use the basic setup and established keywords to distinguish between new TCP connections and established TCP connections. All these two keywords really do, however, is check the TCP flags set on TCP packets. IPFW also has a keep-state keyword that can be used in combination with a check-state rule to make for a truly stateful packet filter.

Before we delve into IPFW’s rule syntax, Table 13-4 will briefly discuss the available command-line options for IPFW.

Table 13-4: IPFW Command-Line Options

Flag

Description

-a

When used as ipfw –a list to list rules, display the byte counters for each rule. The ipfw show command is the same as ipfw –a list.

-c

Show rules in compact format without unnecessary information.

-d

When used as ipfw –d list to list rules, show dynamic rules as well (such as keep-state and check-state).

-e

When used ipfw –e list to list rules, show any expired dynamic rules (rules can expire using the limit keyword).

-f

Never ask for confirmation.

-N

Resolve addresses and port names.

-q

Don't output anything when manipulating rules; implies –f.

-S

When used as ipfw –S list to list rules, show to which set each rule belongs, if any.

-t

When used as ipfw –t list to list rules, show the date and time of the last match on a rule.

Table 13-5: IPFW Commands

Command

Description

ipfw add <index> <rule>

Add the rule, placing it in the position specified by <index>. Use the –N flag if you want to resolve addresses and port names.

ipfw delete <index>

Delete the rule at position <index>.

ipfw flush

Flush all rules except for the default policy (which is at the end of the chain on index 65535). The default policy will be to deny all packets unless you configured your kernel with IPFIREWALL_DEFAULT_TO_ACCEPT, so be careful using this command remotely as you can firewall yourself out of the system.

ipfw list

The list command lists the currently defined rules. You can use several command-line options for controlling the output format (discussed in Table 13-4).

ipfw show

Just like the list command but it also shows the byte counters for each rule.

ipfw zero [index]

Resets the packet counters for all rules or the rule specified by [index].

ipfw resetlog

If a logamount is specified when logging packets to syslog and that logamount is reached, this command resets it, allowing packets matching that rule to be logged again.

Using IPFW Rules   IPFW provides seven different commands for manipulating rules. They are briefly described in Table 13-5.

We’re mostly concerned with adding and deleting rules here. Let’s add a very basic rule:

ipfw add 100 allow ip from any to any out via ed1

This rule says that we should allow any kind of IP packet to go out our external interface (ed1). We’ve specified that this rule be placed in position 100. If we don’t specify an index, IPFW will automatically space rules apart by an increment of 100, by default. Therefore, a subsequent IPFW command that did not specify an index would place its rule at position 200.

So far, we’ve only allowed outgoing traffic to get out. We need to add rules that will let some traffic in. Let’s write a rule that allows only web traffic in to our internal web server.

ipfw add 200 allow tcp from any to 192.168.1.50 80 in via ed1

What if we wanted to explicitly deny certain inbound traffic so that we could also log it, such as attempted telnets and FTPs?

ipfw add 300 log deny tcp from any to {me or 192.168.1.0/24} 23 in via ed1 ipfw add 400 log deny tcp from any to {me or 192.168.1.0/24} 21 in via ed1

Notice we’ve added the log keyword to the beginning of our rule definition. Also notice how we’ve specified the destination. In addition to the any keyword (which matches all IP addresses), the me keyword can be used to match any of the IP addresses configured on the system. As with other firewalls, hosts can be specified one at a time, in CIDR notation, or in an “or-block” like the one shown above. You can also precede any host with a not to negate the match. Ports can be specified individually, as a range, or as a comma-separated list. To illustrate all of these concepts, let’s write a rule that allows inbound access to ports 22 and 443 from all hosts except those on the 192.168.69.0 network.

ipfw add 500 allow tcp from not 192.168.69.0/24 to {me or 192.168.1.0/24} 22,443

We mentioned earlier that rules can be divided into different sets. This is done by specifying a set number after the rule number:

ipfw add 600 set 2 allow udp from any to any

The advantage with sets is that they can easily be turned on and off using the ipfw set command. For example, to quickly allow all UDP traffic, we would type this:

ipfw set enable 2

When we wanted to close that hole, we would type this:

ipfw set disable 2

This ability becomes extremely advantageous when you’re dealing with multiple rules that you’d like to be able to turn on and off at will. You can also move rule numbers from one set to another or swap sets. The following commands move rule 600 from set 2 to 3 and then swap set 3 with set 2:

ipfw set move 600 to 3 ipfw set swap 3 2

What about statefulness? Placing the check-state rule action at the beginning of the chain forces any dynamic (stateful) rules currently in existence to be checked first.

ipfw add 1 check-state

We establish dynamic rules using the keep-state rule option. For example, if we want to let DNS responses to come back through, we’d add a command such as this:

ipfw add 50 allow udp from 192.168.1.0/24 to any 53 via ed1 keep-state

The keep-state keyword creates a dynamic rule upon a match. As soon as a machine on 192.168.1.0 makes a DNS request, IPFW will create a dynamic rule that will allow the DNS server to respond. This effectively allows only incoming DNS packets that are part of an already established conversation.

These are the types of rules you will probably use most often; however, we haven’t discussed a lot of available rule formats and options. IPFW also allows you to do bandwidth limiting and traffic weighting, but that is beyond the scope of this chapter. Table 13-6 details some of the rule actions used at the beginning of a rule command (such as allow, deny, and reject), and Table 13-7 details some of the rule options used at the end of a rule command (such as in, via, and keep-state).

Table 13-6: IPFW Rule Actions

Rule Action

Description

allow | accept | pass | permit

Allow packets that match this rule.

check-state

Turn on matching for dynamic rules created by keep-state or limit rule actions.

count

Update the counter for that rule, continue to pass the packet down the chain.

deny | drop

Drop packets that match this rule without acknowledgment.

divert <port>

Divert packets that match this rule to a divert socket on <port>. This is how IPFW handles Network Address Translation by diverting packets to a natd divert socket.

fwd | forward <ipaddr>[,port]

Matching packets will be forwarded to the IP address and port specified. Can be used for port forwarding or transparent proxies.

pipe

Used for bandwidth limiting.

queue

Used for bandwidth limiting.

reject

Matching packets will be dropped and sent an ICMP host unreachable message.

reset

Matching packets will be dropped and a TCP RST packet will be sent in return.

skipto <rulenum>

Matching packets will jump ahead in the rule list to <rulenum>.

unreach <code>

Like reject, except you can specify any ICMP unreachable code you wish (see Appendix for a table of ICMP unreachable codes).

Table 13-7: IPFW Rule Options

Rule Option

Description

bridged

Only matches bridged packets.

dst-ip <ipaddrs>

Only matches if the destination IP address is one of the addresses defined in <ipaddrs>.

dst-port <ports>

Only matches if the destination port is one of the ports defined in <ports>.

established

Only matches TCP packets with RST or ACK flags set.

frag

Only matches IP fragments—not the first fragment.

gid <group>

Only matches packets transmitted or received by a user in a particular Unix group.

icmptypes <types>

Only matches ICMP packets with a type defined in <types>.

in | out

Only matches packets that are coming in or out on an interface. Can be used in conjunction with the via option.

ipid <id>

Only matches packets with an IP id number of <id>.

iplen <len>

Only matches packets with a length of <len>.

ipoptions <options>

Only matches packets with the options defined in <options> set. Can be used to drop nasty loose source routing (lsrr) packets that tools such as Netcat (see Chapter 1) might be able to use to spoof IP addresses.

ipprecedence <prec>

Only matches packets whose IP header has a precedence of <prec>.

iptos <tos>

Only matches packets with a type of service specified in <tos>.

ipttl <ttl>

Only matches packets with a time to live of <ttl>.

ipversion <ver>

Only matches packets with an IP version of <ver> (such as 4 or 6).

keep-state

Matches will tell the firewall to create a dynamic rule to allow return traffic from the destination IP and port for a certain amount of time.

limit <num>

Only allow <num> number of matching connections—then all other matching packets will be dropped. Used with the stateful rules to limit the amount of connections the state machine has to track.

mac <dst> <src>

Only matches packets with the specified <dst> and <src> MAC addresses.

recv | xmit | via <if>

Options for matching traffic heading a particular direction on a network interface <if>. The via keyword means either recv or xmit.

setup

Only matches TCP packets with the SYN flag set but no ACK flag.

src-ip <ipaddrs>

Only matches if the source IP address is one of the addresses defined in <ipaddrs>.

src-port <ports>

Only matches if the source port is one of the ports defined in <ports>.

tcpack <ack>

Only matches TCP packets with an acknowledgement number of <ack>.

tcpflags <flags>

Only matches TCP packets with the flags in <flags> set. <flags> is a comma-separated list of flags and can consist of fin, syn, rst, psh, ack, and urg.

tcpseq <seq>

Only matches TCP packets with a sequence number of <seq>.

tcpwin <win>

Only matches TCP packets with a window size of <win>.

tcpoptions <options>

Only matches TCP packets with the options in <options> set, such as maximum segment size (mss) or window advertisement (window).

uid <user>

Only matches packets transmitted or received by a particular Unix user.

Using rc.Firewall   As mentioned earlier, the /etc/rc.firewall script that comes with FreeBSD does most of the dirty work for you. All you have to do is define some variables, such as your internal network range, network interfaces, and so on. Then add any other rules you might want, and you’re done. Using rc.firewall as a basis is the best way to avoid firewalling problems and caveats.

Before you can use rc.firewall, you have to set up your system startup file (/etc/rc.conf) to use it. The following lines can be added to your rc.conf file to get basic firewall and NAT functionality working:

firewall_enable="YES" # Set to YES to enable firewall functionality firewall_type="open" # Firewall type (see /etc/rc.firewall) firewall_script="/etc/rc.firewall" # Which script to run to set up the firewall firewall_logging="YES" gateway_enable="YES" natd_enable="YES" # Enable natd (if firewall_enable == YES). natd_interface="ed1" # Public interface or IPaddress to use.

The first four lines turn on the firewall, indicate an open firewall (a definition used by the rc.firewall file), set the startup firewall script (rc.firewall), and turn on firewall logging. The last three lines are necessary if your firewall is protecting a multi-homed machine that will be passing traffic between two networks and performing NAT on the external interface. The ed1 interface is our external interface in this case.

Now, we’ve specified an “open” firewall type. Let’s look into rc.firewall and see what this means:

case ${firewall_type} in [Oo][Pp][Ee][Nn]) setup_loopback ${fwcmd} add 65000 pass all from any to any ;;

You’ll need a little Bourne shell scripting experience to understand exactly what’s going on here. Basically, the open firewall type only calls the setup_loopback function (which sets up a few rules to protect the loopback interface from illegitimate traffic) and then adds a rule to allow all traffic from anywhere to anywhere. The ${fwcmd} variable is defined earlier in the script as the path to the IPFW program (usually /sbin/ipfw), so don’t let that part of the command fool you.

Chances are, you’ll probably want your firewall to be a bit more secure. The rc.firewall file comes with a few other modes you can choose from: client, simple, and closed. The client mode is meant to protect the machine; it is not meant for multi-homed hosts that are protecting networks. For that scenario, you want to use the simple mode. The closed mode will deny everything except loopback traffic—and isn’t useful to you unless you don’t want to use a network at all.

For our purposes, let’s use the simple mode. The first thing we have to do is define variables for our internal and external network values, as shown in the file snippet:

[Ss][Ii][Mm][Pp][Ll][Ee]) ############ # This is a prototype setup for a simple firewall. Configure this # machine as a named server and ntp server, and point all the machines # on the inside at this machine for those services. ############ # set these to your outside interface network and netmask and ip oif="ed1" onet="10.180.192.0" omask="255.255.255.0" oip="10.180.192.229" # set these to your inside interface network and netmask and ip iif="ed0" inet="192.168.1.0" imask="255.255.255.0" iip="192.168.1.1"

The variables we have defined tell the script that the firewall’s external IP is 10.180.192.229 on ed1, its internal IP is 192.168.1.1 on ed0, and the firewall is protecting a 192.168.1.0/24 network from the 10.180.192.0/24 network. After running the setup_loopback function, the script then executes some general protection rules:

# Stop spoofing ${fwcmd} add deny all from ${inet}:${imask} to any in via ${oif} ${fwcmd} add deny all from ${onet}:${omask} to any in via ${iif} # Stop RFC1918 nets on the outside interface ${fwcmd} add deny all from any to 10.0.0.0/8 via ${oif} ${fwcmd} add deny all from any to 172.16.0.0/12 via ${oif} ${fwcmd} add deny all from any to 192.168.0.0/16 via ${oif} # Stop draft-manning-dsua-03.txt (1 May 2000) nets (includes RESERVED-1, # DHCP auto-configuration, NET-TEST, MULTICAST (class D), and class E) # on the outside interface ${fwcmd} add deny all from any to 0.0.0.0/8 via ${oif} ${fwcmd} add deny all from any to 169.254.0.0/16 via ${oif} ${fwcmd} add deny all from any to 192.0.2.0/24 via ${oif} ${fwcmd} add deny all from any to 224.0.0.0/4 via ${oif} ${fwcmd} add deny all from any to 240.0.0.0/4 via ${oif}

Can you make sense of the rules? The first two basically say that we shouldn’t be seeing traffic from internal addresses coming in to the external interface and the same with external addresses coming in on the internal interface. The next three rules protect us from any reserved private network range traffic (see the “What Are Network Address Translation and Port Forwarding?” section near the beginning of the chapter) that has somehow been routed to us. And finally, the remaining rules protect us from any kind of autoconfiguration or multicast on the external interface.

We’ve yet to talk about IPFW and NAT. Remember the natd_enable line from rc.conf? That turns on FreeBSD’s natd. The natd program listens on a special kind of socket called a divert socket. By default, natd binds the divert socket to port 8668, which is defined as the natd port in /etc/services. By using an IPFW divert rule action, we can force packets to pass through the divert socket to natd. Natd will handle all the NAT details. The divert rule looks like this and is already written into rc.firewall:

case ${natd_enable} in [Yy][Ee][Ss]) if [ -n "${natd_interface}" ]; then ${fwcmd} add divert natd all from any to any via ${natd_ interface} fi ;; esac

The rule says to divert packets to the natd socket for all protocols from anywhere to anywhere on our natd_interface (which should have been defined in rc.conf as our external interface, or ed1). NAT is as simple as that.

The rc.firewall file then adds a few rules that will allow basic communication as well as incoming mail, DNS, and web traffic to the external IP address of the firewall. For our purposes, let’s say we’re only running a web server that we want the world to see. We can comment the mail and DNS rules out using a # sign.

# Allow TCP through if setup succeeded ${fwcmd} add pass tcp from any to any established # Allow IP fragments to pass through ${fwcmd} add pass all from any to any frag # Allow setup of incoming email # ${fwcmd} add pass tcp from any to ${oip} 25 setup # Allow access to our DNS # ${fwcmd} add pass tcp from any to ${oip} 53 setup # ${fwcmd} add pass udp from any to ${oip} 53 # ${fwcmd} add pass udp from ${oip} 53 to any # Allow access to our WWW ${fwcmd} add pass tcp from any to ${oip} 80 setup

After that, any initial TCP connections coming in on the external interface are dropped and logged, while anything else will be let through.

# Reject&Log all setup of incoming connections from the outside ${fwcmd} add deny log tcp from any to any in via ${oif} setup # Allow setup of any other TCP connection ${fwcmd} add pass tcp from any to any setup

Finally, there are rules for allowing DNS queries through. The keep-state keyword will ensure that dynamic rules get created so only proper DNS responses come back through.

# Allow DNS queries out in the world ${fwcmd} add pass udp from ${oip} to any 53 keep-state

If you want to add any of your own rules, you need to be careful where you do it. For example, adding a line to allow incoming web traffic to an internal web server (192.168.1.50) at the end of this section wouldn’t work, because it would get dropped as soon as it hit the Reject&Log rule above. We would have to put this rule in before that Reject&Log rule.

On the Internet, you can find many customized rc.firewall scripts available for download. You may find one that serves as a better basis or template for your needs. Simply change the firewall_script variable in your rc.conf file to point to your new firewall script, and you’re good to go.

Network Address Translation and Port Forwarding   We’ve already been over how easy it is to use Network Address Translation with IPFW’s divert rule action and the natd daemon.

ipfw add divert natd all from any to any via ed1

What about port forwarding? In Table 13-7, we mentioned the forward rule action can be used to do either port forwarding or transparent proxy. The following rule would forward port 443 on our firewall’s external IP to port 443 on our internal web server 192.168.1.50.

ipfw add forward 192.168.1.50,443 tcp from any to 10.180.192.229 443 in via ed1

If you were looking for a rule that would transparently forward outbound web traffic to our local proxy server on port 3128, try the following rule:

ipfw add forward 127.0.0.1,3128 tcp from any to any 80 out via ed1

Summarizing IPFW   As with the other firewall products we’ve covered, many more IPFW capabilities (especially in the area of bandwidth limiting) were not covered here. IPFW’s strength is that it can be very easy for the novice to use, but it has such an extensive array of options that it can still be of use to the networking expert. FreeBSD’s IPFW framework makes it relatively easy to turn a FreeBSD box into a packet filtering firewall gateway.

Still Others

Other freely available packet filtering packages haven’t been discussed. One particular package that comes to mind is ipfilter, which also comes with the BSD systems and is an alternative to IPFW. Its functionality and capabilities are similar to those that we’ve already discussed, but we encourage you to check it out at http://coombs.anu.edu.au/~avalon/.

Other supplemental tools can be used to help you implement and augment your firewall capabilities. For example, Firewall Builder (http://www.fwbuilder.org/index.html) is a graphical user interface for Unix variants that can assist you in building firewall rules for iptables, ipfilter, Cisco PIX, and others. Use the GUI to set up your rules, and then generate the appropriate firewall configuration file. Another program, called Guardian (http://www.chaotic.org/guardian/), can allow you to integrate your firewall with an IDS. Guardian works with the freeware IDS Snort (see Chapter 16) so that alerts from snort can automatically trigger ‘accept’ or ‘deny’ rules on your firewall. This allows you to block traffic dynamically from someone who might be port scanning you. Guardian supports ipchains, iptables, ipfw, ipfilter, Checkpoint Firewall-1, and Cisco PIX.

Our goal in this chapter was not to cover in detail every firewall product available, but instead to give you a solid background and knowledge of firewall concepts. If you know the concepts, it’s usually not too difficult to apply those concepts to a new firewall package; it’s usually just a matter of learning the syntax. That being said, we still feel we should briefly cover a few of the more popular commercial firewall products before moving on.


 < Day Day Up > 

Категории