Configuring a Firewall with iptables
The first iptables application described in this chapter is packet-filter firewall configuration. You can use these options on either a router or a non-router system (a workstation or server). When a router is so configured, it's often referred to as a firewall computer, but there are other types of firewall as well. Configuring a packet-filter firewall requires setting a default policy and then configuring exceptions to that policy based on port numbers , IP addresses, or other criteria.
What Is a Firewall?
This section is devoted to a different type of firewall: packet-filter firewalls. These operate on a lower level of the TCP/IP stack, and are restricted to examining various types of addressing information in individual packets, or at most to tracking a transaction to ensure it's not hijacked. Although packet-filter firewalls are often implemented as part of a router, they can also be set up on a workstation or server. When so configured, the firewall works to protect the computer on which it runs, but has no effect on other computers.
Most people think of firewalls as existing to protect a local network from the Internet, as shown in Figure 25.2. Indeed, this is one of the functions of a firewall, and a very important one. Firewalls can also serve to prevent local users from attacking other systems on the Internet, though. You might use a firewall to block all but approved local protocols, and perhaps even restrict certain protocols except from particular computers. For instance, you might allow only the mail server to connect to port 25 on outside computers. (ISPs sometimes use this configuration as an anti-spam measure.) Outgoing controls provide protection against both untrustworthy individuals who have access to your local network and viruses, worms, Trojan horses and the like installed on your local computers without your knowledge. This protection most directly helps other networks, but it's ultimately important to you as well, because blocking hostile actions from your own network can keep your network from being the target of investigations by your ISP or even legal action.
Figure 25.2. Packet filter firewalls can limit some types of access while allowing others.
In some cases, you might use a firewall's rules to redirect connection attempts, as described in the upcoming section, "Redirecting Ports with iptables ." This redirection causes a packet directed at one system to be sent to another. You can use this feature in conjunction with NAT to run servers on a protected network. You might also use it with external-connection restrictions to allow a connection to succeed in a way the client doesn't expect. For instance, instead of blocking outgoing SMTP connections from anything but your mail server, you might redirect such requests to your own mail server. In the case of SMTP, the result is normal mail delivery, provided the mail server is configured to relay local mail. This particular trick won't work with most other protocols, though.
Referring to Figure 25.1, a Linux packet-filter firewall operates by configuring the INPUT , FORWARD , and OUTPUT chains. Each of these chains has its role:
A router with firewall features is most likely to use INPUT and FORWARD rules, while a workstation or server is most likely to use INPUT and OUTPUT rules. In some cases, these rules may have nearly identical effects, particularly with respect to FORWARD and OUTPUT rules on a router. The OUTPUT chain affects both forwarded and locally generated traffic, though, whereas the FORWARD chain affects only routed traffic.
Setting a Firewall's Default Policy
The first step in firewall configuration is in setting the default policy. This is a statement of what the firewall is to do with packets that don't match other rules. You set the default policy with the -P option to iptables , thus:
# iptables -P INPUT DROP # iptables -P OUTPUT DROP # iptables -P FORWARD DROP
This example sets the default policy separately on each of the three standard chains in the filter table. The default policy may be any firewall target, as described earlier ( ACCEPT , DROP , QUEUE , RETURN , or another policy enabled through an appropriate kernel option). The most common default policies are ACCEPT , DROP , and REJECT . The first is the default, and tells Linux to pass on all packets. DROP tells the system to ignore all packets; it's as if they don't exist. REJECT is like DROP , but instead of ignoring the packet, Linux tells the calling system that it can't accept the packet; it's similar to the way Linux responds if there's no process using the port. The most secure firewall configurations use DROP or REJECT as the default policy, because this means that any packet type you don't explicitly permit in subsequent steps is blocked. If you use ACCEPT as the default policy, you must remember to explicitly disallow all the packet types you don't want to let pass. This can be a tedious process, and it's easy to overlook something. With a default policy of DROP or REJECT , by contrast, you only need to explicitly allow certain protocol types. Most computers and even networks only use a few protocols, so this is generally a more manageable task.
Creating Firewall Rules
You create firewall rules by using the --append option to iptables ( -A is a shorter synonym). You follow this command by one or more criteria specifications and a --jump option ( -j being a shorter synonym), which specifies a firewall target such as ACCEPT , DROP , or REJECT . In sum, the command looks something like this:
# iptables --append CHAIN selection-criteria --jump TARGET
This can be shortened by using the shorthand option names :
# iptables - A CHAIN selection-criteria -j TARGET
There are other options you can use in place of --append :
There are several additional options beyond these; consult the iptables man page for details. The following sections describe various options for the selection-criteria you may pass to iptables . You can include multiple criteria in one command; for instance, you can restrict access based on port number and IP address.
Opening and Closing Specific Ports
One way to filter packets is to use specific source or destination ports. For instance, you might want to configure a mail server to pass packets sent to its SMTP port (25). You can do this by using the --destination-port ( --dport ) option. This option also relies upon the --protocol ( -p ) option, which sets the protocol type ( tcp , udp , icmp , or all for all of them). The --source-port ( --sport ) option works in a similar way, but matches the port from which the packet originated. The final command might resemble the following:
# iptables -A INPUT -p tcp --dport 25 -j ACCEPT # iptables -A OUTPUT -p tcp --sport 25 -j ACCEPT
These commands tell the kernel to accept packets directed at port 25 for its local INPUT and OUTPUT chains. The result is that, even if the default policy is set to block packets, the system will accept incoming mail and be able to send mail to others, assuming a mail server is configured appropriately. One key point is that, if you've set the default policy to DROP or REJECT , you must normally open both the input chain to the port for a particular server and the output chain from the same server's port. If you fail to do this, your server may be able to receive packets but not reply to them, or generate packets but not receive them. On a separate firewall computer, you might want to create --destination-port and --source-port rules on the FORWARD chain, to permit SMTP transfers to pass through the firewall. You can combine these with an IP address specification, as described in the next section, "Using Source and Destination IP Addresses," to allow SMTP transfers only to and from the mail server.
If you use a default DROP or REJECT policy, it's important to open ports to allow client programs to communicate with the outside world. There are a couple of ways you can do this:
Using Source and Destination IP Addresses
You can match packets to source and destination IP addresses or network blocks with the --source ( -s ) and --destination ( -d ) options. For instance, if you know that the 172.24.0.0/16 network block is controlled by undesirables, you could include options like the following to block all access to and from that network block in a default ALLOW configuration:
# iptables -A INPUT -s 172.24.0.0/16 -j DROP # iptables -A OUTPUT -d 172.24.0.0/16 -j DROP
These options are often used in conjunction with port-based connections to allow only specific computers to access certain ports, especially in a default DROP configuration. For instance, suppose you run a firewall computer that protects a network, but you want to allow external users on the 10.34.176.0/24 network to access your internal network's SSH servers (on TCP port 22). You might do this with options like the following:
# iptables -A FORWARD -s 10.34.176.0/24 -p tcp \ --destination-port 22 -j ALLOW # iptables -A FORWARD -d 10.34.176.0/24 -p tcp \ --source-port 22 -j ALLOW
Because this example modifies only the FORWARD chain, it does not give any user direct access to the firewall computer's own SSH server (if it's even running one). You might want to add entries to give your local computers access to this server. Suppose your local network uses the 192.168.9.0/24 network addresses. The appropriate commands would resemble the following:
# iptables -A INPUT -s 192.168.9.0/24 -p tcp \ --destination-port 22 -j ALLOW # iptables -A OUTPUT -d 192.168.9.0/24 -p tcp \ --source-port 22 -j ALLOW
Filtering by Interface
Another filtering option is to use the network interface, such as ppp0 or eth1 , as an identifier. This option is most useful on a router or other computer with multiple interfaces. It can help protect against address spoofing, in which a remote system tries to contact a router using a local address, in the hopes that the router has a more lenient configuration for local computers. For the INPUT and FORWARD chains, you specify the input interface name with the --in-interface ( -i ) option; for the FORWARD and OUTPUT chains, you specify the output interface with the --out-interface ( -o ) option. For instance, suppose your internal network uses the 192.168.9.0/24 network address space and is on the router's eth1 , while your Internet connection is on eth0 . You might use a generic anti-spoofing rule like the following:
# iptables -A INPUT -s 192.168.9.0/24 -i eth0 -j DROP # iptables -A FORWARD -s 192.168.9.0/24 -i eth0 -j DROP # iptables -A FORWARD -s !192.168.9.0/24 -i eth1 -j DROP # iptables -A OUTPUT -s !192.168.9.0/24 -i eth1 -j DROP
The first two commands protect against packets directed at the router and internal systems from external systems (on eth0 ) that claim to be internal systems. The last two commands prevent internal systems (on eth1 ) from claiming to have IP addresses outside of the local network.
Performing Stateful Inspection
The latest addition to the Linux packet filtering firewall capabilities is stateful packet inspection. The options that have been described earlier in this chapter all operate on individual packets. There's no way to tell if a packet is part of an existing data stream or is an interloper inserted into an existing data stream by a clever attacker. (The --syn option allows you to check if a packet claims to be opening a new connection, but it's possible to have a computer send out packets that claim to be part of an existing data stream. Such packets can be used to " hijack " an existing TCP connection.) With stateful packet inspection, the kernel can track connections by using packet sequence numbers, source IP addresses, and so on. If you tell it to do so, you can have the kernel reject packets that contain errors that might indicate the session has been hijacked.
Stateful packet inspection requires using the --state option, preceded by the -m state option to load the stateful inspection capabilities of the kernel. You can pass this option one or more of several parameters. If you pass more than one parameter, you must separate them with commas. You can use an exclamation mark ( ! ) prior to the --state option to reverse its meaning. The available parameters are:
You might want to add stateful inspection to an existing rule. For instance, suppose you have a default DROP or REJECT policy on a server, but have a rule to permit connections to and from the server's HTTP port (80). You might add a stateful inspection rule to allow only new, established, or related packets to that port:
# iptables -A INPUT -m state -p tcp --dport 80 \ --state NEW,ESTABLISHED,RELATED -j ACCEPT # iptables -A OUTPUT -m state -p tcp --sport 80 \ --state ESTABLISHED,RELATED -j ACCEPT
This rule permits only new, established, or related incoming connections, and only established or related outgoing connections. (New outgoing connections aren't required because the connection is always created by the client, not the server whose port these rules protect.) These rules prevent an interloper from taking over a client's existing Web server connection.
Using Additional Parameters
The iptables tool supports many additional options that may be useful in packet filter firewalls. For instance, you can create entirely new chains with the --new-chain ( -N ) option, create a rule that applies only to the second and subsequent fragments of a fragmented packet with the --fragment ( -f ) parameter, or match specific TCP flags with the --tcp-flags option. For more information on such parameters, consult the iptables man page or a book on Linux packet filter firewalls. (Be sure to get a title that covers iptables ; some older titles cover only ipchains .)
Putting It All Together
It may be useful to see an entire iptables packet filter firewall script. Listing 25.1 presents such a script. This script is designed for a Web server computer that also supports local connections using SSH for configuring the system.
Listing 25.1 A sample iptables firewall script
#!/bin/sh iptables -F INPUT iptables -F OUTPUT iptables -F FORWARD iptables -P INPUT DROP iptables -P OUTPUT DROP iptables -P FORWARD DROP # Permit DNS traffic iptables -A INPUT -p udp --sport 53 -j ACCEPT iptables -A OUTPUT -p udp --dport 53 -j ACCEPT # Accept local-network return traffic for clients iptables -A INPUT -m state -p tcp --dport 1024:65535 \ --state ESTABLISHED,RELATED -s 192.168.9.0/24 -j ACCEPT iptables -A OUTPUT -m state -p tcp --sport 1024:65535 \ ! --state INVALID -d 192.168.9.0/24 -j ACCEPT # Accept all HTTP connections iptables -A INPUT -m state -p tcp --dport 80 \ ! --state INVALID -j ACCEPT iptables -A OUTPUT -m state -p tcp --sport 80 \ --state ESTABLISHED,RELATED -j ACCEPT # Accept local (192.168.9.0/24) SSH traffic iptables -A INPUT -m state -p tcp --dport 22 \ ! --state INVALID -s 192.168.9.0/24 -j ACCEPT iptables -A OUTPUT -m state -p tcp --sport 22 \ --state ESTABLISHED,RELATED -d 192.168.9.0/24 -j ACCEPT # Accept all local traffic on the lo interface iptables -A INPUT -s 127.0.0.1 -i lo -j ACCEPT iptables -A OUTPUT -d 127.0.0.1 -o lo -j ACCEPT
Some key features to consider about Listing 25.1 are the following: