Section 12.5 Firewalls with IP Tables and DMZ

   


12.5 Firewalls with IP Tables and DMZ

graphics/fivedangerlevel.gif

IP Tables is the third generation of firewall technology for Linux and is the successor to IP Chains. As of the publication of this book, different UNIX vendors use one of several different firewall packages, none of which is IP Tables or IP Chains. Their concepts and techniques are similar, though, so UNIX SysAdmins should find this section useful. The syntax and amount of granularity will vary. Some primarily UNIX SysAdmins may find, as have some of the more advanced mostly Windows SysAdmins,, that a single Linux system operating as a firewall may be the most secure and most easily implemented solution.

IP Tables has some innovative features, not available with IP Chains, which are of particular interest to large shops. The most important new feature is a general connection-tracking (or stateful) feature for tracking regular TCP sessions and UDP and ICMP communication. There is a lot of misinformation about this connection-tracking security compared with IP Chains. This is addressed in depth in "IP Tables Connection Tracking: Fact and Myth" on page 465.

12.5.1 Cut to the Chase: Protecting a Simple SOHO Network

Here we create a set of IP Tables rules to protect a simple small office or home office (SOHO) network. In this common configuration, the Linux firewall will connect directly to the Internet via eth0 connected to a cable modem, though eth0 could be replaced with ppp0 for PPP via a telephone modem or DSL. The second interface, usually eth1, will be on a switch or hub connected to one or more "inside" systems. These could be any combination of Linux, Mac, UNIX, or Windows systems. This will provide a quick hands-on experience with IP Tables while protecting one's home network before it gets broken into, and buy time while the subtle art and exact science of firewalls is discussed.

For those that want their internal networks to have real (a.k.a. routable) IP addresses, simply comment out the line that has a target of

 
 MASQUERADE 


When the Linux kernel first starts all packets are allowed to all available interfaces. Prior to enabling the networking feature, unacceptable packets must be blocked. (Most distributions enable the firewall rules before bringing up interfaces, which offers protection from the get go. Unlike route commands, firewall rules referring to a particular interface may be added before that interface is enabled and they are not removed after that interface goes down, so long as the system stays up.)

However, the IP address, network address, and broadcast address of each interface must be known for proper filtering. Most SOHO users suffer with a dynamic IP address and so this IP address cannot be known prior to activating the network. The best way to deal with this conflict is to have two different scripts.

Even a "network" on the Internet consisting of a single system should be protected by the use of firewall rules. The procedure here may be used. Simply omit the INT* rules applicable to the internal network and add appropriate INPUT and OUTPUT rules for the services being allowed in or out. This would be appropriate for a system collocated at an ISP (a colo system, for short), or for a single home system.


The first script, which is called iptables_pre, will be invoked before networking is enabled. It will create rules to block all packets. The second one, called rc.fwsoho, will be invoked after networking is enabled. It will determine each interface's IP address and related settings and then will build up the firewall rules in a safe order.

If you are using SuSE, Red Hat, and Mandrake systems, copy the script iptables_pre from the book/iptables directory of the CD-ROM to the /etc/rc.d/init.d directory of your system. Then create a symlink so that it will be invoked when your system enters its default run state, typically 3. (Some other distributions may have the rc3.d directory directly under /etc or may have a default run state of 2 instead of 3.) Scripts to be used with Slackware will be covered shortly.

 
 mkdir /mnt2 ; chmod 755 /mnt2 Mount the CD-ROM device on /mnt2 cd /etc/rc.d/rc3.d cp /mnt2/book/iptables/iptables_pre ../init.d/iptables_pre ln -s ../init.d/iptables_pre S00iptables 

Next, disable the existing IP Chains or IP Tables startup scripts in rc3.d. Note that SuSE has both an initialization script and a startup (setup) script. Red Hat and Mandrake have a startup script. Rename them so that they start with a lowercase s instead of an uppercase S to prevent them from being invoked on bootup. The shutdown scripts (seen in SuSE only) should be handled the same way. The file name varies between distributions and versions.

For SuSE8.0, do:

 
 mv K02personal-firewall.final   k02personal-firewall.final mv K18SuSEfirewall2_setup       k18SuSEfirewall2_setup mv K23personal-firewall.initial k23personal-firewall.initial mv S01personal-firewall.initial s01personal-firewall.initial mv S06SuSEfirewall2_setup       s06SuSEfirewall2_setup mv S22personal-firewall.final   s22personal-firewall.final 

For RH7.3, do:

 
 mv S08ipchains s08ipchains mv S08iptables s08iptables 

For Mandrake8.2, do:

 
 mv S03iptables s03iptables mv S08ipchains s08ipchains 

Since Slackware uses the Berkeley startup scheme instead of the AT&T scheme, after copying the iptables_pre script to the /etc/rc.d directory, simply edit /etc/rc.d/rc.inet1. Near the top of it, before any network interfaces are enabled, have rc.inet1 invoke the first script,

 
 /etc/rc.d/iptables_pre 

Issue the following commands to prepare this:

 
 mkdir /mnt2 ; chmod 755 /mnt2 Mount the CD-ROM device on /mnt2 cd /etc/rc.d cp /mnt2/book/iptables/iptables_pre . favorite_editor rc.inet1 

Now, regardless of the distribution, copy the rc.fwsoho from the CD-ROM to the /etc/rc.d directory and symlink it to rc.fw:

 
 cd /etc/rc.d cp /mnt2/book/iptables/rc.fwsoho . ln -s rc.fwsoho rc.fw 

The iptables_pre script looks like:

 
 #!/bin/sh # iptables_pre: pre-block IP Tables IPT=iptables $IPT -P INPUT   DROP $IPT -P OUTPUT  DROP $IPT -P FORWARD DROP $IPT -F # dhclient (DHCP client) uses a raw socket that # bypasses IP Tables. Thus, it works even if all # packets are blocked. If you use a different program # for DHCP to set the dynamic IP address of your # Internet interface, the following may be uncommented: # $IPT -I INPUT 1 -p UDP --sport 67 --dport 68 -j ACCEPT # $IPT -I OUTPUT 1 -p UDP --sport 68 --dport 67 -j ACCEPT $IPT -t nat -P PREROUTING  DROP $IPT -t nat -P POSTROUTING DROP $IPT -t nat -P OUTPUT      DROP $IPT -t nat -F 

How can the system do DHCP discovery to initially set the IP address of its external interface with dhclient since the iptables_pre script blocks all packets? The answer is magic. Really.

The dhclient program opens a raw socket to send and receive packets directly to the interface without being bothered with IP Tables. Those with other DHCP clients that are not as fancy can ACCEPT the appropriate DHCP packets by uncommenting the appropriate lines.

If your ISP does not prevent others from injecting DHCP packets into the network, simply kill dhclient after startup. Alternatively, if you will be allowing it to change the IP address, rc.fw will need to be invoked each time. This may be accomplished by editing /etc/dhclient-script to do

 
 (cd /etc/rc.d;umask 077;./rc.fw > foo.dhclient 2>&1) echo dhclient invoked rc.fw | mailx -s dhclient root 

when the address changes. A version of the script modified to do this is in the book/iptables directory on the CD-ROM.


Now that the iptables_pre script will protect the system while the network interfaces are being brought up, it is time to arrange for the main script, rc.fwsoho (symlinked to rc.fw) to be invoked on bootup. While we could invoke it the same way we invoked iptables_pre, instead we will use a real rc.d-style script to invoke it. This rc.d-style script is based on the Red Hat 7.3 iptables startup script but has been modified to generate a message and error exit if IP Tables is not available. This could happen if IP Chains is loaded. You will want to correct such a situation, of course.

On SuSE, Red Hat, and Mandrake systems, copy the script iptables.rh73 from the book/iptables directory of the CD-ROM to the /etc/rc.d/init.d directory of your system. Then create a symlink to it so that it will be invoked when your system enters its default run state, typically 3, after networking has been enabled. You already disabled the existing IP Chains or IP Tables startup scripts. (Scripts to be used with Slackware will be covered shortly.)

 
 cd /etc/rc.d/rc3.d cp /mnt2/book/iptables/iptables.rh73 ../init.d/iptables.rh73 ln -s ../init.d/iptables.rh73 S07iptables 

Since Slackware uses the Berkeley startup scheme instead of the AT&T scheme, Slackware users simply should edit /etc/rc.d/rc.inet2. Near the top of it, invoke the rc.fw script, which is a symbolic link to rc.fwsoho:

 
 /etc/rc.d/rc.fw 

Slackware users then should issue the following commands to prepare this:

 
 cd /etc/rc.d cp /mnt2/book/iptables/rc.fwsoho . ln -s rc.fwsoho rc.fw favorite_editor rc.inet2 

Now, it is time to dive into the function of the rc.fwsoho script.

A very common error in creating firewalls is to bring up the interfaces before putting the rules in place. The default in Linux is to allow all packets through. This creates a small window of vulnerability. We have avoided this error by invoking the iptables_pre script first.


First, you need to specify the external and internal interface names. By convention, if the external interface is Ethernet, it is eth0 and the internal interface is eth1. (You can add alias commands to the /etc/modules.conf file to change the device to which eth0 and eth1 are assigned.) You will also define the path for the IP Tables and utility programs. Supplying absolute path names increases security slightly and is suggested. The following lines will do this:

 
 #!/bin/sh # rc.fwsoho: SOHO IP Tables rule set # uncomment to see each line as it is executed #set -v # External interface EXTIF=eth0 # Internal interface INTIF=eth1 IPT=iptables IFC=ifconfig G=grep SED=sed 

Second, securely clean out any existing rules. Many people botch this step, either causing their system to be insecure briefly or requiring them to reboot each time they tweak their rules. We set the policy of each chain to DROP and then flush any existing rules. This avoids the common error of flushing rules first while the policy is still ACCEPT, which would allow all packets through. We also can skip the common error of not flushing any existing rules before adding our rules. This error prevents someone from successfully reinvoking the firewall script after changing it because the existing rules will affect packets first.

 
 $IPT        -P INPUT   DROP $IPT        -P OUTPUT  DROP $IPT        -P FORWARD DROP $IPT        -F $IPT -t nat -P PREROUTING  DROP $IPT -t nat -P POSTROUTING DROP $IPT -t nat -P OUTPUT      DROP $IPT -t nat -F 

Third, we tweak the kernel for better protection against protocol-level attacks.[7] We turn on TCP SYN cookies to prevent harm from TCP SYN floods. We also turn on source address verification (rp_filter) so that the kernel will reject any packet arriving on an interface with an inappropriate address. This partially overlaps our rules to block source spoofing. We disable source routing (i.e., where the source system requests a specific route) and ICMP redirects (i.e., messages trying to alter our routing table). These are explained in detail in "Blocking IP Source Routing" on page 133 and "Blocking IP Spoofing" on page 134.

[7] On 2.2 kernels, it was necessary to explicitly tell the kernel to defragment packets. On 2.4 kernels, this happens by default.

Now that we have stopped packets going through the FORWARD chain, it is safe to enable forwarding in the kernel (later, after we selectively allow forwarding, that proc setting will be real handy):

 
 echo 1 > /proc/sys/net/ipv4/tcp_syncookies echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts # Source Address Verification for f in /proc/sys/net/ipv4/conf/*/rp_filter; do     echo 1 > $f done # Disable IP source routing and ICMP redirects for f in /proc/sys/net/ipv4/conf/*/accept_source_route; do     echo 0 > $f done for f in /proc/sys/net/ipv4/conf/*/accept_redirects; do     echo 0 > $f done echo 1 > /proc/sys/net/ipv4/ip_forward 

Fourth, we must specify the details for our external interface that connects to the Internet, our internal interface that connects to our IP Masqueraded local area, and the loopback device:

 
 EXTIP="`$IFC $EXTIF|$G addr:|$SED 's/.*addr:\([^ ]*\) .*/\1/'`" EXTBC="`$IFC $EXTIF|$G Bcast:|$SED 's/.*Bcast:\([^ ]*\) .*/\1/'`" EXTMSK="`$IFC $EXTIF|$G Mask:|$SED 's/.*Mask:\([^ ]*\)/\1/'`" EXTNET="$EXTIP/$EXTMSK" echo "EXTIP=$EXTIP EXTBC=$EXTBC EXTMSK=$EXTMSK EXTNET=$EXTNET" INTIP="`$IFC $INTIF|$G addr:|$SED 's/.*addr:\([^ ]*\) .*/\1/'`" INTBC="`$IFC $INTIF|$G Bcast:|$SED 's/.*Bcast:\([^ ]*\) .*/\1/'`" INTMSK="`$IFC $INTIF|$G Mask:|$SED 's/.*Mask:\([^ ]*\)/\1/'`" INTNET="$INTIP/$INTMSK" echo "INTIP=$INTIP INTBC=$INTBC INTMSK=$INTMSK INTNET=$INTNET" LPDIF=lo LPDIP=127.0.0.1 LPDMSK=255.0.0.0 LPDNET="$LPDIP/$LPDMSK" 

Fifth, we create two user-defined chains to log and block unacceptable packets. Unlike IP Chains, IP Tables does not offer a -l flag that could be appended to a rule's target for this purpose. Our technique avoids the cumbersome but common practice of having a -j LOG before each -j DROP or -j REJECT rule that we want to log.

 
 # Do not complain if chain already exists #   (so restart is clean) $IPT -N DROPl   2> /dev/null $IPT -A DROPl   -j LOG --log-prefix 'DROPl:' $IPT -A DROPl   -j DROP $IPT -N REJECTl 2> /dev/null $IPT -A REJECTl -j LOG --log-prefix 'REJECTl:' $IPT -A REJECTl -j REJECT 

The loopback device is a simulated device so that networking code can be used even on systems without an actual network. A peculiarity of Linux is that its process of wanting to send packets to the Internet commonly will show as inputting data from the loopback device but with the source IP address of the appropriate real device. This will occur even though the loopback network is supposed to be 127.0.0.0/8. To deal with this situation, we allow packets from this device if the source IP address matches any of our interfaces:

 
 $IPT -A INPUT   -i $LPDIF -s $LPDIP  -j ACCEPT $IPT -A INPUT   -i $LPDIF -s $EXTIP  -j ACCEPT $IPT -A INPUT   -i $LPDIF -s $INTIP  -j ACCEPT 

Next, we block broadcast packets. Usually, these are crackers trying to attack a whole range of systems with a single packet. Many ISPs do not bother to filter these out. Some broadcasts may be Windows chatter from the internal network but also could be a misconfigured or compromised system. Note that other than the loopback ACCEPT targets above, we will have most of our DROP and REJECT rules before we have any ACCEPT rules in each chain. This reduces the chance of allowing some ACCEPTs accidentally before we block all known evil packets.

 
 # Block broadcasts # (We could also do -s rules) $IPT -A INPUT   -i $EXTIF -d $EXTBC  -j DROPl $IPT -A INPUT   -i $INTIF -d $INTBC  -j DROPl $IPT -A OUTPUT  -o $EXTIF -d $EXTBC  -j DROPl $IPT -A OUTPUT  -o $INTIF -d $INTBC  -j DROPl $IPT -A FORWARD -o $EXTIF -d $EXTBC  -j DROPl $IPT -A FORWARD -o $INTIF -d $INTBC  -j DROPl 

Now, block packets sent to us through the external interface with a destination address other than our external interface's IP address. It is important to note that packets get to the INPUT and FORWARD chains after any IP Masquerading. Thus, we must not have a similar rule for the FORWARD chain.

 
 # Block Internet from trying to access internal or route thru us $IPT -A INPUT   -i $EXTIF -d ! $EXTIP  -j DROPl 

Next, block any internal system whose source address is not within our internal network. This is an absolutely critical part of Egress filtering. It will prevent a compromised internal system from possibly evading our IP Masquerading and attacking other systems on the Internet with an untraceable IP address:[8]

[8] These Egress checks would have blocked the effect of the significant bug discovered in IP Tables in May 2002. This bug in IP Masquerading failed to Masquerade certain ICMP error messages relating to Masqueraded systems. It made it easy for crackers to map out one's internal IP Masqueraded network IP even though IP Tables should be blocking this. Our additional Ring of Security, where we do not allow ICMP packets out to the Internet (except for ping requests), also protects against this bug.

 
 # Block internal with bad network address $IPT -A INPUT   -i $INTIF -s ! $INTNET -j DROPl $IPT -A OUTPUT  -o $INTIF -d ! $INTNET -j DROPl $IPT -A FORWARD -i $INTIF -s ! $INTNET -j DROPl $IPT -A FORWARD -o $INTIF -d ! $INTNET -j DROPl # One last Egress check for sanity $IPT -A OUTPUT  -o $EXTIF -d ! $EXTNET -j DROPl 

It is critical to block all outbound ICMP packets except your ping requests in order to prevent crackers from mapping your network. Despite widely believed urban legends to the contrary, this will not cause your network access to suffer due to blocked messages begging for fragmentation. While it might be convenient to allow inbound pings, crackers usually use them as their first step in seeing what addresses have systems connected. A simple telnet to the service offered is an excellent test of system health and, in fact, will show sick systems as down.

 
 # Block outbound ICMP (except ping) $IPT -A OUTPUT  -o $EXTIF -p icmp \   --icmp-type ! 8 -j DROPl $IPT -A FORWARD -o $EXTIF -p icmp \   --icmp-type ! 8 -j DROPl 

Hopefully, you will be using an intrusion detection system (IDS) such as the Cracker Trap or Portsentry to see who is trying to break into your network. (These are discussed in "Adaptive Firewalls: Raising the Drawbridge with the Cracker Trap" on page 559 and "Using PortSentry to Lock Out Hackers" on page 613, respectively.) In a short period of time you will have a list of repeat offenders that you will want to block. If you use the Adaptive Firewall called the Cracker Trap, it will generate this list automatically in the file fw.trouble. The mundane initial form of fw.trouble may be copied from the book/crackertrap directory of the CD-ROM. Even if you edit this file manually, we source its contents at this point in rc.fwsoho so that its blocking rules take effect.

 
 . /etc/rc.d/fw.trouble 

At this point most people would add rules to allow those services desired. However, I always go out of my way to block common attacks first. By having these blocking rules near the top of the rule list, there is a much smaller risk of letting these packets through accidentally. I see these attacks daily in monitoring my clients' networks. To make life easy, I suggest listing the forbidden ports in a pair of variables and using a loop for processing.

Note that we forbid internal systems from launching similar attacks on the Internet. This protects everyone else on the Internet in the event one of your systems gets compromised, say, by a Windows e-mail virus. This is part of being a good Internet citizen and also reduces your chances of getting sued for negligence.

Ports common to TCP and UDP:

 
 # Frequently attacked blocked services: COM blocks both TCP&UDP # Note that ALL UDP ports can be abused by attackers to have YOU do #   a DoS attack against the third party of their choice; this is why #   even benign UDP services are blocked # COMmon ports: # 0 is tcpmux; SGI had vulnerability, 1 is common attack # 13 is daytime # 98 is Linuxconf # 111 is sunrpc (portmap) # 137:139, 445 is Microsoft # SNMP: 161,2 # Squid flotilla: 3128, 8000, 8008, 8080 # 1214 is Morpheus or KaZaA # 2049 is NFS # 3049 is very virulent Linux Trojan, mistakable for NFS # Common attacks: 1999, 4329, 6346 # Common Trojans 12345 65535 COMBLOCK="0:1 13 98 111 137:139 161:162 445 1214 1999 \   2049 3049 4329 6346 3128 8000 8008 8080 12345 65535" # TCP ports: # 98 is Linuxconf # 512-515 is rexec, rlogin, rsh, printer(lpd) #   [very serious vulnerabilities; attacks continue daily] # 1080 is Socks proxy server # 6000 is X (NOTE X over SSH is secure and runs on TCP 22) # Block 6112 (Sun's/HP's CDE) TCPBLOCK="$COMBLOCK 98 512:515 1080 6000:6009 6112" # UDP ports: # 161:162 is SNMP # 520=RIP, 9000 is Sangoma T1/E1 card control # 517:518 are talk and ntalk (more annoying than anything) UDPBLOCK="$COMBLOCK 161:162 520 517:518 1427 9000" 

The following commands will add rules to block this evil:

 
 echo -n "FW: Blocking attacks to TCP port " for i in $TCPBLOCK; do    echo -n "$i "    $IPT -A INPUT   -p tcp --dport $i  -j DROPl    $IPT -A OUTPUT  -p tcp --dport $i  -j DROPl    $IPT -A FORWARD -p tcp --dport $i  -j DROPl done echo "" echo -n "FW: Blocking attacks to UDP port " for i in $UDPBLOCK; do    echo -n "$i "    $IPT -A INPUT   -p udp --dport $i  -j DROPl    $IPT -A FORWARD -p udp --dport $i  -j DROPl done echo "" 

Now that we have blocked a lot of nastiness unconditionally, it is time to decide what services we want to allow. This part of the script should be altered to allow only the services that you want to use right now. This follows the important security policy of allowing only the minimum needed to get the job done. While you will assume that each internal system is allowed the same services, there is no requirement for this. For example, if you do not want your child to send e-mail, his system can be blocked from sending to TCP port 25.

IP Tables' state capability solves the long-standing security problem of active FTP. When using the original FTP protocol, now called active FTP, data is transferred by having the server initiate a TCP connection back to the client to connect to a previously agreed-upon high port number on the client system. This is a very brain-damaged protocol. All firewalls should block most externally originating connections to client systems. Fortunately, IP Tables' state capability can open up only this agreed-upon port for the duration of this connection and then close it.

To enable this special FTP connection tracking, it is necessary to do a modprobe (or an insmod) on the kernel modules ip_nat_ftp and ip_conntrack_ftp. The following code does this:

 
 MODULES="ip_nat_ftp ip_conntrack_ftp" for i in $MODULES; do     echo "Inserting module $i"     modprobe $i done 

There is a similar module for IRC. If you wish to handle another popular but brain-damaged protocol, even those supported under IP Chains, you are out of luck.

It now is time to enable the services that inside systems (including the firewall itself) are allowed to use. FORWARD rules apply to systems other than the firewall itself, in case you want to make a distinction in which services are allowed where. We also introduce the --syn flag here. It will cause the rule to match only if the TCP SYN bit is set and the ACK and FIN bits are off. This combination of enabled bits is true only for the packet that starts a TCP connection.

Note that OUTPUT rules will affect only packets originating from the firewall itself and that FORWARD rules will affect only packets originating from other systems. Another difference is that in IP Chains, you specified a target of MASQ in the forward chain to indicate that the source address should be altered for outbound packets. In IP Tables, you merely ACCEPT these packets and address altering is done in the new POSTROUTING chain.

Let us assume that you want to allow ssh, http, https, ftp (passive mode), mail, pop, imap, pops, imaps, 11371 (PGP/GPG keyservers), DNS, time, and Network Time Protocol (NTP). These rules should be as specific as possible. Limiting the destination to only your ISP's DNS and mail servers by using the -d flag is an excellent idea. The following will allow this traffic:

 
 TCPSERV="domain ssh http https ftp ftp-data \    mail pop3 pop3s imap3 imaps 11371 time" UDPSERV="domain time ntp" echo -n "FW: Allowing inside systems to use service:" for i in $TCPSERV; do   echo -n "$i "   $IPT -A OUTPUT  -i $EXTIF -p tcp -s $EXTIP  \     --dport $i --syn -m state --state NEW -j ACCEPT   $IPT -A FORWARD -i $INTIF -p tcp -s $INTNET \     --dport $i --syn -m state --state NEW -j ACCEPT done echo "" 

The following will let internal systems do a standard UDP DNS lookup. It is recommended that you further restrict DNS lookups to the ISP's DNS servers by specifying each system's IP address in the -d argument of a rule.

 
 echo -n "FW: Allowing inside systems to use service:" for i in $UDPSERV; do   echo -n "$i "   $IPT -A OUTPUT  -i $EXTIF -p udp -s $EXTIP  \     --dport $i -m state --state NEW -j ACCEPT   $IPT -A FORWARD -i $INTIF -p udp -s $INTNET \     --dport $i -m state --state NEW -j ACCEPT done echo "" 

As it was with TCP and UDP, so it is with ICMP. Usually, the only ICMP type that initiates a connection is the echo request, used by the following ping command.

 
 # Allow to ping out $IPT -A OUTPUT  -o $EXTIF -p icmp -s $EXTIP  \   --icmp-type 8 -m state --state NEW -j ACCEPT $IPT -A FORWARD -i $INTIF -p icmp -s $INTNET \   --icmp-type 8 -m state --state NEW -j ACCEPT 

As part of our rings of security, we must accept the chance of the firewall being partially compromised. For example, a cracker may be able to become an ordinary user by breaking a nonroot program or password. Because of this risk, we chose not to allow the firewall to be able to access any ordinary services inside, in the previously mentioned rules. We choose to allow it to SSH into internal systems later on. Here, we allow it to ping internal systems, as the chance of compromise is remote:

 
 $IPT -A OUTPUT  -o $INTIF -p icmp -s $INTNET \     --icmp-type 8 -m state --state NEW -j ACCEPT 

Do you want anyone on the Internet to use any of the servers on your firewall? The answer probably is yes. Most mail servers are configured to send an auth (or ident) request to any system trying to send it mail and then to wait between 30 seconds and five minutes for a reply before giving up and accepting the e-mail. If you do not reply to this request, you will suffer this delay for each piece of e-mail sent out. Commonly, you will accept such ident requests but configure the firewall so that the numeric UID of the user is sent rather than a user name, which might be useful for password guessing. Securely configuring the ident server is discussed in "The ident Service" on page 231. The following rule will allow the ident service:

 
 $IPT -A INPUT   -p tcp --dport auth \     --syn -m state --state NEW -j ACCEPT 

An alternative that most mail servers will accept is to send an ICMP "get-lost" message thusly:

 
 $IPT -A INPUT   -p tcp --dport auth -j REJECT 

The REJECT target means that an ICMP type 3, subtype 3 message should be sent to say "Port unreachable."

If you have a static IP address or otherwise can determine your external interface's IP address, you may want to allow SSH from the Internet. If you have a really strong password, you could do:

 
 $IPT -A INPUT   -i $EXTIF -p tcp --dport 22 \     --syn -m state --state NEW -j ACCEPT 

If you want to be more careful, you might allow yourself to SSH in from your office at Pentacorp and also allow your friend in from the University:

 
 $IPT -A INPUT   -i $EXTIF -p tcp -s pentacorp.com/24  --dport 22 \   --syn -m state --state NEW -j ACCEPT $IPT -A INPUT   -i $EXTIF -p tcp -s chemwiz.state.edu --dport 22 \   --syn -m state --state NEW -j ACCEPT 

In any case, if you want to allow SSH from hardened internal systems (hopefully only those running Linux or UNIX hardened as per the book) add:

 
 $IPT -A INPUT   -i $INTIF -p tcp                      --dport 22 \   --syn -m state --state NEW -j ACCEPT 

To allow remote administration of your internal network, you may want to harden your internal system thoroughly, grant SSH access to the system from the firewall, and grant SSH access to the firewall from the trusted outside systems. Of course, you must be very careful with these systems to avoid a significant risk of endangering the security of the entire network. The following example will grant this access:

 
 # Connect only to hardened systems # (hopefully only those running Linux or UNIX hardened as per the book) $IPT -A OUTPUT  -o $INTIF -p tcp                      --dport 22 \   -d 10.0.0.42 --syn -m state --state NEW -j ACCEPT 

As noted, our previously stated rules in the FORWARD chain (before the SSH rules) did not indicate that the source address of outbound packets needs to be changed from that of your IP Masqueraded internal network to that of the firewall's outside interface. Instead, this is done in a subsequent chain, the POSTROUTING chain. Somewhat logically, this chain processes packets after they have been routed to their outbound interface. This chain, along with the PREROUTING and OUTPUT chains, comprise the nat table of chains.

To allow for the case where the firewall will be a proxy for servers, there is the new PREROUTING chain in the nat table. Packets are matched against this chain before routing, that is, before deciding whether they go on to the INPUT chain or to the FORWARD chain. This destination network address translation (DNAT) feature is useful even in some SOHO networks. If it is not being used, use just the following:

 
 $IPT -t nat -A PREROUTING                       -j ACCEPT 

For the SOHO rules, we need to do IP Masquerading, now called source network address translation, or SNAT. The MASQUERADE target, almost identical to the IP Chains MASQ target in the Chains' FORWARD chain, will do just fine. This must be followed by a general ACCEPT rule in the POSTROUTING chain so that other packets can be on their way. Leave out the rule that specifies a target of MASQUERADE if you do not want to Masquerade your internal network.

 
 $IPT -t nat -A POSTROUTING -o $EXTIF -s $INTNET -j MASQUERADE $IPT -t nat -A POSTROUTING                      -j ACCEPT 

It is "almost identical" because the MASQUERADE target has the additional feature that if the output interface goes down, active connection data is removed. While this is helpful for a dynamic PPP connection to prevent a very small risk of misdirected packets, I think that IP Tables again has missed the boat. This is because it fails to remove this connection data if the interface stays up but its address changes, such as may happen when a DHCP request from the ISP is received, changing the IP address (to prevent a customer from running a Web server without paying higher commercial rates).

The OUTPUT chain allows altering locally generated packets. Unless you are getting exotic, the following is needed:

 
 $IPT -t nat -A OUTPUT                           -j ACCEPT 

The IP Tables documentation recommends for static external IP addresses where you should use the SNAT target instead of the MASQUERADE target. Just to punish anyone who follows this recommendation, the SNAT target is not smart enough to figure out that addresses should be mapped to the IP address of the outgoing interface. Instead, the address of the outgoing interface must be specified. If you want to use this target instead of MASQUERADE, it would look like:

 
 $IPT -t nat -A POSTROUTING -o $EXTIF -s $INTNET \ -j SNAT --to $EXTIP 

A consequence if you use the SNAT target with a DMZ is that a separate rule would be needed for packets from the internal network going to the DMZ. (Change EXT to DMZ for that rule.)

Did we forget anything? What about all of those reply packets? What about the third packet of the initial TCP three-way handshake? With IP Chains, we would need a corresponding rule for each of these. With IP Tables, we just say "uh, allow all of the obvious related stuff, too":

 
 iptables -A INPUT   -m state \     --state ESTABLISHED,RELATED -j ACCEPT iptables -A OUTPUT  -m state \     --state ESTABLISHED,RELATED -j ACCEPT iptables -A FORWARD -m state \     --state ESTABLISHED,RELATED -j ACCEPT 

Finally, any packet that has not been otherwise handled should be logged and blocked. While a chain's policy (which takes effect if no rule in the chain matches the packet) would block the packet, the policy cannot be made to log it first. Thus, we do the following:

 
 $IPT -A INPUT   -j DROPl $IPT -A OUTPUT  -j REJECTl $IPT -A FORWARD -j DROPl 

As you recall, this complete script may be copied from the CD-ROM:

 
 cd /etc/rc.d cp /mnt2/book/iptables/rc.fwsoho . ln -s rc.fwsoho rc.fw 

It is time to reboot the system and test the firewall by exercising the allowed services and by using nmap and, possibly, Ethereal from outside and inside systems while running a

 
 tail -f /var/log/messages 

on the firewall. You would run nmap from a system on the Internet while running Ethereal on an inside system and then reverse the positions. On the Internet side, a basic set of nmap commands might be:

 
 nmap -P0 -sS -F -O -T Aggressive your_real_IP nmap -P0 -sU -F -O -T Aggressive your_real_IP nmap -sP -PI -T Aggressive your_real_IP 

Then, from an inside system, repeat the procedure but list the internal and external IP addresses of the firewall as well as the name of another system on the Internet that you are allowed to scan. Finally, repeat both scans from the firewall system itself.

Now, on to all the other details of IP Tables for fancier networks. Yes, we have just touched the surface.

12.5.2 IP Tables' Advantages over IP Chains

This connection-tracking feature can be used to prevent most hijackings of TCP connections for non IP Masqueraded clients that suffer from poor TCP sequence number randomization, such as Windows systems, some UNIX systems (notably SGI), some IBM configurations, and many older systems. It also can be used to prevent an attacker from hijacking a UDP conversation in the same way[9] and from injecting spurious ICMP packets for attacking and probing. IP Chains' IP Masquerading, the MASQ target, (and IP Tables' equivalent, MASQUERADE or SNAT) offers similar security and, in fact, IP Tables' connection tracking is based on IP Chains' IP Masquerading code. See "IP Tables Connection Tracking: Fact and Myth" on page 465 for an in-depth enlightening analysis.

[9] While the term "UDP connection" technically is incorrect because UDP is connectionless, most UDP packet exchanges occur between pairs of systems. As a practical matter, these exchanges may be considered connections even though it is programs that consider it so rather than protocol.

A significant advantage of IP Tables over IP Chains is that Active FTP now can be safely enabled for clients behind a firewall. Recall that Active FTP requires the server to be able to initiate a connection back to the client using a high port number on the client. (Passive FTP can be selected in most interactive FTP clients with the passive command. Most browsers automatically use Passive FTP.) Active FTP cannot be securely enabled under IP Chains. It can be under IP Tables. Even the SOHO set of rules enables it.

Other new features include being able to match packets based on a MAC address,[10] the local process's UID, Time To Live (TTL), or the rate of a class of packets being seen. These allow better detection and rejection of interlopers trying to inject packets or scan us. While it is easy to change the IP address of a system for spoofing, it is harder to change the MAC address, and some network interface cards (NICs) do not offer this option. While only root can open a port below 1024 for listening (for security purposes), a number of trusted services have port numbers above this and thus represent a security risk. These services include Socks (a general proxy port) and X (a general security risk). You now can restrict their use to root.

[10] MAC addresses (used for ARP resolution) are explained in "Understanding Address Resolution Protocol (ARP)" on page 145.

Incoming packets initiating TCP connections to your organization's servers can be randomly distributed among a set of servers to spread the load, with subsequent packets of a connection being routed to the same server. With IP Tables, you can specify a text string to precede the logged message. This makes understanding why the packet was logged easier. Logging of incoming packets includes logging the MAC address; this is very useful when DHCP or another dynamic IP assignment method is in use.

IP Tables has the ability to REDIRECT packets originally addressed to other systems to instead be sent to the firewall system itself (as in IP Chains). IP Tables also has a generalized DNAT feature more capable than IP Chains' portfw facility. This allows arbitrary changing of the destination IP address and port number of packets before sending them on their way. Thus, if you wanted to enforce the use of Squid automatically for Web caching, it no longer must reside on the firewall system. Any packets destined for port 80 on the Internet now can be DNAT'ed to the Squid machine.

12.5.3 IP Tables' Disadvantages Compared to IP Chains

Sadly, though, some questionable design changes were made in IP Tables. These changes make life more difficult for SysAdmins and were done for no reason that this author can fathom. I certainly am standing on my soapbox as I discuss these, but an important part of any development and one of the things that makes most Open Source software great is public review, open discussion of the advantages and disadvantages, and suggestions for improvement. These include the following, all of which easily could be corrected. I may do so and put the results on the book's Web site at

www.realworldlinuxsecurity.com/

for all to enjoy.

  1. The -l flag, used for logging, now is gone from the target specified by j. This means that to get logging (desired for most rules-blocking packets), you must have two rules, one to match and LOG and one to match and DROP. The documentation does not even mention that creating a user-defined table to LOG and DROP will eliminate these double rules. Such a user-defined table could be the following (which I usually use in my IP Tables firewalls):

     
     iptables -N DROPl iptables -A DROPl -j LOG iptables -A DROPl -j DROP 

    One then specifies -j DROPl instead of one rule ending in -j LOG and then a similar rule ending in -j DROP.

    The disadvantage of this over a true -l flag is that this will not log the rule number that caused the logging. This could be overcome to a degree by having several similar user-defined tables that make use of the LOG target's --log-prefix option. Thus, the DROPlscanner table might look like

     
     iptables -N DROPlscanner iptables -A DROPlscanner -j LOG \     --log-prefix "scanner:" \     --log-level err iptables -A DROPlscanner -j DROP 

    Surely, reimplementing -l would be almost as trivial.

  2. Packets being routed through the system (i.e.,where neither the packet's source or destination is the machine itself) are not processed by either of the INPUT or OUTPUT chains, only by the FORWARD chain (and the NAT chains). You must, therefore, have a different set of rules for packets to and from the firewall than for packets being forwarded. This offers no increase in functionality, because under IP Chains, this distinction could be made by source or destination address when desired.

    Note that these two changes (the lack of -l and the requirement of separate rules depending on whether a packet is for the firewall itself or being forwarded) means that what could be done with one rule in IP Chains now requires four rules under IP Tables.

  3. IP Masquerading (NAT) for most applications that are supported under IP Chains is not included in IP Tables. The applications that are not supported under IP Tables include Real Audio (raudio), ICQ, cuseeme, Quake, portfw, autofw, user, vdolive, and PPTP (Beta version available). There is IP Masquerading for ftp and Internet Relay Chat (IRC) through the ip_nat_ftp.o, ip_nat_irc.o, ip_conntrack_ftp.o, and ip_conntrack_irc.o kernel-loadable modules in the

     
     /lib/modules/2.4.*/kernel/net/ipv4/netfilter 

    directory.

  4. The names of IP Chains' built-in chains (input, output, and forward) were renamed to INPUT, OUTPUT, and FORWARD.

  5. The MASQ target has been replaced by the source network address translation (SNAT) target. The iptables command demands that SNAT be followed with --to-source and an IP address or a dash-separated range of IP addresses and optionally followed by a colon and port range. It does not even allow an IP followed by a slash and the number of network bits as an -s or -d value would allow. Huh? As you recall, under IP Chains, the MASQ target changed the source IP address in outgoing packets (i.e., those heading to the Internet from your internal Masqueraded systems) to show as the IP address of your firewall's external interface. Now, you are required to specify the IP of the external interface explicitly. Defaulting to the outbound interface as was done in IP Chains would make life easier on SysAdmins and reduce the likelihood of a mistake.

    The new target MASQUERADE is similar to SNAT except that connection-state information is removed if the interface goes down; this is useful for dialup connections where the IP address likely will change on the next connection. Keeping the existing MASQ name and offering a flag to indicate if state information should be removed when the interface goes down and having the --to-source flag be optional might have been a better solution.

    The IP Tables documentation emphasizes that the MASQUERADE target should be used only when the external interface is a dynamically changing IP address, such as a dialup connection (PPP, DSL, or cable modem). However, I suspect that there is no reason not to use it instead of the more difficult-to-use SNAT target.

    What if an internal system sends packets to the organization's DMZ? Under IP Chains, the packet will appear to come from the firewall's DMZ interface (rather than its external interface). What about IP Tables' SNAT? Well, if you want the same behavior, you need two SNAT rules, the first using -d to match the DMZ systems and a second one for handling packets going to the Internet. What if not all of your DMZ systems are in a convenient range of IP addresses easily maskable? You will need more rules to handle this. Just use -j MASQUERADE to make your life easier.

  6. The REDIRECT target has been renamed to DNAT. It must be followed by --to-destination.

  7. Shortcuts to reduce typing and line length to 80 characters have been eliminated. These include no longer allowing a port number specification to follow an IP address specification, separated by white space. Now, --dport or --sport must be used. The -y flag has been eliminated, too. Only its longer form, --syn, remains.

  8. The use of the - sport option for ICMP packets no longer is allowed. Only the - icmp-type option is accepted to match specific types of ICMP packets.

  9. The optional -i flag for specifying the interface with which the rule should be associated now means INPUT interface. For those chains associated with output packets, the -o flag must be used instead, the direction is not ambiguous for most chains (other than FORWARD).

  10. The -p flag must appear before any --dport or --sport flag. The explanation is that rather than being one monolithic piece of code, IP Tables has been broken up into several kernel modules, each of which is loaded only if its respective features are referenced. Thus, -p tcp will cause the tcp module to be loaded and only this module knows how to process the --dport and --sport flags.

    Good program design separates the program's user from implementation details such as this, wherever possible. A decent parser would recognize the -p wherever it appears on the line (even if it appears after --dport or --sport) and process the -p (by loading the respective module) before processing --dport or --sport.

  11. The -C command in IP Chains allowed you to ask, "If I had a packet with this protocol, source and destination IP addresses, and ports, and these options, would it be accepted, denied, or rejected?" For really stubborn rule-set bugs, I found it helpful. Unfortunately, it does not exist in IP Tables as of the 2.4.18 kernel (iptables 1.2.5). This seems to be another case of, "We cannot be bothered to port this." I do not think my soapbox is too tall to assert that dropping random features like this one from the new version of an operating system (or application) is not a sign of quality, especially now that IP Tables has been out for over a year and a half.

  12. Many new names are rather long to type, with no abbreviations offered. These include PREROUTING, POSTROUTING, MASQUERADE, and --to-destination.

  13. Chains now are grouped together into tables. Thus, to specify a rule for the PREROUTING chain (used for network address translation), you must type

     
     -t nat -I PREROUTING 

    That is a whopping 20 characters, many capitalized, to type. It is too bad that a short easy-to-type form such as

     
     -A prerte 

    is not offered.

The only real reason iptables is prevented from figuring out which rule applies to which table is the use of the same name (i.e., OUTPUT) both in the default table and the nat table. Certainly, recognizing natout as the name of the nat table's OUTPUT chain would be trivial to implement. This would eliminate the need to specify -t nat to manipulate these chains. By the way, you must name the correct table in which a user-defined chain should appear. If no table is named, the default table is assumed.

The effort to eliminate these incompatibilities would have been a small fraction of the total development time and would have made life easier for SysAdmins converting from IP Chains. Certainly, a compatibility flag (or, better still, an environment variable) still could be added that would enable support of IP Chains syntax and semantics. Upward compatibility is an important feature in any engineering project because it allows users to avoid wasting their time and money redesigning and replacing their software, scripts, etc. If a program named, say, ipchains was provided that offered IP Chains syntax and semantics but interfaced with IP Tables, painless and gradual transition would be possible.

12.5.4 IP Tables Connection Tracking: Fact and Myth

Almost all of the documentation that this author has read regarding IP Tables claims that the big advantage of IP Tables over IP Chains for most sites is the statefulness provided by its connection tracking. This means that it can be configured to allow your client systems to initiate TCP connections and UDP conversations, and for those clients to receive ICMP error messages related to these TCP and UDP packets while preventing external systems from attacking these same systems with unrelated packets.

The biggest risk that connection tracking can prevent is an attack on the port on which a client is listening while awaiting a server's response. Some clients will accept packets from any IP address and port that sends packets to them and some are vulnerable to buffer overflow attacks. The only way to protect such a client is to ensure that only a server with which it initiates communication is allowed to send packets to it.

Many SysAdmins do not realize that IP Chains' IP Masquerading (NAT) already has this state capability and will drop packets sent to its ports that were being forwarded to IP Masqueraded systems from someplace other than the IP and port with which the Masqueraded system initiated communication. Thus, if all vulnerable systems are IP Masqueraded with IP Chains, they already have this protection. I confirmed this both by inspecting the kernel's IP Chains code (in the 2.2 kernel) and by testing.[11] Once again, if all of your systems with weak network stacks are IP Masqueraded systems, you already have this protection. IP Tables' connection tracking's only apparent advantage is in protecting non IP Masqueraded systems with weak network stacks and in allowing Active FTP from clients behind it safely. At most sites, the only non IP Masquerading systems are servers in the DMZ. These servers must accept connections initiated by outside systems anyway; thus, connection tracking will not protect them. Most people use FTP from browsers (which use Passive FTP), and most command-line FTP programs accept the passive command to select this mode.

[11] To test, I caused a Linux system (a client system) on an internal IP Masqueraded network to initiate a telnet connection to a Web server on a system elsewhere on the Internet where I had root access. I then started the Ethereal packet sniffer on the client system to see what packets got through the firewall and where it sent them. Next, I used the nmap -sF scan method, which sends TCP FIN requests. It requested that an open TCP connection to the destination IP and port be closed. The intent of this scan was that if the port is not already open and connected to this remote port, an ICMP error message will be returned, providing an effective way of scanning for open ports on most firewalls. The advantage here is that if this packet actually gets through to the client system, the open telnet connection will be closed, thus providing absolute proof that the packet got through.

Next, I issued a netstat -M command on the Linux firewall system to list all active IP Masqueraded connections. The number in parentheses is the port number on the firewall that the outside (server) system sees packets from and the number before it is the port on the client system being Masqueraded. The nmap -sF scan should include -p, followed by this port number and -g followed by the port number on the server that you specified to telnet, e.g., 80. These packets will get through (and will drop the connection and be seen in Ethereal or tcpdump) only if nmap's source IP and port matches that of the server system to which the client initially sent packets.

Neither IP Tables nor IP Chains filters out packets with bad TCP sequence numbers and, thus, both leave open the small vulnerability of an attack. Many platforms, including most Windows dialects, some UNIX versions, and even ancient Linux versions (prior to the 2.0 kernel) fail to randomize TCP sequence numbers. This vulnerability is explained in "TCP Sequence Spoofing Explained" on page 243, and defeating it is discussed in "Defeating TCP Sequence Spoofing" on page 246 and in "Fighting Connection Hijacking and ICMP Attacks" on page 468.

Finally, many mail servers and some Web servers will use the ident (or auth) facility to identify what user on a client system is initiating a connection to them. They do this by sending a request to the client system's service (on TCP port 113) saying, for example, "What user has TCP port 56017 open?" They know what the client system's port number is because it is stored in the header of the TCP packet and is available to the application. Unfortunately, there is no checking of whether the system making the request is a server with an active TCP connection (or with recent UDP activity) with the port about which it is asking. Cracker scans of this port are quite common.

I assert that a good stateful firewall should drop or reject these ident requests, except for those from servers contacted recently by one's clients. To date, IP Tables (and IP Chains) do not do this. I estimate that creating the necessary ip_conntrack_ident.c and ip_nat_ident.c for either of these from the existing ip_conntrack_ftp.c and ip_nat_ftp.c kernel modules would take a day or two. A partial solution would be to use identd's -n flag to return only a numeric UID. This still tells the cracker that this port is open and ripe for attack.

Partial IP Masquerading

Running a server on a vulnerable OS platform presents an interesting problem. While a recent version of Apache running on, say, a Windows or an SGI UNIX platform is immune from most attacks, that system probably will need to act as a client to do DNS lookup, send and receive e-mail, etc. These client-side services are vulnerable to attack if the platform or its respective utilities have vulnerabilities.

What about the possibility of using IP Masquerading when such a system initiates a client request and of not using IP Masquerading when it is acting as a server? Well, this is quite possible and easy using either IP Tables or IP Chains since IP Masquerading can be used selectively, depending on the source and destination IP addresses and ports. Let us assume that the www.pentacorp.com system needs to initiate UDP DNS requests to dns.isp.com and exchange mail with mail.isp.com. The IP Tables forwarding rules might be:

 
 EXTIF=eth0 iptables -A FORWARD -p   tcp -d www.pentacorp.com \     --dport 80 -j ACCEPT iptables -A FORWARD -i $EXTIF \   -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -A FORWARD -p   tcp -s www.pentacorp.com \   ! --sport 80 -j SNAT --to-source fw.pentacorp.com iptables -A FORWARD -p ! tcp -s www.pentacorp.com \   -j SNAT --to-source fw.pentacorp.com 

The IP Chains forwarding rules might be:

 
 ipchains -A forward -p   tcp -d www.pentacorp.com 80 \   -j ACCEPT ipchains -A forward -p   tcp -s www.pentacorp.com 80 \   -j ACCEPT ipchains -A forward -p   tcp -s www.pentacorp.com \   ! --sport 80 -j MASQ ipchains -A forward -p ! tcp -s www.pentacorp.com \   -j MASQ 


If Portsentry's Advanced Stealth mode is in use, one wrong guess will lock the attacking system out. The sharpest crackers will harvest this data generated by one system, then attack from an unrelated system that would not have been locked out by Portsentry. Note that a cracker scanning a firewall where all client systems are IP Masqueraded still will not be able to send packets to the clients unless he can guess the IP and port number of the server that each client currently is communicating with. Turning off ident entirely will result in slower access to some Web pages and slower mail delivery, typically by 30 300 seconds per connection.

The ident service is discussed in detail in "The ident Service" on page 231 and also is addressed in "Cut to the Chase: Protecting a Simple SOHO Network" on page 446 and in "Basic IP Chains Firewall Usage" on page 527.

12.5.5 Fighting Connection Hijacking and ICMP Attacks

TCP connection hijacking and UDP spoofing attempts actually are quite rare. Still, you should guard against them. ICMP attacks to break in or for DoS are rare as well, though the use of ICMP packets for scanning (or probing) are very common. Taking all of the following steps will provide an effective (but not 100 percent effective) defense:

  1. Either use IP Masquerading (with either IP Tables or IP Chains) to protect systems or use IP Tables' connection tracking.

  2. Block all incoming ICMP packets except, perhaps, ping replies (ICMP type 0). IP Masqueraded or connection tracking will provide good (but not 100 percent) protection against ICMP attacks.

  3. Use Portsentry (discussed in "Using PortSentry to Lock Out Hackers" on page 613) as an Adaptive Firewall in Advanced Stealth mode protecting all ports not already open in order to lock out crackers attempting TCP and UDP attacks on client systems.

  4. Use Squid, your own mail server, and a caching (or noncaching) DNS server to insulate your client systems from external systems' protocol-level attacks via these servers.

  5. Limit other services to trusted servers or block them completely. These include RealAudio, IRC, Instant Messengers (IM),[12] Quake, etc. The more ways in, the greater the likelihood of a successful attack.

    [12] See "Instant Messenger (IM) Policy" on page 341 for details on why IM is dangerous and for policy ideas.

  6. Upgrade or replace systems not covered by the previous rules with systems that have good TCP sequence-number randomizing and well-designed network stacks. The most reliable way to obtain this information is to do a harmless scan of your systems with nmap. If your internal network is 192.168.0.0/24, run the following command (from an internal system so that firewall rules will not interfere) to analyze your systems:

     
     nmap -P0 -sS -O \    -p 21,22,23,25,53,110,80,113,139,1024,6000 \    -T Aggressive 192.168.0.0/24 

    The -P0 tells nmap to scan all systems, not just the ones that respond to pings. The -sS causes nmap to do a Stealth TCP scan. A -sT scan also would work safely. The -p flag specifies what ports to scan. We need at least one open TCP port for this analysis; select a set appropriate for your site. The ones listed here are ports commonly open on either Linux, UNIX, Windows, or Mac systems. The -T Aggressive simply causes nmap to scan faster because we are not interested in being stealthy. Lastly, 192.168.0.0/24 is our internal network to be scanned.

    The following (with the number being greater than 500,000 or so and the words Good luck!) indicates a system immune to all but the most intense brute-force connection hijacking attempt:

     
     TCP Sequence Prediction: Class=random positive increments Difficulty=1963958 (Good luck!) 

12.5.6 Red Hat 7.3's Firewall Configuration

graphics/threedangerlevel.gif

Red Hat 7.3 was released as I was struggling to finish this manuscript. As with Red Hat's other 7.x series releases, it comes with a 2.4 kernel that supports IP Tables and mostly supports IP Chains. The "mostly" is because the kernel developers do not support port forwarding on the 2.4 version of IP Chains. Their rationale is that "it is not used enough" and IP Tables does not support IP Masquerading of all of the services that were supported under the 2.2 kernel. IP Masquerading support was dropped for raudio, ICQ, cuseeme, Quake, portfw, autofw, user, vdolive, and PPTP (Beta version available) in IP Tables. Those with a single externally visible IP address who want to distribute services among multiple systems either will need to use IP Tables or a proxy server[13] or stick with a 2.2 kernel to achieve this.

[13] A proxy server is a server that listens on a port for client requests and then forwards them on to the "real" server. Socks is an example of a proxy server, as is a caching-only DNS server. A well-designed proxy server actually improves security because the connection it has with the client is separate from the one it has with the real server. Thus, neither the client nor the server can use protocol-level attacks (i.e., sending corrupt packets or otherwise violating protocols as an attack) against the other.

During installation of Red Hat 7.3, I was presented with a Firewall Configuration screen offering different levels of security (High, Medium, or No firewall), a button for default rules (I already had specified that this was to be a workstation), or a button for customization. If customization was selected I could click off which devices (interfaces) were considered trusted and click on checkboxes to enable incoming DHCP, SSH, telnet (boo, hiss), www or http, mail, or FTP (catcalls). I have become accustomed to digging through poor documentation in other systems that use such undefined quantities as "High" or "Medium." Usually, the time it takes to learn what the undefined quantities mean exceeds the time it takes to create my own script that I fully understand and document.

Not here! Red Hat had an Online Help button right there in plain sight. Better still, the documentation was detailed and clearly written. For example, it explains what High Security is:

High Security: By choosing High Security, your system will not accept connections that are not explicitly defined by you. By default, only the following connections are allowed:

  • DNS replies.

    Presumably it uses the IP Tables' RELATED feature to prevent crackers from initiating DNS attacks.

  • DHCP so any network interface that uses DHCP can be properly configured.

    Presumably it enables DHCP only if the DHCP checkbox was clicked.

Using this High Security will not allow the following:

  • Active mode FTP Passive mode FTP, used by default in most clients, should work fine.

  • IRC DCC file transfers.

  • RealAudio.

  • Remote X Window System clients.

If you are connecting your system to the Internet, but do not plan to run a server, this is the safest choice.

It then points out that additional ports may be enabled via the Customize button and the checkboxes and that additional specific port numbers may be listed (it even explains that the syntax is port:protocol). Next, it defines medium security. Its choices of what to include in each of these security levels is very good. Clearly, they have studied past problems (such as break-ins via NFS and X) and closed these holes by default. This is a better design than Mandrake, whose highest security level makes the machine unusable.

Red Hat 7.3 has another interesting feature. The service scripts, ipchains and iptables, accept a save argument that will cause the current rule set that the kernel is using to be stored in a file. This save is done when the system is shut down gracefully or on request with one of the following commands, depending on which firewall you are running:

 
 service ipchains save service iptables save 

When the system is rebooted or the respective firewall is started with start or restart, it will resume using these rules. As with SuSE 8.0's Firewall2 script, which will be looked at next, this feature is more for publicity and the simplest of firewalls.

A major disadvantage of this technique is that it prevents the use of shell variables, loops, and comments. I do not see a significant advantage to offset this disadvantage. Both will get out of hand if just a little bit of complexity is needed.

12.5.7 SuSE 8.0's Firewall Configuration

graphics/fourdangerlevel.gif

SuSE 8.0's Firewall2 script did not even do something as simple as correctly enabling FTP (active or passive in the DMZ). Debugging this problem was time-consuming and painful, both for an SuSE-experienced SysAdmin and for myself (I have a great deal of experience with IP Tables, FTP issues, and firewall techniques, but little experience with SuSE). This is the classic problem with fairly simple scripts (regardless of what platform they run on or what they create) intended to translate someone's intention into a program in a language considered too complex for the script's user to understand.

One common result: the script is not powerful enough to do truly useful stuff, and the effort to understand both how the script works and what it is doing wrong far exceeds the effort simply to write an IP Tables script directly. While less impressive or less useful in its sales literature, SuSE might have served its customers better by following my procedure of providing clients with a well-written and well-commented IP Tables or IP Chains script that they can alter directly.

I make liberal use of variables in a file that is separate from the main rules file. Clients then can edit the variables file to enable or disable specific features without needing to understand the rules set. This also allows me to supply tweaked versions of the rules file without affecting each client's customization.

The time I spent determining that SuSE's script generated a decent but not great set of IP Tables commands, and trying to fix it to enable FTP exceeded the effort to customize my standard firewall script. I disagree with SuSE's suggestion that pings and traceroutes be allowed through both to the firewall itself and to any non IP Masqueraded systems. Many crackers first ping each address they are attacking and continue the attack only if they get a ping reply.

Thus, disabling pings cuts down on attacks. Less sophisticated users will not know how to use pings or traceroutes anyway. More sophisticated users simply can do a telnet to the port of a supported TCP-based service (such as 80, 22, or 25) to test for the system and server running. It is important to note that a ping will work even on a very sick system[14] and, therefore, it is not a good status test anyway.

[14] Pings, like all ICMP packets, are generated by the kernel at interrupt time. For this reason, they will be generated even if the system is out of memory, out of process table slots, out of disk space, out of swap space, someone did a rm -rf /, or init or xinetd are dead. Any of these problems will prevent most higher level services, such as Sendmail, Apache, or SSH, from working.

I found smoothwall to suffer similar problems to SuSE's Firewall2 when I last looked at it a year ago. In both cases, the machine-generated scripts were hard to understand and convoluted. My advice is to learn enough to "roll your own" and verify its effectiveness with nmap, as discussed in "The nmap Network Mapper" on page 592. The effort will be comparable to fighting with these scripts and you will build an understanding of how the rules work. This understanding is necessary for properly configuring almost any firewall, regardless of the platform. I am otherwise impressed with SuSE's 8.0 installation procedure as trouble-free, easy, and doable by someone without needing lots of hardware knowledge and other experience.

12.5.8 Firewall Tricks and Techniques

graphics/fourdangerlevel.gif

Creating, changing, and especially debugging firewalls is a difficult process. It often is confusing, frustrating, and difficult to get right. Failure can substantially diminish the security of your network and leave you with a false sense of security. Those of you setting up your first serious firewall will discover that you need to know far more about protocols and network traffic than you ever wanted to learn.

This is true especially if more than a few simple services are used. Add and debug one or two services at a time. Do not be afraid of saying to management, "I recommend against allowing that service because of security risks." If certain users can show a need for certain services, allow those services to those users only, not to everyone.

Achieving access to a desired service on the opposite side of the firewall does not necessarily mean that you have achieved security. Use of popular scripts or firewall rule-building GUIs, consultants, or even large vendors does not guarantee that the end result, your firewall, will be secure. Many tools are overly simplistic or too cryptic for use and analysis. Alternately, those you hire may have limited understanding of your requirements and of possible solutions for your security problems. Only your understanding, analysis, testing, and audit (or demonstration of the same by whoever does the work) will ensure a secure firewall.

Here are some tips and techniques to make this work easier, faster, and more likely to be secure:

  1. Chains vs. Tables: First One Loaded Wins

    Both IP Tables and IP Chains are implemented as kernel modules in the Linux 2.4 kernel. If either currently is loaded, the other one cannot be loaded due to a lockout mechanism. There is no agreement among Linux distributions as to which to use by default, and those that use IP Chains by default may switch to defaulting to IP Tables soon. The best way to determine the default is to issue an lsmod and see if there is an ipchains module or an ip_tables module (and related modules). Then examine the startup scripts.

    In Red Hat 7.3, there is both an S08ipchains script and an S08iptables script in the /etc/rc.d/rc3.d directory. Following the UNIX System V convention, they are invoked in ASCII collating sequence (essentially alphabetically). Since S08ipchains sorts before S08iptables, it wins. To force the use of iptables, you can disable the use of ipchains with the chkconfig command:

     
     service ipchains stop chkconfig --del ipchains 

    Under IP Chains, for tight security, you need a pair of rules for each port allowed between classes of systems: one rule for the packets from the client to the server and a second rule for the replies from the server back to the client. This doubles both the effort required and the risk of an error.

    A welcome new feature in IP Tables is the state module. Instead of needing an entry to accept each reply packet for each of many services allowed, you need have only a single entry to rule them all and in the darkness see them. This rule is needed for each chain in the main table:

     
     iptables -A INPUT   -m state \     --state ESTABLISHED,RELATED -j ACCEPT iptables -A OUTPUT  -m state \     --state ESTABLISHED,RELATED -j ACCEPT iptables -A FORWARD -m state \     --state ESTABLISHED,RELATED -j ACCEPT 

    ESTABLISHED refers to the reply (return) packets traveling the exact reverse of source-to-destination path. RELATED refers to packets traveling to or from different but related ports, such as FTP's data port (TCP port 20) that is opened when a request for data transfer is made from the control port (TCP port 21). RELATED handles complicated applications such as FTP, where multiple related connections are opened.

    A major security advantage of the state module over IP Chains and IP Tables' claim to fame is that a reply packet, typically from some random system on the Internet, will be allowed through the firewall only if a client system just sent packets to the particular system and port. For example, assume that your non IP Masqueraded client systems are allowed Web access to any system on the Internet.

    Under IP Chains, you would need to allow any system on the Internet to send your clients packets from port 80 if the SYN bit is off. This runs the risk of connection hijacking, buffer overflows, and DoS attacks, especially against weak operating systems. It is quite common now for crackers to attack from port 80, 21, 22, 23, 25, or 53.

    When using IP Tables' state capability, these rogue "reply" packets will be dropped because they were not preceded by packets originating from your client systems. Since there is no equivalent of the SYN flag for the UDP and ICMP protocols, these protocols have additional risks under IP Chains that can be blocked easily under IP Tables with the state module. (IP Masquerading under either IP Chains or IP Tables provides this same state capability.)

    Even if a server that your users connect to is compromised, the server can attack one of your client systems only while the client system is connected to the same port from which the attacker is operating. This essentially limits such an attack to e-mail, Web, and FTP viruses and buffer overflows, and limits them to clients that connect to the compromised system. If this compromised server attacks an unrelated port, Portsentry's Advanced Stealth mode (if enabled on your firewall) will lock out the attacker. Make full use of IP Tables' statefulness. The cost is zero.

    If you rename S08ipchains to s08ipchains and remove the ipchains modules (including any loaded ip_masq_* modules), the kernel will allow you to load IP Tables.

    If you are using Mandrake version 8.2, there are scripts for both ipchains and iptables, S03iptables and S08ipchains. IP Tables precedes IP Chains, so iptables is the default. To force the use of ipchains, type:

     
     service iptables stop chkconfig --del iptables 

    SuSE 8.0 follows a similar convention with its IP Table startup script being

     
     S06SuSEfirewall2_setup 

    either in /etc/init.d/rc3.d or /etc/init.d/rc5.d if it is configured to start up X automatically. The presetup script is

     
     S01personal-firewall.initial 

    and the final script is

     
     S22personal-firewall.final 

    in the same directory. Note, however, that SuSE 8.0 does not provide a script for starting IP Chains. You will need to supply your own, as explained elsewhere in this section.

    Slackware follows the Berkeley Software Distribution (BSD) UNIX convention, rather than System V, and uses the rc.inet1 script in /etc/rc.d to start networking and the rc.inet2 to start the networking daemons, including IP Chains.

    As you know, a module being used by another module cannot be removed with rmmod. This use is indicated with the use count and may be shown with lsmod. As a security feature, if either IP Chains or IP Tables has any rules defined, including user-defined chains, each of these increments the module's use count. Due to this feature, all user-defined chains and rules must be removed prior to removing the ipchains or iptables module. Of course, once the rules are removed the default policy will apply to all packets. When the ipchains (or ip_tables) module is removed, no firewalling will be done, leaving a completely open system. For protection, it is recommended to first take down the interfaces (e.g., ifconfig eth0 down or ifconfig eth1 down) or disconnect the network cable.

    If you are working remotely and the firewall system itself has no vulnerable services, it is practical to take down the internal interface (eth1 by convention) and any DMZ interface (eth2 by convention) to allow continued work remotely during any development. More tips for safely and easily doing firewall development remotely are given later in this section.

    The following commands will stop IP Chains from running, to enable starting IP Tables. The same technique may be used to stop IP Tables to switch back to IP Chains. At the reboot, whatever default behavior is currently set will occur.

     
     ipchains -X        Remove user-defined chains ipchains -F        Flush remaining rules rmmod ip_masq_ftp  if loaded rmmod ip_masq_irc  if loaded ... rmmod ipchains     Remove the ipchains module 

    These IP Tables loadable kernel modules are in the

     
     /lib/modules/2.4.*/kernel/net/ipv4/netfilter 

    directory. You will need to load the ip_conntrack_* or ip_masq_* modules for the applications you will be allowing, using modprobe or insmod, e.g.,

     
     modprobe ip_conntrack_ftp 

    The modules currently loaded, their use counts, and a list of who is using them may be shown with the lsmod command.

    An alternative to the above commands would be to reboot the system, after renaming or editing the appropriate startup scripts (or removing execute permission from either ipchains or iptables).

  2. Enabling Restarting of the Firewall Rules

    A critically important feature of any firewall rule set is the ability to reinvoke the script after making changes to it, and to have the new version of the rules be installed with no momentary loss of security and no loss of existing TCP connections or other failed communication. While it takes only a bit of effort to accomplish this, the firewalls in both SuSE 8.0 and Red Hat 7.3 and most other Linux-based rule sets I have analyzed fail to accomplish this correctly.

    There are two design requirements to keep in mind when designing the order of your IP Tables or IP Chains rules or those in your UNIX, Cisco Pix, Checkpoint, or other firewall:

    1. Firewall Rule Additions Are Atomic

      Each rule added, removed, or changed is done as an atomic operation. This means that each iptables or ipchains command has an effect on the kernel at an exact instant in time. Before that instant the rule does not exist and afterwards it does. Between any pair of commands, an essentially unlimited number of packets can come in on an interface and be processed and either sent on their way out via an interface or dropped.

    2. Avoid Momentary Loss of Protection

      It is far preferable for the firewall to drop all incoming packets, even legitimate ones, for a few seconds than to be at risk and let in even one evil packet. All popular protocols and, indeed, all competently designed protocols of any importance have a retry mechanism. This retry mechanism is just what it sounds like. If a sent packet or its response gets lost, the packet is resent a short time later. If the first packet did get through and only the response got lost, the mechanism prevents the packet's action from being repeated in a harmful way.

      This mechanism will prevent, for example, a flaky network from causing one to purchase accidentally two automobiles instead of one. This mechanism in TCP, for example, easily can withstand packets being lost for a minute.

    What do these facts mean for firewall design? The first rules in any firewall script (rule set) should be to set the policy to DROP (IP Tables) or DENY (IP Chains) for all three of the chains (INPUT, OUTPUT, and FORWARD in IP Tables or input, output, and forward in IP Chains). Then, flush any existing rules with -F. Next, add rules. Finally, if a policy of "accept" is desired change the policy to ACCEPT.

    Note that by following this scheme, at no time will the firewall briefly allow evil packets through. It is important to ask yourself the question: "After each iptables or ipchains command is executed, is it possible for an evil packet to get through?" The answer always should be "No." Many SysAdmins make the serious mistake of setting the policy to ACCEPT and then flushing the existing rules with -F and adding rules. The policy always should be set to DROP or DENY before flushing.

  3. Most Firewalls Should Have a Policy of DROP, DENY, or REJECT

    I recommend strongly not having an ACCEPT policy for any built-in firewall chain as it is too easy to leave out a needed DROP, DENY, or REJECT rule and, thus, allow evil packets through. It is far better for someone's legitimate packets to be dropped and then to fix the firewall than to have someone compromised by an evil packet and then fix the firewall.

  4. Policy Actions Do Not Log

    Each chain with a policy other than ACCEPT should have its last rule be one that unconditionally has its target either logging (IP Tables) or the same target action as the policy but with logging (IP Chains). In other words, if the policy for the IP Tables FORWARD chain is DROP, it also should have as its last rule:

     
     iptables -A FORWARD -j LOG 

    Similarly, if the IP Chains input chain has a policy of DENY, it also should have as its last rule:

     
     ipchains -A input -j DENY -l 

    This is a convenient way of logging each packet that gets dropped by default. This may not apply if the volume of traffic at your site is so large that you can justify not recording these. In this case, though, it would be preferable to have rules that drop uninteresting types of attacks without logging to reduce the number of packets dropped by default at the end of each chain.

  5. Rules Are Not Removed When an Interface Goes Down

    Firewall rules referencing an interface are not removed when the interface is taken down, e.g., ifconfig eth0 down or when a PPP interface is shut down. In fact, with Linux firewalling, any interface name specified simply is stored as a string without analysis. When a packet is being evaluated, the packet's interface is matched against any interface named in the rule, with + being a wildcard, similar to the * in other subsystems. Note that this is different from a route (specified with the route command), which will be removed when the interface goes down. While any MASQUERADE rules (under IP Tables) are not removed when a transient interface goes down or when a dynamic IP address changes, the connection state information is removed from the kernel's memory.

  6. Rules Do Not Affect Raw Sockets

    A raw socket acts like a direct connection between the process that opens it and the device to which it is connected. Inbound packets received from raw sockets are not filtered by IP Tables nor by IP Chains, but outbound packets are filtered. Raw sockets typically are used by sniffers to analyze traffic. These sniffers include tcpdump, Ethereal, Portsentry (in Advanced Stealth mode), nmap, and some cracker Trojans. Raw sockets also are used by some programs to inject packets, such as nmap and some cracker Trojans, into the network.

    The use of raw sockets changes how you must deal with Portsentry when it locks out a "noisy" internal system doing broadcasts that cannot be stopped. Normally, you simply would add a rule to block the broadcast. Instead, you need to edit the portsentry.conf file and list the destination port as one to be ignored, either in the ADVANCED_EXCLUDE_TCP variable or in the ADVANCED_EXCLUDE_UDP variable.

    In testing a firewall rule set (which always should be done before deployment), Ethereal or tcpdump can be used to see what packets are coming into the system. This can be compared with what gets through IP Tables or IP Chains.

    Because outbound packets are filtered, proper strict Egress filtering will prevent you (or your enemies) from using nmap or similar tools from your firewall or internal systems. The rules will need to be relaxed temporarily for such testing.

  7. Useful Aliases

    Over the years I have found that using short names saves a lot of needless typing and that a comment to explain a short name is a more efficient solution than what I call "writing an essay to name a variable, argument, or command." This is far from a universal belief, so do what works best for you. I use tcsh aliases to allow me to type ipt for IP Tables or ipc for IP Chains. Similarly, to list the existing rules, I have the aliases iptL and ipcL. Since logging will include the rule number (with each rule number starting with 1 in its chain), I also create aliases to list existing rules with numbering. All of my listing aliases pipe through more, though you may prefer the more capable GNU less paging program.

    My tcsh firewall aliases look like:

     
     alias ipc       ipchains # list rules ... and with numbers alias ipcL      'ipchains -L -n -v \!:* | more' alias ipcLn     'ipchains -L -n -v --line-numbers \!:* \\                   | more' alias ipt       iptables # list rules ... and with numbers alias iptL      'iptables -L -n -v \!:* \\                   | sed "s/..........//" | more' alias iptLn     'iptables -L -n -v --line-numbers \!:* \\                   | sed "s/..........//" | more' # list NAT rules ... and with numbers alias iptLt     'iptables -L -n -v -t nat \!:* \\                   | sed "s/..........//" | more' alias iptLnt    'iptables -L -n -v -t nat --line-numbers \!:* \\                   | sed "s/..........//" | more' # list rules that have matched packets ... and with numbers alias iptLZ     'iptables -L -n -v \!:* \\                   | grep -v "^....0.....0." \\                   | sed "s/..........//" | more' alias iptLnZ    'iptables -L -n -v --line-numbers \!:* \\                   | grep -v "^[0-9][0-9]* *0.....0." \\                   | sed "s/..........//" | more' # zero out counts on all rules and policies alias   iptZ    'iptables -Z;iptables -t nat -Z' # do one ping of target alias p1     ping -c 1 \!\* 

    Note that the (.) characters in the sed and grep patterns represent spaces. The -L is the list command and -n suppresses DNS lookup on IP addresses. Unless DNS will succeed (possibly through /etc/hosts) on all of your IP addresses, including your private internal IP addresses, this will avoid lots of waiting for DNS timeouts. The -v flag is for verbosity and --line-numbers will include rule numbers. These numbers correspond to the value after # in the logs.

    The p1 alias does a single ping to the specified system. This is a useful way to make a quick check of whether the target system and the network are up, as well as an easy way to do DNS resolution on the host name.

    The iptLZ and iptLnZ aliases list the rules (with and without line numbers) but they omit rules that have not matched any packets. Additionally, the iptZ alias sets the counts to zero; it should be invoked before running a test. The iptLZ may be invoked after a test to show matching packets. Both aliases are very useful in determining what rules are taking effect.

  8. Selective Masquerading

    For someone designing a firewall, IP Masquerading (NAT) simply is a target not fundamentally different from ACCEPT or DROP. While it is common for a Masquerading rule to specify only a source network to match on, one need not be limited to this. For more complex schemes, additional requirements can be added. This can be used to achieve several features. One might be to not Masquerade internal addresses for packets going to your Web, FTP, or mail server in your DMZ.

    This allows logging and restrictions based on which internal system originated a connection. Additionally, this would enable your server to see that the IP address is from an internal system rather than these addresses being Masqueraded to the IP of the firewall's DMZ interface. If your internal network is 192.168.0.0/16 using an ACCEPT rule for packets with this source address and a destination of your DMZ network in the POSTROUTING nat chain prior to the MASQUERADE or SNAT rule for your internal network will enable your servers to allow different access to different internal systems.

    A second application for selective Masquerading would be to route traffic between different internal networks that are Masqueraded when communicating with the outside world. These might be the internal network and an organization-only DMZ that is separate from the DMZ accessible to the Internet.

    Another usage would be in an organization with multiple offices on the Internet, each IP Masqueraded but with a VPN between each of them. A user should not have to worry about Masquerading but merely should be able to specify the host name or IP address of the system in the other office. By forwarding these packets without Masquerading, both the client and the server system will see each other's assigned IP address.

    Another usage that a client requested was having his systems in the DMZ do remote system logging to the SysAdmin's hardened Linux system on the internal network. As you recall, the /etc/syslog.conf file can be configured to cause syslog to forward log messages to any remote system via UDP port 514. Strictly speaking, IP Masquerading would not be triggered by this anyway, because IP Masquerading is triggered by a packet originating from the internal network being Masqueraded, and the syslog protocol sends packets in only one direction. I must point out that I did recommend against the client doing this because it allows untrusted packets into the internal network, that is, packets that are not a response to a request that originated inside the internal network. As such, it represents a small security risk, small in that I assume recent versions of syslog have been audited for buffer overflow and similar vulnerabilities.

    When using IP Tables, simply add a rule with a target of

     
     -j SNAT --to-source fw.pentacorp.com 

    or

     
     -j MASQUERADE 

    in the nat POSTROUTING chain to match packets that need to be Masqueraded. It also is important to block packets from the Internet with spoofed source addresses of one of our internal networks. If the internal network is 192.168.0.0/24, if the other office's internal network (that we will VPN with) is 192.168.1.0/24, and if the internal DMZ's network is 10.0.0.0/24, the following rules will Masquerade the internal network and the internal DMZ when systems in them access the Internet, while not Masquerading between internal networks:

     

    [View full width]

    EXTIF=eth0 INTIF=eth1 DMZIF=eth2 IDMZIF=eth3 INT2IF=ppp0 INTNET=192.168.0.0/24 # Other office's INTNET, connected with VPN INT2NET=192.168.1.0/24 # Internal-only DMZ IDMZNET=10.0.0.0/24 iptables -A FORWARD -s $INTNET -i $EXTIF -j DROPl iptables -A FORWARD -s $INT2NET -i $EXTIF -j DROPl iptables -A FORWARD -s $DMZNET -i $EXTIF -j DROPl iptables -A FORWARD -s $IDMZNET -i $EXTIF -j DROPl iptables -A FORWARD -i $INTIF -o $EXTIF -j ACCEPT iptables -A FORWARD -i $IDMZIF -o $EXTIF -j ACCEPT iptables -A INPUT \ -m state - -state ESTABLISHED,RELATED -j ACCEPT iptables -A OUTPUT \ -m state - -state ESTABLISHED,RELATED -j ACCEPT iptables -A FORWARD \ -m state - -state ESTABLISHED,RELATED -j ACCEPT iptables -A FORWARD -i $INTIF -o $INT2IF -j ACCEPT iptables -A FORWARD -i $IDMZIF -o $INT2IF -j ACCEPT iptables -A FORWARD -i $INT2IF -o $INTIF -j ACCEPT iptables -A FORWARD -i $INT2IF -o $IDMZIF -j ACCEPT iptables -t nat -A PREROUTING -j ACCEPT iptables -t nat -A OUTPUT -j ACCEPT iptables -t nat -A POSTROUTING -s $INTNET -o $EXTIF -j graphics/ccc.gifMASQUERADE iptables -t nat -A POSTROUTING -s $INTNET -o $DMZIF -j graphics/ccc.gifMASQUERADE iptables -t nat -A POSTROUTING -s $IDMZNET -o $EXTIF -j graphics/ccc.gifMASQUERADE iptables -t nat -A POSTROUTING -s $IDMZNET -o $DMZIF -j graphics/ccc.gifMASQUERADE iptables -t nat -A POSTROUTING -j ACCEPT
  9. Remotely Doing Firewall Development

    Another recent book on Linux firewalls recommended against remotely doing firewall development. I had been doing remote firewall development at that time easily and securely for over a year with only a single occurrence of needing to ask the client to edit a script and reboot the system. Under Linux and UNIX, almost anything can be accomplished with a little thought or some searching on google.com. To allow remote firewall development easily and securely, apply the following guide:

    1. Make the firewall restartable without rebooting, using the techniques discussed earlier in this section in "Enabling Restarting of the Firewall Rules."

    2. When invoking the shell script that has the rules, redirect all output to file and then use more or less to see the output. This is critical. If using Bash, the following will accomplish this:

       
       cd /etc/rc.d ./rc.fw > foo 2> foo more foo /bin/rm foo 

      If using tcsh, use the following or equivalent:

       
       cd /etc/rc.d ./rc.fw >&! foo more foo /bin/rm foo 

      This redirection is necessary to avoid a subtle race condition. If there is a syntax error in an IP Tables or IP Chains command invocation or a host name cannot be resolved, an error message will be output to standard error. Also, you may have echo commands sprinkled throughout the script (which is recommended to help determine where an error occurred). If you are remotely connected, say, via SSH, this output will be sent through the TCP connection.

      What happens if these errors occur before the rule is added that allows the TCP traffic back to the SSH client system from which you are working? The data will start filling up ssh's output buffer (and the kernel's TCP buffer for this process) because ssh is receiving iptables' and echo's output through the pseudo-tty facility. What happens when iptables or echo tries to write its output to the pseudo-tty (e.g., standard error or standard output) when these buffers are full? Why, the iptables or echo process will be suspended until the output buffers' data can be moved on its way.

      Of course, the output buffers' data never will be flushed, because it needs the IP Tables rule to be added later in the script, but the script processing is waiting for the output to be written. This is a classic deadlock, sometimes called a deadly embrace. If you have not provided a Plan B, your only choice is to telephone someone with physical access to the system and talk him or her through undoing the damage. If you were editing the firewall rules that are invoked automatically when the system reboots, even a simple reboot will not recover. This problem and other errors will magically disappear if you follow the suggestion to Add a Crontab Entry to Reset Your Firewall Rules to a Usable but Safe State (see #10).

  10. Add a Crontab Entry to Reset Your Firewall Rules to a Usable but Safe State

    During remote firewall development, occasionally you will goof and lock out all remote access to the firewall system, guaranteed. A clever solution is to add a root crontab entry to revert back to a usable configuration. This usable configuration simply has to be usable enough for the remote SysAdmin to SSH back in and fix the problem. If your work is being done outside of normal hours (so that regular users and clients are not affected), a very simple set of rules can be used. I have found that simply disallowing forwarding while continuing to allow full access to and from the firewall system itself works very well.

    This technique will prevent anyone on the Internet from accessing any internal network, but will allow the remote SysAdmin SSH access to the firewall system. If access between some internal systems and the Internet is needed, the scripts presented can be enhanced to allow this or a previous working version of the full rule set could be reinstated via the crontab entry. The following IP Tables rules will reset the rules to allow recovery. It is included on the CD-ROM as book/iptables/rc.fw.offsafe:

     
     #!/bin/sh IPT=/sbin/iptables echo "Turning Firewall off BUT DISABLING forwarding." echo "DANGER!!!" # Need full path name if invoked from cron: no $PATH $IPT -P FORWARD DROP $IPT -P INPUT ACCEPT $IPT -P OUTPUT ACCEPT $IPT -F $IPT -t nat -P PREROUTING  ACCEPT $IPT -t nat -P POSTROUTING ACCEPT $IPT -t nat -P OUTPUT      ACCEPT $IPT -t nat -F 

    For those using IP Chains, the following rules will reset the rules to allow recovery. It, too, is included on the CD-ROM, as book/ipchains/rc.fw.offsafe:

     
     #!/bin/sh IPC=/sbin/ipchains echo "Turning Firewall off BUT DISABLING forwarding." echo "DANGER!!!" # Need full path name if invoked from cron: no $PATH $IPC -P forward DENY $IPC -P input   ACCEPT $IPC -P output  ACCEPT $IPC -F 

    To invoke one of these from cron periodically, first copy the appropriate one to /etc/rc.d. Then store root's current crontab file, if any, in foo. Note that we operate in a secure directory to prevent a local cracker from adding commands to foo, since it will be invoked by root.

     
     cd /root chmod go-rwx . rm foo crontab -l > foo 

    Next, decide an average amount of time that you can suffer waiting before crontab invokes your recovery script, rc.fw.offsafe. Also decide how long it will take you to test the "real" firewall script. Often, what I was testing did not work. I then would determine that the reason for the failure was that the crontab had reverted the system back to safe mode. I find that during intense development an average wait time of 10 minutes worked well.

    Double this 10 minutes because, statistically, the hang generally will occur between crontab invocations. Thus, the crontab entry should be invoked every 20 minutes. The following commands will add the correct entry:

     
     cd /root echo "0,20,40 * * * * /etc/rc.d/rc.fw.offsafe \     > /dev/null 2>&1" >> foo crontab foo /bin/rm foo 

    Generally, the firewall will have no particularly vulnerable servers or clients on it. Preferably, its only servers are SSH and, commonly, ident. Those who are being extra careful will use TCP Wrappers or firewall rules to limit even who can access SSH to prevent brute-force password guessing. However, opening up the rules briefly will present a small risk. Using TCP Wrappers to limit who can access SSH will remove this small risk.

    An annoying downside to this crontab technique is that it "messes up" your rule set being debugged. The problem is that it is hard for a program to determine if there is a defect in the rule set that is preventing you from connecting with SSH. The obvious solution of doing something similar to who | grep root will fail to detect a hang-up, as it could take hours for SSH's TCP retry capability to finally time out.

    A solution that would work nicely is to have the crontab entry that resets the firewall rules check for the existence of a file, say, /home/you/fwok.tmp. If the file exists, it is removed. If it does not exist, the firewall is reverted to the safe but accessible mode. Next, set a script running on the client system where you are working that will do an SSH into the firewall periodically to invoke touch /home/you/fwok.tmp. If the crontab entry is invoked every 20 minutes, this script should do the touch every 15 minutes or so. This is a very reliable test as it goes through the same paths in the firewall w.r.t. packet exchange as a normal interactive SSH.

    Next, from the client system, set up SSH public key access to the you account on the firewall so that a passphrase is not required. While this would let in anyone that compromises the client system for the purpose of doing an SSH, they only would be able to access this you account on the firewall, not root. For extra security, dedicate a separate account on the firewall to this purpose and configure the authorization file on the firewall for this account to allow only doing a touch /home/you/fwok.tmp via this key. (Setting up an SSH public key in this manner is explained in "Configuring SSH" on page 413. Supply the -P flag to the non-OpenSSH version of ssh-keygen to suppress the need for a passphrase. This will allow the script to run noninteractively.)

    The crontab entry on the firewall would need to be changed to invoke a script that might look like:

     
     #!/bin/sh if [ -f /home/you/fwok.tmp ] ; then      /bin/rm /home/you/fwok.tmp else      /etc/rc.d/rc.fw.offsafe fi 

    The script that you would leave running on the client system might look like:

     
     #!/bin/tcsh loop:      ssh you@fw.pentacorp.com touch /home/you/fwok.tmp      sleep 900      goto loop 
  11. Have Firewall Script Test If Standard Error Is Tty

    As noted in "Remotely Doing Firewall Development" on page 481, if doing remote debugging of firewall rules, it is critical to redirect standard output and standard error to a file (or /dev/null). This is important to prevent outputting to SSH and risking filling up the buffers and, therefore, locking out the remote SysAdmin. Since people cannot do the exact same thing correctly 100 times in a row, there also is a risk of forgetting to do the redirection. The following code can be added to the start of the rc.fw file (or whatever file you use for the firewall script). It will exit with an error if one invokes the script without redirecting its output to other than a tty.

    The script is not really clever and thus can be defeated by filtering through cat, but it would take a deliberate attempt to defeat it. Also, if one is working locally, it could be annoying. Still, it is recommended:

     
     #!/bin/sh # dup stderr to stdin for testing tty -s 0<&2 if [ $? -eq 0 ] ; then   echo "FATAL ERROR in rc.fw: no rules altered:"   echo "Invoked without redirecting stdout and stderr"   sleep 3   exit 1 fi 
  12. Changing Rules by Time of Day

    Computer security is all about reducing risk, that is, reducing the probability of something bad happening. Any given cracker likely will try attacking one or several ports of your systems once, and, if his program doing the scanning sees nothing interesting, he likely never will bother you again. After all, there are another 100,000,000 systems on the Internet for him to try. Even if, say, management allows employees to work from home during the day, how many will be working from home at 3 A.M. and will need to access all of the company's internal systems? Yet, most such companies will allow remote access and thus remote attacks 24/7. This creates a lot of opportunity for attacks, brute-force password guessing, and similar vulnerabilities.

    Why allow all of this risk? Simply close off unnecessary access when it is not needed. Suppose the Webmistress works from home during the day but very rarely works outside of business hours, and suppose you are an e-commerce site so Web-server security is critical. The date program can be used to determine the current hour of the day and day of the week. We will allow SSH access into the Web server from 8 A.M. through 5:59 P.M. Monday through Friday. The following lines of Bourne shell code added to the /etc/rc.d/rc.fw script will accomplish this:

     
     #!/bin/sh IPT=/sbin/iptables EXTIF=eth0 hour="`/bin/date +%H`" dayw="`/bin/date +%w`" # Enable SSH to web server during the business day #   Assume policy of DROP if [ $hour -ge 8 -a $hour -le 17 \   -a $dayw -ge 1 -a $dayw -le 5 ] ; then         $IPT -A FORWARD -i $EXTIF -p tcp \           -d www.pentacorp.com \           --dport 22 -j ACCEPT         $IPT -A FORWARD -o $EXTIF -p tcp \           -s www.pentacorp.com \           --sport 22 ! --syn -j ACCEPT fi 

    This script then could be invoked at 8 A.M. and 6 P.M. (0800 hours and 1800 hours) Monday through Friday with the following root crontab update:

     
     cd /root crontab  l > foo echo "0 8,18 * * 1-5 /etc/rc.d/rc.fw \   > /dev/null 2>&1" >> foo crontab foo /bin/rm foo 
  13. Routing DMZ Traffic over the VPN

    Most users and SysAdmins give little thought that their e-mail goes over the Internet unencrypted, and thus should not include confidential information. Even those that do know this and are careful probably will assume that their e-mail sent to someone else in the company is confidential. Certainly, if they know that there is a VPN between different offices, e-mail goes through the VPN. Doesn't it? Cough, cough, ahem, well, probably not.

    Typically a VPN is set up to link each office's internal IP Masqueraded network together. However, typically there is one mail server in the headquarters' DMZ, say, mail.pentacorp.com. Thus, when a user in a field office either sends mail to an employee in another office or retrieves her e-mail, she references this mail server. Since it is not an IP in the private network in the headquarters, it is not routed through the VPN. Instead, it travels over the Internet unencrypted, for anyone to see.

    This problem is not so much of a firewall problem as it is a routing problem. If the VPN interface on the firewall at the Albany field office is ppp0, one need only add the command

     
     /sbin/route add -host mail.pentacorp.com dev ppp0 

    Clearly, this will cause all packets destined for the corporate mail server to be sent into the VPN at the Albany field office. When they pop out at headquarters, they still will have the destination IP address for mail.pentacorp.com and the VPN there should route them on to the DMZ. Reply packets will have a destination IP address of the internal system in the Albany office and be routed back through the VPN automatically. Are we forgetting anything?

    Well, if the VPN is down, the interface probably will go away and with it will go the route that we just added. Packets to the mail server will start going over the Internet unencrypted again. We probably want to prevent this by adding a rule to reject such packets. The following lines in rc.fw at the Albany field office will accomplish this:

     
     EXTIF=eth0 /sbin/iptables -A FORWARD -o $EXTIF \   -d mail.pentacorp.com -j REJECT /sbin/iptables -A OUTPUT  -o $EXTIF \   -d mail.pentacorp.com -j REJECT 
  14. Check What Your IP Chains Rules Will Do with a Specified Packet

    The IP Chain c option allows checking what would be done with a particular packet. It is useful for narrowing down a problem to a particular chain. Even simple applications involve two packets, each going through at least two chains. To test traffic to our Web server, the following could be used:

     

    [View full width]

    IPC=/sbin/ipchains $IPC C input -i eth0 p tcp y s 1.2.3.4 1025 d www. graphics/ccc.gifpentacorp.com 80 $IPC C output i eth2 p tcp y s 1.2.3.4 1025 d www. graphics/ccc.gifpentacorp.com 80 $IPC C input -i eth2 p tcp -d 1.2.3.4 1025 s www. graphics/ccc.gifpentacorp.com 80 $IPC C output i eth0 p tcp -d 1.2.3.4 1025 s www. graphics/ccc.gifpentacorp.com 80
  15. First Set Policy to DROP, Then Flush the Old Rules

    This has been noted elsewhere, but it is important enough to repeat, especially since the two most popular Linux distributions' latest versions have made this mistake. Even if you want a policy (or default packet handling) of ACCEPT, first set the policy for the INPUT, OUTPUT, and FORWARD chains to DROP, flush the old rules with -F, add the new rules, and then set any desired policy to ACCEPT.

  16. iptables -X 2> /dev/null

    In both IP Tables and IP Chains, the -N command creates a new user-defined chain and the -X removes a user-defined chain. Is there a problem with this design? As noted earlier, it is very valuable to have a single firewall script that can be invoked when the system first boots up that sets the rules and that can be tweaked and invoked again to remove old rules and add new tweaked rules securely and in a hassle-free manner. The question is: "Should this script, near its beginning, use -X to remove any user-defined chains from its last invocation?" If it does, a harmless but embarrassing error message will be generated on bootup because there was no last invocation.

    Alternatively, if we do not specify -X, the -N command will fail on subsequent invocations because the new user-defined chain we are trying to create already exists. This looks like a goof in the design to me. A workaround is to send the harmless error message to the big bit bucket in the sky via the Bourne shell command

     
     /sbin/iptables -X your_chain 2> /dev/null 

    or the csh command

     
     /sbin/iptables -X your_chain >& /dev/null 
  17. Each Table Has Different Name Space for Its Chains

    This means that a chain of a particular name, such as OUTPUT or JOE can appear in each table and they will not be mixed up.

  18. DNAT and SNAT Targets Fail to Do DNS Lookup

    It is a convention in almost all Linux programs, including IP Tables, that wherever a numeric IP address is allowed, a host name may be specified and it will be resolved into the equivalent numeric IP address. This is true in IP Tables except in the argument to -j DNAT --to-destination or to -j SNAT --to-source. This seems to be a bug.

  19. Feature When Specifying a Host Name Instead of a Numeric IP Address

    Even though the inability to specify a host name with -j DNAT seems to be a bug, carefully consider before using a host name with IP Tables or IP Chains instead of a numeric IP address. Why should this program be any different from using a host name for e-mail, ssh, ping, etc.? This is a subtle gotcha. Only the most knowledgeable SysAdmins know that a host name can resolve to multiple IP addresses. This feature was intended for very large organizations that may have multiple mail, FTP, or Web servers.

    By listing multiple entries for the same host name (e.g., "A" or "MX" record), each with a different IP address, any given DNS request will get one at random, thus distributing the load for the purpose of load balancing and failover. Why should you care? Most programs just want to connect to the server and do not care if the IP address for this Web connection is the same as the next one or not. However, when firewall rules are specified, we are not worried about one connection but every subsequent connection. Cleverly, when a host name is specified to iptables or ipchains, a special DNS lookup is done.

    Every IP for that host name is used, generating multiple rules for checking all future packets. When Joe User then tries to connect to www.reallybigcorp.com he will match one of the rules as long as the DNS entries are not changed.

    The problem with specifying host names is mundane, but likely to be a problem. What if the DNS server is down when you restart the firewall? Worse, what if the network temporarily is down when the firewall is restarting? Linux and quality PC hardware is very reliable. The most likely cause of a well-configured Linux firewall going down is a power failure long enough to exceed the UPS's battery capacity. By configuring the BIOS (CMOS) and LILO to reboot automatically when power is restored, and by invoking the firewall rules and any VPN initialization during bootup, the firewall will come up automatically when power returns.

    What will happen if you specify host names in the firewall rules and the firewall system comes up before the router or the telephone equipment? Each DNS resolution will time out after a painfully long delay of several minutes. If you have one hundred of these, it may be half a day before your firewall comes up. When finally it comes up, the rules using the host names will not have been added because DNS resolution failed. By the time your users hunt you down (possibly with pitchforks and torches), the router and telephone equipment will have come up and the problem will appear to be a glitch in the firewall, which it is. Not very tidy, really.

    The solution, of course, is to determine the IP addresses and either specify them in the rules or add them to /etc/hosts. Adding to /etc/hosts will allow other programs to do quick resolution, too. My favorite solution for doing DNS resolution for such a purpose is to have the tcsh (csh) alias

     
     alias p1 ping -c 1 \!\* 

    Issuing the command p1 mail.isp.com will do a DNS lookup, a single ping, and exit, all rather quickly. In the process, ping will display the IP address.

  20. Building Rules for Host Names That Have Multiple IP Addresses

    The preferred way to get a complete list of IP addresses for a host name is by using the dig or nslookup commands with the host name as an argument, then look in the answer section of the output. For an interesting case, try

     
     dig www.yahoo.com 

    Not only will you get a lot of IP addresses, your buddy 1,000 miles away will get different IP addresses.[15] This is a fine application for the Bourne shell for command. The following example illustrates this:

     
     IPT=/sbin/iptables EXTIF=eth0 BIGWWW="64.58.76.178 64.58.76.176 64.58.76.225" for i in $BIGWWW; do Rules done 


[15] Yahoo and some other large companies cache their large files at different servers around the world. When you do a DNS lookup on one of their sites, a special program on the DNS server analyzes your IP address and determines the nearest server, then resolves the host name to that server's IP address. Yahoo and most other companies use the services of Akamai Technologies Inc. to do this. Akamai's co-founder, Daniel Lewin, died in one of the hijacked planes that Al Qaeda terrorists piloted into the World Trade Center.

12.5.9 Building an IP Tables Based Firewall with DMZ

graphics/fivedangerlevel.gif

Certainly, you will want a firewall between the Internet and your clients' systems (your users). Linux makes a fine firewall platform. However, you also probably want a firewall between your public-use systems, such as your Web server and e-mail server, and the rest of your internal network, and between your internal network and the Internet. This is to protect your public-use systems from attacks but also to protect the rest of your systems in case your public-use systems are compromised. Larger sites should have intranet firewalls protecting different parts of their internal networks from each other in case of a breach, as discussed in "Intracompany Firewalls to Contain Fires" on page 84

A single system acting as a firewall can be used to protect both the internal network and the public-use systems simply by having three network cards. One card would connect to the Internet via the ISP or upstream router and may be an Ethernet card, a T1 card, a T3 card, a modem providing PPP, etc. A second card would connect to the internal network. The third card would connect to the public-use systems such as the Web server, public-use FTP server, and e-mail server. This usually is called a Demilitarized Zone or DMZ.

Since most SysAdmins will be using either IP Tables or IP Chains, the sections concerning each of these have complete instructions. Because there is some duplication in the processes, there is some duplication of information.


It also is possible to have additional subnets protected by this single Linux firewall. One client had me separate his organization into three classes of users with different security requirements, as well as a DMZ accessible from the Internet, and an internal DMZ accessible only from within the organization. This internal DMZ sees the actual internal IP addresses of the networks due to selective IP Masquerading, discussed later.

The term DMZ comes from the Demilitarized Zone, a narrow strip of land separating North and South Korea so that soldiers in each country are far enough apart to be unable to shoot each other. Anyone inside the zone likely will get shot at.


The IP Tables facility has been available starting with the 2.4 kernel. IP Tables is available with all major distributions. The now rather-obsolete older facility was called ipfwadm and will not be discussed here. (If you are worried about firewalls and are using an older kernel, then it is time to upgrade anyway to avoid various problems. If you are determined to stay with the older kernel and use ipfwadm then most of what is discussed here can be done with ipfwadm, too.) The basic concept is that you specify which packets will be allowed to continue on their journey. The restriction can be any combination of source and destination system IP addresses, protocol type, port numbers, if the packet is the "SYN" packet that initiates a TCP/IP connection, and which interface the packet came in on or would go out on.

There are some settings that are important on the firewall system that typically are not done by default. They are necessary to prevent certain attacks. They include:

  • Defragging packets, discussed in "Fragmentation Attacks" on page 389

  • Disabling source routing and ICMP redirects, discussed in "Blocking IP Source Routing" on page 133

  • Blocking IP spoofing (though this can be done in IP Chains rules), discussed in "Blocking IP Spoofing" on page 134

  • Ignoring echo broadcasts (though this can be done in IP Chains rules), discussed in "Kernel Protocol Switches" on page 80

  • Possibly ignoring all echo requests originating externally (though this can be done in IP Chains rules), and also discussed in "Kernel Protocol Switches" on page 80

Be sure to review "Intracompany Firewalls to Contain Fires" on page 84 for some considerations for additional custom rules that you may need.

12.5.10 What IP Tables Cannot Do

IP Tables implements what is called a stateful firewall. This means that the decision to allow a packet through is made not only by its source and destination addresses, port number, and protocol, but also by whether packets that properly should precede it through the firewall have been seen. It protects against a TCP SYN flood attack, also known as a half-open attack, because the attacker sends only one of the two TCP packets needed to complete an open-session sequence.

If that final packet from the client does not arrive within a short period of time, the stateful firewall will forget that the first packet was received. This protects the servers against the SYN flood attack. Note that all Linux kernels since the early 2.2 kernels can protect themselves against this attack if configured to do so.

The Linux implementation that defends against this attack is rather clever and so there is no significant loss of efficiency even when under intense attack. This attack is explained in "SYN Flood Attack Explained" on page 245 and the proper configuration to resist it is given in "Defeating SYN Flood Attacks" on page 245. Certainly, each Linux system that might receive TCP connections from untrusted systems should be configured to resist this attack.

Another feature present in some commercial firewalls that goes beyond that in IP Tables is content filtering. However, as we just discussed, there are commercial products for Linux that do this filtering. This is where the firewall looks beyond the protocol headers, looks at the actual data in the packets, and does filtering based on the data. For example, to block viruses one might filter out any e-mail that contains an attachment that a Microsoft mail client might interpret as a visual basic (.vbs) or .exe program. Another possibility might be to block http traffic whose content or URL is considered inappropriate by management, Human Resources, or the Legal Department for the workplace or during working hours.

This would be the inappropriate disclosure of confidential data, threatening or harassing e-mail, searching for jobs in other companies, visiting sites unrelated to the organization's goals during working hours, and the like. Many organizations are interested in this feature because the law in some jurisdictions holds them liable for the actions of their employees or for competitive reasons, rather than because they have an interest in being a moral arbiter.

Squid and Squidguard offer content filtering of URLs but not of the information in the pages themselves. This seems to be a philosophical decision rather than a technical one. Sendmail has a limited filtering capability at present. Some Intrusion Detection Systems, such as Snort, have a filtering capability. See "Stateful Firewalls" on page 510, "Using Sendmail to Block E-Mail Attacks" on page 393, and "The Snort Attack Detector" on page 598.

For URL-based content filtering, Cerberian (www.cerberian.com) offers a well-designed commercial product that interfaces with Squid and connects to its huge database of categorized sites. You can block any combination of categories, such as gambling, shopping, various classes of naked people, leisure activities, etc. I have researched this product and was impressed with its capabilities.

Another content-filtering product comes from Surf Control (www.surfcontrol.com) and offers a business version for UNIX computers. There are others. Consult your favorite search engine for links to other content-filtering products.

On the surface, all of the URL-based content-filtering products are roughly equivalent, so we will let their respective marketing departments flex their muscles to convince you of their superiority. The Cerberian product got the commercial product "Authors Choice" nod because it works with Squid, one of my favorite Open Source projects.

12.5.11 IP Masquerading (NAT) Explained

These days, almost everyone's internal network is Masqueraded. This means that the IP addresses of the internal systems are private and not addressable from the Internet. Exceptions to this would be DMZs, those few organizations with as many public IP addresses available as machines, and servers at an ISP's collocation facility. Even then, Masquerading of their systems that will not be offering services to the Internet offers some security. Thus, understanding IP Masquerading is necessary for most SysAdmins, even those with home networks.

When an IP packet is received, the kernel goes down a list of rules until it finds a rule matching the packet; then it handles the packet in the manner the rule specifies. If no matching rule is found, the default action is taken. It is called a chain instead of a list to confuse everyone, especially those that know this type of data structure is called a list. This concept is similar to TCP Wrappers or lots of other configurable programs as diverse as login and nfsd. For example, when you log in, the login program reads your user name and then goes down the list of user names in /etc/passwd until it finds the matching one and, if the correct password is supplied, executes the rule. This includes setting your UID, GID, home directory, and shell.

IP Tables allows Masquerading. This concept, while very powerful, frequently is misunderstood. It means simply that the firewall system can allow the systems behind it (e.g., those on the organization's internal network) to pretend to be the firewall system. This means that if a system behind (inside) the firewall sends a packet to a system outside the firewall, it will appear to that outside system as if the packet originated from the firewall system itself. The firewall system will assign a port number temporarily to serve as the "source" of this packet. That outgoing packet's source address will be that of the firewall system itself rather than that of the originating system, from the point of view of the outside system.

When the outside system sends its reply packet, it will have a destination address of the firewall system and a destination port number that the firewall is using as the temporary source. When the firewall receives this packet, it will determine which Masqueraded system and port are associated with this temporary source port and it will send the packet on to that system. Note that the inside system will see the packets as coming from the actual server system but the server system (and all systems along the way outside of the firewall) will see the packets as coming from the firewall system itself. This is illustrated in Figure 12.2.

Figure 12.2. Masquerading packets.

graphics/12fig02.gif

In Figure 12.2, you see that research.pentacorp.com is on the Corporate Ethernet with a Masqueraded address. In other words, its address was not accessible, or routable, from the Internet. This protects it from almost all Internet attacks.[16] In this figure, we have three machines, the Internet server at www.linuxjournal.com, the firewall at pentacorp.com, and the internal workstation at research.pentacorp.com. We will follow the request packet from research.pentacorp.com (r.p.c.) through the firewall (p.c.) to the Web server at linuxjournal.com (l.c.) and follow the reply packet back to r.p.c.

[16] This will not protect it from an evil server sending either corrupt packets (i.e., a protocol-level attack) or evil content (i.e., a virus attack). However, this will protect it from another system attacking its temporary port on the firewall or even a different process on a server to which one of your Masqueraded systems has a TCP connection. I confirmed the latter by inspecting the 2.2 kernel's Masquerading code in net/ipv3/ip_masq.c. An incoming packet from the Internet is de-Masqueraded only if both its source and destination IPs and ports match that in the Masquerading table in the firewall's kernel.

I further ran tests with nmap. First, I established a TCP connection from one of my IP Masqueraded systems to a server on the Internet, S, and determined what port on my firewall was being used as the temporary port. I then ran a FIN scan against that port from a different system, A, that saw the port as closed.

I then did the FIN scan from the server system, S, which, of course, used a different port. This also saw the port as closed. Next, I then used nmap's -g flag to tell it to use a source port that was the same as the port on the S system to which I connected (i.e., TCP port 22, used by SSH). Two things happened: nmap saw the temporary port on my firewall as open and the FIN packet was forwarded to my IP Masqueraded system, which interpreted it as a request to FINish (i.e., close the connection), and so the connection was dropped.

This also implies that a cracker cannot port scan the port range that Linux uses for Masquerading with the hope of hijacking existing connections, unless he intends to guess what server IPs to spoof and, further, can break the TCP sequence randomization of the client systems (or any proxy servers, such as Squid, that might be in use). This should be an equivalent security level to that of the RELATED feature of IP Tables.

  1. r.p.c forms a packet with itself as a source address of 192.168.1.93:49157 and a destination address of 207.178.22.49:80. Because the destination address is outside of the local network, r.p.c.'s default rule routes the packet to the gateway, which is pentacorp.com.

  2. p.c reads the source and destination address of the packet, determines that it is from one of the IP Masqueraded (NAT'ed) machines on the network, and modifies the source address and port to be from itself (207.22.43.184:49179). Then, it stores the IP address and port from the original packet in a table for handling the return packet. The modified packet then is sent to l.c.

  3. l.c responds to the packet and formulates a reply packet with the source as itself and the destination of 207.22.43.184:49179, and sends the packet back to p.c.

  4. p.c receives the packet, looks up the port number in the destination address to determine who the original sender was (r.p.c.), replaces the destination with the r.p.c.'s address, then sends the packet to r.p.c.

The r.p.c. system is none the wiser that somebody has been mucking with its packets and neither is l.c.

There are two common reasons for using IP Masquerading. The first, particularly common for home networks, is to avoid being trapped by many ISPs that don't allow more than one home system. They do this simply by assigning you a single IP address and only allowing traffic from that address through their site. When you are using IP Masquerading, that is all that the ISP sees. The second reason for using Masquerading is for increased security at your site. Because you are using a private network address, a cracker cannot send packets to any of your systems behind the firewall because packets with private IP address destinations are not routed through the Internet.

If you will be Masquerading your entire internal network, it is best to use one of the official network addresses assigned by Internet Assigned Numbers Authority (IANA) in RFC1597 for this purpose. This avoids any danger of accidentally conflicting with an actual network connected to the Internet. There is one private class-A network, 16 class-B networks, and 256 class-C networks available for this purpose. They are shown in Table 12.1.

Table 12.1. Private Network Numbers

Range

Class

10.0.0.0

10.255.255.255

A

172.16.0.0

172.31.255.255

B

192.168.0.0

192.168.255.255

C


Note that 172.16.*.* is the first class-B network, 172.17.*.* is the second, etc. The first class-C network is 192.168.0.*, the second one is 192.168.1.*, and so on.

IP Tables allows the kernel to do any one of three different things to a packet.

  1. It can accept the packet and allow it to continue to its requested destination, possibly subjecting it to other checks in other chains first.

  2. It can drop the packet; this means dropping it in the bit bucket without any notice to the sending system.

  3. It can reject the packet; this causes the sending system to be told that the packet was rejected, by sending an ICMP packet back to it.

There are three different chains, or lists, of rules. At each stage, if the packet is not accepted then that is the end of the packet (except when the packet is rejected, the kernel will notify the sending system). The first chain (list) is the input chain. Once a packet has been received by the kernel and its checksum checks out and it does not appear to be corrupted, it is subjected to input chain rules. If it is accepted, it is considered for de-Masquerading. If its destination IP is the firewall and the destination port is one of those allocated for Masquerading, then the destination IP and port are changed to those of the machine being Masqueraded and the packet then is sent to be processed by the output chain.

If the incoming packet is not being de-Masqueraded, then normal routing takes place. If routing indicates that the destination is the firewall itself, then it is sent to the port requested to be received by the process listening on that port. Packets generated by local processes go directly to the output chain for processing. If the routing table says that the packet should be routed to another machine, the packet then is processed by the forward chain. Any packet that originated from someplace other than the firewall system itself and whose destination is other than the firewall system will be subject to the forward chain rules.

Packets that pass the forward chain and packets that are generated on the firewall itself then are processed according to the output chain rules. Those that pass go on their merry way. This is illustrated in Figure 12.3.

Figure 12.3. Traversing the firewall using IP Tables.

graphics/12fig03.gif

As you see in Figure 12.3, each packet is subject to either the INPUT, OUTPUT, or FORWARD chains, depending on whether the packet is going to or from a process on the firewall. This is a dramatic and incompatible change from IP Chains. It requires a major redesign of almost all firewall rule sets when transitioning from IP Chains to IP Tables. This is because it was common under IP Chains to have the input and output rules handle most filtering and have the forward rule handle only routing and network address translation (NAT).

To ensure that packet forwarding is enabled, issue the command

 
 cat /proc/sys/net/ipv4/ip_forward 

if the result is 1, then it is enabled. If it is 0, then forwarding must be enabled with the command

 
 echo 1 >> /proc/sys/net/ipv4/ip_forward 

or, for those with Red Hat or similar, by editing /etc/sysconfig/network so that FORWARD_IPV4 is set to true and the system is rebooted.


Under IP Tables, NAT is not done by the FORWARD chain. Instead, it is done by the POSTROUTING chain for the traditional hiding of internal systems. IP Tables introduces the new DNAT target, short for destination address network address translation. This allows the firewall's external IP address to be listed as a server's IP address. It then routes the packet invisible to the real server, possibly on the internal IP Masqueraded network.

IP Tables' DNAT facility has more capabilities than IP Chains' REDIRECT target. IP Tables, like IP Chains, is very efficient and requires only a low horsepower system to handle even fairly high bandwidth Internet connections. Even a 486 system should be able to service a T1 or E1. A modern Pentium should handle a T3 (45 Mbps) or larger with ease.

12.5.12 IP Tables Commands

While you could issue commands directly to the kernel using the setsockopt() system call, most people will want to use the iptables program. Only the most commonly used features will be covered here. The -P flag allows specifying the default policy for a chain if none of the rules in that chain are matched. The chain names are input, forward, and output. For any chain, the default policy or target may be ACCEPT, DROP, or REJECT. The syntax looks like

 
 iptables -P chain target [options] 

To add a rule to the end of a chain, the -A flag is used. This flag is the most frequently used flag. The syntax looks like

 
 iptables -A chain rule [options] 

A rule may include an interface specification, a source IP address and port, a destination IP address and port, a protocol specification, and a target or disposition for the packet. Each of these is introduced with a dash (-) and the type of item, followed by the value. The value may be preceded by a bang (!) to mean not that value. An interface is specified by -i or --interface, possibly followed by a bang (!), followed by the name of the interface as shown by ifconfig. If the name ends in +, it will act as a wildcard similarly to the way an asterisk (*) does for other programs. These are some typical interface specifications.

 
 -i eth0 -i ! ppp0 

A source address is specified by -s or --source; a destination address is specified by -d or --destination. Each of these accepts an IP address, specified either by host name or by numeric dotted quad. The quad may be followed by a slash (/) and a number indicating the quantity of high-order bits that should be matched. Instead of this number, a dotted quad may be used with each 1 bit tested. The rest of the bits of the address are ignored and may be anything. This address may be followed by a port name, port number, range of port numbers, or ICMP message number or name. The range is indicated by separating a pair of ports with a colon (:). Port names are looked up in /etc/services. If either the low or high port numbers of a colon-separated range are omitted, it defaults to 0 or 65535, respectively. If no port is specified, then the rule applies to all. If the protocol type is ICMP, then the "port number" instead should be the numeric ICMP code. Some examples are

 
 -s www.pentacorp.com 80 -d mail.pentacorp.com ! smtp -s 192.168.0.0/16 1024:65535 

Instead of including the source or destination ports with the respective addresses, any of --source-port, --sport, --destination-port, or --dport may be used. Finally, the --icmp-type flag may be used to specify an ICMP message name. The most commonly specified ones are listed in Table 12.2. There are alternate ways to block ICMP echos that may be preferable in some cases, discussed in "Kernel Protocol Switches" on page 80.

Table 12.2. Common ICMP Packet Types

Number

Name

Used By

0

echo-reply

ping reply

3

destination-unreachable

any TCP and UDP programs

4

source-quench

flow control

5

redirect

routing when not using routing daemon

8

echo-request

ping

11

time-exceeded

traceroute

12

parameter-problem

mostly crackers

14

timestamp-rely

mostly crackers


A fragment (i.e., the second or subsequent packets of a fragmented message) may be matched with -f. The first fragment may be matched with ! -f. The -t flag will alter the type of service (TOS) that affects the delay, throughput, reliability, and cost in the handling of the packet. This is implemented by having multiple queues; the TOS determines which queue. Thus, telnet requests do not need to get stuck behind a massive queue of FTP data packets. The types of service, the arguments for -t to specify them, and the typical uses are listed in Table 12.3.

Table 12.3. Types of Packet Service

TOS

Arguments

Possible Uses

0

echo-reply

ping reply

Minimum delay

0x01 0x10

ssh, www

Maximum throughput

0x01 0x08

smtp

Maximum reliability

0x01 0x04

payroll application

Minimum cost

0x01 0x02

nntp


Possible rules to implement this are listed below.

 
 iptables -A FORWARD -i $INTNET -p tcp --dport ssh      -t 0x01 0x10 iptables -A FORWARD -i $INTNET -p tcp --dport payroll  -t 0x01 0x04 iptables -A FORWARD -i $INTNET -p tcp --dport nntp     -t 0x01 0x02 

Finally, what to do if the packet matches this rule may be specified with the -j or --jump flags, each of which requires a target. That target normally is ACCEPT, DROP, REJECT, REDIRECT, MASQUERADE, SNAT, DNAT, or LOG, but may be a user-defined chain or a lesser used target. The target names are case-sensitive. If there is no j, then this rule will cause only side effects, such as logging and updating counts. The --syn flag may be used with -A to require the packet's SYN bit that indicates that it is the first packet of a three-way TCP open. The -F command causes all rules in the specified chain to be flushed (deleted). If no chain was specified, then all rules will be flushed in that table.

The -L command will list all rules in the specified chain or in all chains. The -C command, extremely helpful in IP Chains by allowing you to test the specified packet against the rules, was not ported to IP Tables. The -h command offers help. The -v flag causes verbosity.

12.5.13 Starting a Firewall Script

Now, let us put this information to use. We will build a standard firewall for a small organization that could be a small company or a home network. We will follow the standard convention that eth0 will be the interface going to the Internet and eth1 will be the interface to the internal network. Many home networks now are connected to the Internet via DSL or cable modems, and the interface devices typically offer Ethernet on your side. (For those using PPP, the only difference is that the Internet interface will be ppp0 and the internal interface probably will be eth0.)

The number one priority must be protection against crackers. Trying to "allow everything but what we fear" is too prone to errors, so we will "deny or reject everything but what we specifically allow." If this causes us to stop and scratch our heads occasionally when bringing up a new service or system, this is much preferable to kicking ourselves because we let a cracker through. Thus, we will follow the standard procedure of blocking everything and letting in specific types of packets that we consider safe. I prefer to do this before network interfaces are brought up, to avoid race conditions where attackers are lying in wait. On distributions that use the System V style of startup scripts, such as Red Hat and Mandrake, the scripts will be in /etc/rc.d/rc3.d and S10network will be the script that starts networking.

Next, we will build on the SOHO firewall discussed in "Cut to the Chase: Protecting a Simple SOHO Network" on page 446. Begin by building on the script that was started in the previous section. The A flag instructs iptables to add the rule to the end of the chain. There are other flags that can insert rules in the middle of chains or can alter existing rules but those will not be needed here.

We will allow any packets coming in on the loopback device with a proper source address and any going out to the loopback device with a proper destination address.These may include the IP address of the external, internal, or DMZ interface. Other addresses could be a misconfiguration or bug or could be an attempt at cracking; in any of these cases, blocking bad packets is the purpose of the firewall.

DANGER!

It is a very common mistake to first flush existing IP Tables rules with iptables -F with a policy of ACCEPT when starting or restarting the firewall. This will result in a window of time when there are no rules in place, leaving the network open to attack. Even those that should know better have made this mistake as the problem is present in both Red Hat 7.3 and SuSE 8.0.

It is critical to set the policy of the INPUT, OUTPUT, and FORWARD chains to DROP or REJECT prior to flushing with iptables -F.


The -A flag causes this rule to be added to the end of the chain. The -i flag specifies the interface to which this rule applies; for systems with more than one interface this is important. The -s flag indicates that the argument that follows it should be used to match source IP addresses. The -d flag will cause the argument that follows it to be matched against the destination address. Finally, the -j flag specifies what the kernel should do with the packet of all the conditions that match. Capitalization is significant to the target (action) specified by -j.

It is a good idea to log each rule that detects a security violation. The results will surprise you. The -l flag of IP Chains that causes the matched packet to be logged with syslogd was not ported to IP Tables. Instead, there is the LOG target. Most creators of IP Tables firewalls, including SuSE 8.0's Firewall2 have preceded each DROP or REJECT rule with one that is the same except that the target is LOG.

I came up with the idea to create a user-defined chain instead. This chain would be named DROPl (the l is the letter "el") or REJECTl for logging rejected packets. This chain would have an unconditional LOG rule followed by an unconditional DROP.

Then, in the 100 other places in the script where we want to LOG and DROP, we merely specify a target of DROPl. There is a small disadvantage to this technique in that the logged entry shows the rule number of the "universal" LOG rule rather than the rule number of the rule that matched the characteristics of the particular packet.

To help decide if this will work for you, ask yourself if you can determine from the packet's source and destination IP addresses and ports and related details why the packet was dropped? It has been my experience that once a firewall is debugged, it almost always is easy. During debugging you can sprinkle the script with LOG rules or even watch the packet counts of the individual rules increase. To create and populate these chains, add the following near the beginning of the firewall script as per rc.fwsoho:

 
 IPT=/sbin/iptables # Do not complain if already exists #   (so restart is clean) $IPT -N DROPl   2> /dev/null $IPT -A DROPl   -j LOG $IPT -A DROPl   -j DROP # Do not complain if already exists #   (so restart is clean) $IPT -N REJECTl 2> /dev/null $IPT -A REJECTl -j LOG $IPT -A REJECTl -j REJECT 

If one of these targets is referenced later in this section, it is assumed that these rules are part of the script. To use one of these to DROP all packets from the evilhackerguild.org, add the following rule:

 
 EXTIF=eth0 $IPT -A INPUT   -i $EXTIF -s evilhackerguild.org -j DROPl $IPT -A FORWARD -i $EXTIF -s evilhackerguild.org -j DROPl 

If any of the conditions do not match, the kernel tests subsequent rules either until a match occurs or until the end of the chain is reached. If the end is reached, the policy is executed; this may result in the packet being executed (dropped or rejected).

We take a similar philosophy for the internal network. It is more important here to block packets with incorrect source addresses. On a large network, there is the chance that these are from someone operating from an internal system trying to crack someone else on the Internet.

As good net citizens (who also do not want to receive visits from the Men in Black), we will be very careful about this. This is known as Egress filtering; it is explained more thoroughly in "Egress Filtering" on page 81. The rules follow.

 
 $IPT -A INPUT   -i $INTIF -s $INTNET -j ACCEPT $IPT -A FORWARD -i $INTIF -s $INTNET -j ACCEPT 

As you can see, packets coming in from the internal interface must have an internal source address and packets destined for the internal interface must have a destination address that is internal or they will not fulfill their destiny.

12.5.14 Creating a DMZ

We already have covered most of creating a DMZ. Much of the reasoning and physical organization was discussed in "Intracompany Firewalls to Contain Fires" on page 84. Most of the IP Tables work was done in "Cut to the Chase: Protecting a Simple SOHO Network" on page 446. We will start with that configuration, including installing the iptables_pre file and disabling the IP Tables and IP Chains facility that may have come with your Linux distribution. The rules granting access from the Internet to system stypically found on the DMZ, such as www.pentacorp.com or ftp.pentacorp.com, need to be added and some rules need changing (e.g., those that assume we have only a single external IP address). You will recall that packets are intended to go to these DMZ systems from both the Internet and the internal network.

Now, we need to create those rules because they are on a separate interface. Following convention, the DMZ interface will be eth2. Again, we will build on our existing example. Our organization has a class-B network. We will use subnetting to split the lower 16 bits allocated for host bits. We do this so that the uppermost of these lower 16 bits will determine whether the destination is on the network outside the firewall or on the DMZ network. These outside systems include the firewall's external IP address, possibly our upstream router, maybe some VPN boxes, or test systems, etc.

Since routers traditionally are host 1, have the uppermost bit be 0 for external addresses and 1 for DMZ addresses. (Certainly, those wanting many subnets can allocate more bits to specify the subnet and can create multiple subnets. One then would add more network cards to the firewall or would use a VLAN card and split out the different subnets here.)

If you are working with Red Hat and its progeny, you will want to edit ifcfg-eth0 in /etc/sysconfig/network-scripts; Slackware users will want to edit /etc/rc.d/rc.inet1. The netmask will change from

 
 255.255.0.0 

to

 
 255.255.128.0 

Similarly, our network and broadcast addresses will gain a network bit and lose a host bit. For Red Hat and friends, or possibly enemies, we then copy ifcfg-eth0 to ifcfg-eth2. Then, in ifcfg-eth2, weincrease the value of the network number by 1. In other words, if the entire class-B network has a network and netmask of

 
 216.247.0.0 255.255.0.0 

then eth0 will have a network and netmask of

 
 216.247.0.0 255.255.128.0 

and eth2 will have a network and netmask of

 
 216.247.128.0 255.255.128.0 

The complete rule set is on the CD-ROM as rc.fwdmz in the book/iptables directory. Following is what had to change from the rc.fwsoho script. First, we need to capture the DMZ interface name and the IP, broadcast, and network addresses:

 
 # DMZ interface DMZIF=eth2 DMZIP="`$IFC $DMZIF|$G addr:|$SED 's/.*addr:\([^ ]*\) .*/\1/'`" DMZBC="`$IFC $DMZIF|$G Bcast:|$SED 's/.*Bcast:\([^ ]*\) .*/\1/'`" DMZMSK="`$IFC $DMZIF|$G Mask:|$SED 's/.*Mask:\([^ ]*\)/\1/'`" DMZNET="$DMZIP/$DMZMSK" echo "DMZIP=$DMZIP DMZBC=$DMZBC DMZMSK=$DMZMSK DMZNET=$DMZNET" 

Recall that evil packets from the Internet (as well as those from the internal network) already have been denied so the OUTPUT rule allows anything to the DMZ. We do want to allow the appropriate services to respond to queries. However, because there have been many security problems with FTP, we do not want to allow our people access to the public FTP server. This is to prevent them from storing confidential company documents on it and risking a security breach that might make those documents public. Instead, we want to provide a separate FTP server on the internal network.

We need to mention the DMZ network where we will block broadcasts:

 
 # Block broadcasts $IPT -A INPUT   -i $DMZIF -d $DMZBC  -j DROPl $IPT -A OUTPUT  -o $DMZIF -d $DMZBC  -j DROPl $IPT -A FORWARD -o $DMZIF -d $DMZBC  -j DROPl 

Next, we must remove the following rule because it only works if we have a single external IP address. We could use user-defined chains to implement it, but since it really is somewhat redundant, we will remove it:

 
 # Block Internet from trying to access internal or route $IPT -A INPUT   -i $EXTIF -d ! $EXTIP  -j DROPl 

Next, we must add the all-important rule to block outgoing packets with bad source addresses; this is part of Egress filtering. We will add this after the # Block internal with bad network address rules:

 
 # Block DMZ      with bad network address $IPT -A INPUT   -i $DMZIF -s ! $DMZNET -j DROPl $IPT -A OUTPUT  -o $DMZIF -d ! $DMZNET -j DROPl $IPT -A FORWARD -i $DMZIF -s ! $DMZNET -j DROPl $IPT -A FORWARD -o $DMZIF -d ! $DMZNET -j DROPl 

We can allow the firewall to ping systems in the DMZ here, after the position where we allow the firewall to ping internal systems:

 
 # Allow firewall to ping DMZ      systems $IPT -A OUTPUT  -o $DMZIF -p icmp -s $DMZNET \   --icmp-type 8 -m state --state NEW -j ACCEPT 

The custom part of these rules specifies which systems in the DMZ will be allowed to receive which services. Just edit the appropriate variables here, which should be near the top of the script, to specify the IP address (not the host name) of each server in use. The same system may be listed in multiple variables but only a single system may be listed in each. We will also list the ISP's DNS and mail servers.

 
 # DMZ servers DNS1_IP="" HTTP_IP="" HTTPS_IP="" FTP_IP="" MAIL_IP="" POP3_IP="" POP3S_IP="" IMAP3_IP="" IMAP3S_IP="" ISPDNS1="" ISPMAIL1="" 

This rule redirects any auth requests back to the firewall to handle; this will protect your servers from these attacks. It should be done for any server that sends mail out to the Internet. Unless you have your users use your mail server as a "smart" relay host, this probably will be all of the servers except for the DNS servers. Hence, we will map the whole subnet.

 
 # Redirect auth requests to DMZ servers back to firewall # May be limited to servers that send mail out to Internet $IPT -t nat -A PREROUTING -p tcp --dport auth --syn -m state \    --state NEW -d $DMZNET -j DNAT --to-destination $EXTIP 

The rules to enable access to these servers are below. These rules may be placed after the auth rules.

 
 # Enable defined DMZ servers if [ "$DNS1_IP" != "" ] ; then   $IPT -A FORWARD -i ! $DMZIF -p udp -d $DNS1_IP \     --dport domain       -m state --state NEW -j ACCEPT fi # Usually do not enable TCP DNS to avoid Zone Transfers, etc. if [ "$HTTP_IP" != "" ] ; then   $IPT -A FORWARD -i ! $DMZIF -p tcp -d $HTTP_IP \     --dport http   --syn -m state --state NEW -j ACCEPT fi if [ "$HTTPS_IP" != "" ] ; then   $IPT -A FORWARD -i ! $DMZIF -p tcp -d $HTTPS_IP \     --dport https  --syn -m state --state NEW -j ACCEPT fi if [ "$FTP_IP" != "" ] ; then   $IPT -A FORWARD -i ! $DMZIF -p tcp -d $FTP_IP \     --dport ftp    --syn -m state --state NEW -j ACCEPT fi if [ "$MAIL_IP" != "" ] ; then   $IPT -A FORWARD -i ! $DMZIF -p tcp -d $MAIL_IP \     --dport smtp   --syn -m state --state NEW -j ACCEPT fi if [ "$POP3_IP" != "" ] ; then   $IPT -A FORWARD -i ! $DMZIF -p tcp -d $POP3_IP \     --dport pop3   --syn -m state --state NEW -j ACCEPT fi if [ "$POP3S_IP" != "" ] ; then   $IPT -A FORWARD -i ! $DMZIF -p tcp -d $POP3S_IP \     --dport pop3s  --syn -m state --state NEW -j ACCEPT fi if [ "$IMAP3_IP" != "" ] ; then   $IPT -A FORWARD -i ! $DMZIF -p tcp -d $IMAP3_IP \     --dport imap3  --syn -m state --state NEW -j ACCEPT fi if [ "$IMAP3S_IP" != "" ] ; then   $IPT -A FORWARD -i ! $DMZIF -p tcp -d $IMAP3S_IP \     --dport imap3s --syn -m state --state NEW -j ACCEPT fi 

These rules do not enable access from the firewall itself as that would require an additional rule for each, thanks to IP Tables' cumbersome design.[17] The following would enable access from the firewall itself to the Web server, for example:

[17] Several people have suggested that, as a compromise, one could create a user-defined table and throw all packets from the INPUT and FORWARD chains onto it for joint processing. Of course, sometimes you want to process packets on the FORWARD chain similar to the way you process those on the OUTPUT chain. Perhaps throwing FORWARD packets that come from certain interfaces onto the "mostly INPUT" user-defined chain and throwing FORWARD packets from other interfaces onto the "mostly OUTPUT" user-defined chain might work. Maybe I'm just missing something.

 
 $IPT -A INPUT   -i $DMZIF -p tcp -s $DMZIP -d $HTTP_IP \   --dport http --syn -m state --state NEW -j ACCEPT 

Do the servers need to act as clients? The Web server and mail server probably want to do DNS for logging and security checks. At least one of them probably will want to send out mail. The whole purpose of a DMZ is to limit the damage done if a server is compromised. This limitation should take into account the damage that might be done to the rest of the Internet from a cracker using one of your servers to attack other systems. Besides getting you blamed, possibly on the front page of the Wall Street Journal or in court, if you pay for bandwidth by the byte, your costs could go very high.

These rules should be as specific as possible. Limiting the destination to only your ISP's DNS and mail servers with -d is an excellent idea. Replacing $DMZNET with addresses of specific systems in your DMZ needing the access is desirable too:

 
 # Enable DMZ systems to use the services that they need if [ "$ISPDNS1" != "" ] ; then   $IPT -A FORWARD -i $DMZIF -p udp -s $DMZNET \     --dport domain -d $ISPDNS1 -m state --state NEW -j ACCEPT fi if [ "$ISPMAIL1" != "" ] ; then   $IPT -A FORWARD -i $DMZIF -p tcp -s $DMZNET \     --dport smtp   -d $ISPMAIL1 -m state --state NEW -j ACCEPT fi 

After our one rule with a MASQUERADE target (for Masquerading initial packets from internal systems to the Internet), we will add a corresponding rule to Masquerade packets from internal systems to the DMZ. (If we are not Masquerading our internal network, do not use this rule.)

 
 $IPT -t nat -A POSTROUTING -o $DMZIF -s $INTNET -j MASQUERADE 

Lastly, let's have a bit of magic to avoid dealing with our upstream router. While a Linux firewall makes a fine easy-to-configure T1/E1/Frame Relay Router with the addition of a Sangoma or similar network card, many organizations are stuck with a Cisco Router. Often, these Cisco Routers are managed by the ISP or others who are hard to deal with and are not always terribly knowledgeable, based on my experience. Something as simple as "we are inserting a firewall whose IP is thus and such, please reconfigure to route all of our traffic through it, except these XYZ outside IP addresses" can be beyond the capability of many.

The answer is Proxy ARP. Briefly, with Proxy ARP, when a system on the outside interface assumes that a DMZ system (or non IP Masqueraded inside system) is connected directly to the outside LAN, this outside system will send an ARP request to discover the inside system's MAC address. With Proxy ARP enabled on the firewall, it will provide its own outside interface's MAC address and forward the packets to the DMZ automatically. More details are discussed in "Understanding Address Resolution Protocol (ARP)" on page 145. The following lines will enable this.

 
 echo "Enabling Proxy ARP from the External interface" echo 1 > /proc/sys/net/ipv4/conf/$EXTIF/proxy_arp 

Proxy ARP does no harm to those systems that are configured to route the traffic through the firewall. Where desired, Proxy ARP can be enabled for the other interfaces, too. For example, if enabled from the DMZ with

 
 echo "Enabling Proxy ARP from the DMZ interface" echo 1 > /proc/sys/net/ipv4/conf/$DMZIF/proxy_arp 

the DMZ systems will not need their gateway system's IP updated to the firewall system either.

12.5.15 Routing Secrets

graphics/threedangerlevel.gif

Most documentation on firewalls and routers gives the usual spiel about having more bits in the network mask of the internal subnets than in the external network. Back in the days when everyone had at least a full class-C network, that was fine. What about now, where you may get only two or three real IP addresses, after writing off both the host portion of all zeros and all ones to broadcast addresses?[18] What can you do to not waste half of the address space on the external network where only the firewall and possibly the upstream router reside?

[18] Originally, if the host portion of an IP address was all zeros, it would be interpreted as a broadcast to the entire network. A short time later, the "Einstein" who came up with this discovered that unconfigured systems commonly had a host portion of the IP as all zeros. This caused lots of unintentional broadcasts. Thus, it was decided that a host portion of all ones was a better broadcast address. Decades later, nobody has bothered to free up the all zeros address as usable. Most operating systems still interpret the latter as a broadcast address.

The answer: Use the route command to add routes for specific host addresses. It is helpful to keep in mind that an interface's network routing entry only is used if a more specific route is not found first. Routes are sorted, starting with the most specific first (the largest number of 1 bits in the netmask). These host-specific route commands will take priority. This allows using addresses that are outside of the range allocated for your DMZ, for example, to be used for systems that are inside the DMZ, over those that are generated automatically when an interface is brought up.

We are not stuck even with giving up two addresses for the external interface's broadcast addresses and another two for the DMZ's broadcast addresses. Have the DMZ use the high half of the address range. Thus, the same broadcast address is used for both. What happens when sending out a broadcast? (You should not be sending broadcasts anyway, but the system won't melt if you do.) What do you do with the DMZ all-zeros broadcast address? Why, use it for a system outside of the firewall that does not need to access systems inside.

As it turns out, though undocumented, when deciding how to route a packet, the Linux kernel will check the IP address and broadcast address for each operating interface before checking the routing table. An interesting consequence of this is that if an interface's broadcast address is that of a system to which you are trying to send packets (either intentionally or by mistake), that system had best be on that interface 'cause that's where the packet is going.

If you use these routing secrets, your firewall rules probably will need to be changed. This is because of assumptions about network masks and where systems belong that match them. For example, blocking a packet from the DMZ whose source address does not match $DMZNET no longer will work. Instead, add a rule that matches each DMZ system's source IP address on the outside interface (where it should not be) and drop the packet.

12.5.16 IP Tables' Lesser Used Features

graphics/threedangerlevel.gif

IP Tables has some other features of interest, primarily for those managing large shops. Perhaps the most useful is the limit module. It allows IP Tables to match only until a packet-rate limit is reached. The manual suggests this is useful with the LOG target to limit the amount of logging done (e.g., when your system is under intense attack). This might help fight Denial of Service (DoS) attacks, especially w.r.t. the time of the SysAdmin.

I can see using the logging feature instead to limit how much of one's bandwidth is used for particular services. The syntax is:

 
 -m limit --limit rate[/{second,minute,hour,day}] 

The MAC (or Ethernet) address of a locally connected system can be matched via:

 
 -m mac --mac-source [[!] XX:XX:XX:XX:XX:XX] 

This can be useful to protect against an in-house "script kiddie" who knows enough to change her IP address but not enough to change her MAC address. Instead, I recommend specifying permanent ARP table entries, as discussed in "Preventing ARP Cache Poisoning" on page 146 and peaking ahead to "Using Arpwatch to Catch ARP and MAC Attacks" on page 626.

The --tcp-flags option can be used for other matching of TCP flags besides the use of --syn. You can use

 
 -m state --state INVALID 

to detect crackers trying to send packets that violate protocols in order to breach the firewall.

Lastly, IP Tables supports IPV6 addressing. IPV6 will happen right after the U.S. and Great Britain adopt the metric system. The latter was going to happen within about five years, according to my third-grade teacher.

The -m owner module can be used to match packets originating from a particular user or process on the firewall, using the --uid-owner, --gid-owner, --pid-owner, --sid-owner (or session ID), or --cmd-owner name. This can be useful for collocated systems or for those that violate the important security rule of running different services on different systems.

12.5.17 Stateful Firewalls

graphics/threedangerlevel.gif

The firewall techniques discussed in the previous sections offer excellent protection from many common types of attacks and definitely should be used. Unlike IP Chains, these IP Tables rules allow the protection of a fully capable stateful firewall. Note that if all of your nonserver systems are IP Masqueraded, IP Chains already is providing this stateful capability. Let us examine the protection that this statefulness offers.

One of the holes against which IP Tables protects is some violations of protocols, such as the use of echo replies for network mapping and activating DDoS zombies. Another threat that has been increasing is the use of traditionally open ports, such as TCP ports 23 and 80 (i.e., telnet and www) to run services unrelated to the intended purposes of these ports in order to circumvent written policy and firewall rules.

Some organizations put employee home systems in the organization's DNS server with names like steve-home.pentacorp.com, containing the home system's IP address, as this makes it easy to refer to the remote systems. In these cases, if a cracker dumps the DNS table via zone transfer, he locates wonderful targets for attack.

Not only does this open up an employee's home system for attack, many of which are not secured, but it opens up the organization's system to attack via their employee's home system. This allows a cracker to attack the organization through an employee's compromised system.


While even Squid (a popular open-source Web-page caching program) can block access to undesirable sites, some people will use "redirecting" Web sites that are not blocked (i.e., unless you specifically block them) to route requests to undesirable sites.

Microsoft offers products based on Simple Object Access Protocol (SOAP) as a way to avoid dealing with firewalls limiting access to approved traffic. A typical SOAP request is disguised to look like an ordinary HTTP request. The following is an example that Microsoft quotes.[19]

[19] Taken from http://msdn.microsoft.com/library/periodic/period00/soap.htm. This URL no longer is available.

http://skonnard.com/soaplike/businessobj.asp?param1=hello+world

Fortunately, the SOAP specification uses the Content-Type header value text/xml-SOAP; this allows easy blocking using the Squid tools discussed below. (SOAP and some of these other tunnelling techniques were discussed in "Stopping End Runs Around Firewalls" on page 74.) There also is Squidguard, which also offers blocking. Lastly, there is Junkbuster, which is designed specifically to shield the person browsing from ads, intrusive cookies, or Web sites getting information about them. These are available at the following locations and on the CD-ROM and Web site.

www.squid-cache.org/

www.squidguard.org/

www.junkbusters.com/

The Linux 2.4 kernel, released in mid-2000, offers a stateful firewall capability with IP Tables, also known as NETFILTER. It allows you to control the rate of various packets being accepted and to shunt certain packets off to user-level programs for further analysis to allow stateful firewalls. Also, there is Phoenix Adaptive Firewall and Checkpoint. While Phoenix is an expensive commercial product costing about U.S.$3,000, it is the first firewall to be ICSA certified for Linux. This makes it a good Linux solution for big and stuffy shops and an excellent alternative to products running on closed-source alternatives.

Various stateful firewall possibilities are discussed at these locations:

www.netfilter.org/

www.seifried.org/lasg/network-servers/proxy/index.html

www.obfuscation.org/ipf/ipf-howto.txt

coombs.anu.edu.au/~avalon/ip-filter.html

12.5.18 SSH Dangers

graphics/fourdangerlevel.gif

The firewall rules typically would block most service requests into the internal network. Certainly, the default should be to block all externally originated requests (or connections) that are trying to get into your internal network, then let in specific services. Under special conditions, you may want to allow some access to particular hardened systems that are on your internal network, though it is preferable to have all such systems in the DMZ.

Generally, it should be OK to allow ssh requests to your internal systems so your employees can work from home. For more security, one possibility is to use strong passwords combined with allowing access to ssh only from certain IP addresses or networks. Alternatively, use really strong passwords and a good Intrusion Detection System with at least near real-time pager capability. (See "Using Logcheck to Check Log Files You Never Check" on page 608) Understand that if your organization is a large entity then the probability is good that at least some of your users' home systems will be compromised, usually in exploits unrelated to company activities. Employees who are less knowledgeable about security and using operating systems less secure than Linux are at the greatest risk.

Home systems that have continuous connections, such as those with cable modems and DSL, are at the greatest risk of suffering a break-in. Cable modems have the additional risk of being networked with other nearby subscribers in what could be thought of as a LAN.

The question is: If a home system is broken, will the cracker simply look around and "hide behind" it to attack other random systems or will he be sophisticated enough to monitor your employee's activities and discover the SSH passkey and then invade your organization's systems? While I have not seen any reports of the latter, it is a real danger. The more attractive your site is to crackers and the larger it is, the more likely this is to happen. A cracker even could target your employees' home systems for such an attack. This targeting is not hard to do.

A cracker merely needs to search the Web and News groups for your organization's name and search these pages for employee names. Then he would do the same search on the employees' names looking for e-mail addresses unrelated to your organization. These would be the employees' home e-mail addresses. With some ISPs, the e-mail address may be mapped directly to the fully qualified host name of the subscriber's home system when connected.

Then, the cracker merely needs to find one employee who has not bothered to secure his system. This is the risk you must contend with if you allow employees to connect from their home systems. Clearly, the more employees you let in, the greater the risk of this sort of attack, and the greater the risk of a cracker breaking into an employee's system and attacking your organization's network.

This, alone, would be a justification for denying even SSH. Except for those installations that need high security, it might be reasonable for you to evaluate the security of the home system of each employee that requests SSH access to her office system. It is very important to have written management approval to conduct this home-system security audit, as some users might complain and this otherwise could result in problems for the SysAdmin.

Additionally, there may be laws in your area affecting this activity that need to be reviewed. This audit might involve a list of questions the employee must answer, either verbally or in electronic or written form. It is suggested that you e-mail the list of questions to the employee, offer the option of responding electronically or in writing, print out the responses, then have the employee review and sign the printout. This signature also should guarantee that the employee will contact a SysAdmin prior to doing anything that reduces home security and therefore possibly affecting the organization's security.

Once you have management approval, SysAdmins will want to make this check of security of each home system by seeing what ports are open, asking the user what services are supported and what systems are allowed to use them, examining what versions of critical software are running, and looking at how secure the passwords are. For home Linux systems, you will be particularly interested in what versions of telnet, FTP, sendmail (unless blocked by the ISP), named, and Netscape are in use. In short, review those programs where buggy versions allow break-ins, as discussed in "Quick Fixes for Common Problems" on page 17. You will also be interested in the use of intracompany firewalls and similar techniques. These are discussed in "Intracompany Firewalls to Contain Fires" on page 84.

Certainly, by allowing this SSH connection, or a similar connection such as TCP Wrapper access, between the organization's network and a user's home system, you are adding the user's home system to the organization's network. It is important, therefore, to educate each home user on security. Even those of you running other operating systems could benefit from some of the information in the book. Also, you may want to write up some notes on security to be supplied to users seeking SSH access. You might even want to have a quiz on the material before granting access. While this might seem excessive, the organization's security depends on it. The alternative would be to forbid SSH access, and certainly you will be blamed if someone breaks into the organization's network this way.

12.5.19 Encrypted Mail Access

graphics/threedangerlevel.gif

A major question that needs to be answered is whether there should be a single mail server that accumulates e-mail and allows client systems to download e-mail via POP3 or IMAP. The other option is to allow e-mail to be delivered directly to users' own systems via SMTP. If the clients are Windows and Macs then POP3 and IMAP are popular and reasonably safe. Certainly, the Windows users will be vulnerable to the various e-mail viruses and worms regardless of how they receive e-mail. See "Using Sendmail to Block E-Mail Attacks" on page 393 for some help fighting known viruses and worms, though the real solution is disabling the execution of foreign code. Even if the clients are Linux and UNIX boxes, this is a safe and easy solution. Also, it protects even these clients against the vulnerabilities of sendmail. Certainly, POP3 and IMAP requests originating from outside of the organization should be blocked by the firewall unless wrapped in SSL or a similar secure protocol.

Many of the new mail readers can receive SSL-wrapped mail from POP3 or IMAP mailboxes. This would involve allowing the spop3 and simap services, sometimes called pop3s and imaps. These would be TCP ports 995 and 993, respectively. If the IP address of your mail server is 216.247.56.62 then the following IP Chains commands will allow access.

 
 $IPT -A FORWARD -i $EXTIF -p tcp -j ACCEPT -s 0.0.0.0/0 \    -d 216.247.56.62/32 995 $IPT -A FORWARD -i $EXTIF -p tcp -j ACCEPT -s 0.0.0.0/0 \    -d 216.247.56.62/32 993 

You may want to allow e-mail directly to some of your Linux and UNIX client boxes whose sendmail versions are kept up-to-date.

In either case, the firewall rules would block all incoming service requests to the Web server except http, https, and ssh. Similarly, the firewall would block all services to the external DNS server[20] and mail server except DNS and SMTP, respectively, and ssh.

[20] The external DNS server would provide DNS services to sites requesting it from outside of the organization. Typically, it recognizes only the public systems, such as the Web server, mail server, and the external DNS server. It also may provide information for systems where sendmail and ssh daemons are allowed by the firewall to accept requests from the Internet.


   
Top


Real World Linux Security Prentice Hall Ptr Open Source Technology Series
Real World Linux Security Prentice Hall Ptr Open Source Technology Series
ISBN: N/A
EAN: N/A
Year: 2002
Pages: 260

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