Originally Published: Monday, 4 October 1999 Author: Quentin Cregan
Published to: enchance_articles_security/Advanced Security Articles Page: 1/1 - [Printable]

03 October - 09 October
Deploying a Linux Firewall
by Jason Tackaberry

The Internet is said to have connected somewhere around 50,000 networks, and over 200 million hosts. Unfortunately, some of these hosts are bound to have malicious users, or crackers. These days, a virtual firewall of sorts is constructed between the local network and the Internet to prevent the "fire" (the crackers) from spreading into the "building" (the local area network)...

   Page 1 of 1  


Many years ago, large, brick walls were erected between buildings and wood houses, so that if a fire broke out, it wouldn't spread. Naturally, these walls were called firewalls.

The Internet is said to have connected somewhere around 50,000 networks, and over 200 million hosts. Unfortunately, some of these hosts are bound to have malicious users, or crackers. These days, a virtual firewall of sorts is constructed between the local network and the Internet to prevent the "fire" (the crackers) from spreading into the "building" (the local area network).

The firewall is typically the first line of defense for any Internet-connected network. Unfortunately, many network administrators take firewalls for granted, and they become not only their first but their last line of defense. It can't be stressed enough that firewalls are not the be all and end all of network security. While a well-constructed firewall policy will greatly reduce your susceptibility to attack, it will not (and cannot) completely eliminate it.

What a firewall does and how it behaves depends on what level it operates on. (Those familiar with the OSI model will understand this.) Firewalls generally operate at the network layer (IP), or the application layer, such as HTTP proxies.

Those firewalls at the network layer are often called screening routers. A screening router examines the IP header on each incoming (and possibly outgoing) datagram and determines whether or not it should pass. It makes this determination by comparing key fields such as the source and destination addresses to the policy set by the administrator. Most screening routers will also examine the packet at the next layer (the transport layer), which allows you to create policies based on TCP or UDP port, or ICMP type and code.

Firewalls at the application layer are called gateways or proxies, and are designed to understand protocols at this level, such as HTTP or telnet. Application gateways are useful because they can offer very high level control over traffic, and so they are in some ways more secure than screening routers. For example, an application gateway may choose to filter all HTTP POST commands. Most importantly, gateways can maintain logging specific to application layer protocols. A paranoid (and privacy-ignorant) company may choose to have all mail pass through a gateway to log the To, From, and Subject fields of the header, for instance.

Network layer Firewalls

Although sometimes called screening routers, generally speaking the term firewall refers to a filter at the network layer. Linux has supported packet filtering at this level since version 1.3.x (ipfwadm). Kernel 2.2 has revamped this support and uses a user-space utility called ipchains. (Linux 2.4 is slated to have yet another redesign of the firewalling code.) Since 2.2 is the most recent, stable kernel, we will focus on ipchains.

In order to use ipchains, your kernel must be compiled with IP firewalling support (CONFIG_IP_FIREWALL).

Firewall Rules

The biggest and most complicated part of implementing a firewall is determining the best rule list for your network. The best policy is always the most restrictive, permitting only that which is absolutely required. These restrictions can be and usually are asymmetric. Outgoing traffic is rarely restricted as much as incoming traffic.

The first step in planning your rule list is identifying the requirements of the interaction between your internal network and the Internet. There are two strategies, depending on what your default policy is. The default policy defines how the firewall behaves if it encounters a packet that doesn't match any rule in the list. If your default policy is ACCEPT (allow the packets to pass through the firewall), then you will want to identify what services you do not want accessible from the Internet. If your default policy is DENY (block the packets from passing through the firewall), then you must choose which services you do want accessible from the outside world. Since the latter is the most restrictive, it is the best choice. (In fact, a default policy of ACCEPT provides very little security and is strongly discouraged.)

A typical, single-homed server may run a web service (HTTP), mail services (POP3 and SMTP), an FTP daemon, and perhaps secure shell (SSH) for remote administration. To allow these services to be accessible externally, there must be a rule for the ports used by each of these services. (For an extensive list of which ports are used for which services, see RFC 1700: Assigned Numbers.) For our example server, we'll need to enable ports 80 (HTTP), 110 (POP3), 25 (SMTP), 20 (FTP data), 21 (FTP control), and 22 (SSH). Using ipchains, a simple shell script might look like:

    for port in 80 110 25 20 21 22; do
      ipchains -A input -p TCP --dport $port -j ACCEPT
The command line arguments given to ipchains in this example are:

-A input: append this rule to the chain called "input". (Chains are further described below.) -p TCP: apply this rule to TCP packets --dport $port: match the rule if the destination port on the packet is $port -j ACCEPT: allow the packet to pass through the filter Some services are not listed in RFC 1700, such as NFS. NFS also relies on other services to function properly (portmap and mount in particular). Getting these services to work through a firewall may not be entirely obvious. The best strategy would be to run a network monitor (such as tcpdump) while you are using the service in question and examine which ports were used.

In particular, it is important that you pay close attention to who may access ports below 1024. This is because only root can bind to these ports, and so services that listen on ports under 1024 could potentially be running as root. Unfortunately, it isn't necessarily the case that any service bound to these ports is running as root. Furthermore, services bound to ports above 1024 may run as root as well. You should be aware of these details.

It is quite often the case that you may want to permit access to a service on your local network, while denying access from the Internet. By using the -s parameter, you can specify the source from which to accept the packets. Suppose your intranet mail server will offer IMAP only to your local network. If your LAN network is, you might call ipchains like:

    ipchains -A input -p TCP -s --dport 143 -i eth0 -j ACCEPT
The first parameter following the -s switch specifies the network IP followed by a mask. (The mask may be a full subnet mask or a single value used in CIDR.) The -i switch indicates to which interface this rule applies. It may not seem obvious why this switch is necessary; in fact, the rule will work fine without it. However, remember that the most restrictive rule is always the most secure. By limiting the interface on which the packet can be accepted, it prevents the possibility source-spoofed attacks on other interfaces.

You may find it useful to explicitely trust your local network. You can tell ipchains to do this with:

    ipchains -A input -s -i eth0 -j ACCEPT
Be aware, however, that you must fully trust your local network or this will be a great security risk. Consider if another Internet-accessible machine on your network is compromised: suddenly your firewall policy becomes useless against the intruder. Except in certain network configurations where the implications of this are completely understood, this practice isn't recommended. Don't be a lazy network administrator.

A secure firewall will also place restrictions on outgoing traffic so that only packets originating from within the local network are allowed through. This prevents users on your network from sending spoofed packets (which are almost always malicious in nature.) If your default outgoing policy is ACCEPT and the interface that is connected to the Internet is eth0, you can run:

    ipchains -A output -s ! -i eth0 -j DENY
The Chains in ipchains

You may be wondering where ipchains gets its name. Think of each rule in the list as a link on a chain. The first rule, or link, is consulted, and if there is no match, the next link is checked, and then the next, and so on, until the end of the chain is reached. If no rule matches, the default policy for that chain is applied to the packet.

ipchains has 3 internal chains: input, output, and forward. The input chain is consulted for all packets received on the interface; the output chain applies to those packets sent from the interface; and the forward chain is checked against packets that are destined for another machine. There can also be any number of user-defined chains, which offer no extra functionality but make managing large lists of rules much easier.

Odds and Ends

Earlier, two default policies were mentioned: ACCEPT and DENY. It should be clear what ACCEPT does, but DENY deserves some clarification. If the firewall is to DENY a packet (whether this be the default policy or an action specified by some rule), the packet is dropped by the firewall, and no further action is taken. This is the most paranoid action, but in some ways it also breaks the TCP protocol. TCP says that if a connection cannot be established for some reason, the router that discards the packet should send an ICMP message to the requesting host so notifying it. For this reason, ipchains provides another action called REJECT. REJECT also discards the packet, but it will send the appropriate ICMP message to the requesting host. This is perhaps the most courteous behaviour, but if you want your firewall to be as "stealthy" as possible, you'll want to use DENY.

ipchains also supports a few options that warrant mentioning. Firstly, most parameters can be negated. This is especially useful if your default policy is ACCEPT. In this case, suppose you want to deny everyone access to your SMTP server except your local network. You may execute:

    ipchains -A input -p TCP -s ! --dport ! 25 -i eth0 -j DENY
Another useful feature is wherever ipchains expects a port, you can specify a port range (denoted by first:last). Suppose, with a default policy of DENY, you may want to allow all packets in user space (that is, all ports between 1024 and 65536 inclusively):
    ipchains -A input -p TCP --dport 1024:65536 -j ACCEPT ipchains -A input -p UDP --dport 1024:65536 -j ACCEPT
Finally, ipchains also has the ability to filter packets with the SYN flag on. In TCP, all connections require a three-way handshake. The first stage in this handshake is for the requesting host to send a SYN (synchronize) packet. (That is, a packet with only the SYN flag on.) A very paranoid firewall which allows absolutely no connections from the outside world to pass into the local network may choose to filter all SYN packets. If we assume the IP of the server is, You can do this in ipchains with:
    ipchains -A input -p TCP -d ! -y -j DENY
This instructs ipchains to deny all SYN packets that are destined for any host other than the firewall. (This allows the firewall to accept incoming connections itself.) Note that while this increases the level of security, it will break certain protocols such as FTP (active mode transfers) or IRC (sending DCC requests). With FTP, you can deploy clients that support passive mode transfers, but other protocols may require a proxy (described below).

Application layer Gateways

Application layer gateways are services that run in user land (that is, outside the kernel) and act as a proxy between two points, one on the "outside," and the other on the "inside." For this reason, they are usually referred to as proxies.

Proxies require that the applications explicitely have support for them. If you are implementing a firewall in a large organization that currently doesn't have one, proxies may not be feasible because of the time it would take to reconfigure each machine. On the other hand, there are usually very convincing reasons to use a proxy. Since proxies work at the application layer, they must know the protocol. This means the proxy can take advantage of protocol-specific features. For example, an HTTP proxy will often cache data on the proxy's server. If every client uses the HTTP proxy, users will notice significant speed increases and network administrators will notice a significant reduction in incoming Internet traffic.

Proxies and packet filters will often coexist in the same environment. Administrators may choose to use a packet filter for very low-level control over incoming traffic and then use proxies to take advantage of logging and caching.


SOCKS is a general-purpose proxy, available on almost any platform. It is fairly popular, and many networked programs have support for it. SOCKS works transparently to the user; once the proper settings have been configured, the client application works with the SOCKS server in a seamless manner.

A free SOCKS implementation for Linux is available from NEC. Another, called Dante, is also available and is distributed under an Open Source license. Dante also has the ability of "SOCKSifying" client applications that do not directly support SOCKS.


Squid is perhaps the most popular HTTP proxy available for Unix. It is incredibly flexible, secure, and best of all, free. Squid is available here. Also, check our past articles on securing and setting up ACLs for Squid.

Network Address Translation

Because of the difficulty in obtaining class C addresses, many organizations are using the reserved, "non-routed" set of IPs. (This is described in further detail in our article on intranet routers, available here.) Because these IPs are not routable on the Internet, they can not be used to directly connect to machines outside their local network. Clients on the local network may either access services on the Internet via application-level proxies, or they may use a router that offers network address translation (NAT). Linux supports NAT in the kernel by way of IP masquerading.

How IP Masquerading Works

IP Masquerading has the advantage of being completely transparent for most common Internet tasks. The client sets its default router as the Linux system that is configured for IP masquerading. Then, when it sends a packet to a host on the Internet, the packet travels over the local network to the Linux router. The Linux router determines this packet is destined for the "outside world," and creates a virtual connection between the remote host and the host on the local network, if one doesn't yet exist. The Linux router allocates a port (usually above 60000) that acts as the glue between the host on the local network and the remote host. Subsequently, when the client sends a packet on the network, the router retags the source IP with the Internet-accessible IP of the router and the source port with the "glue" port mentioned above. When it receives packets from the Internet destined to this port, it then retags the destination IP and port and sends the packet to the host on the local network. From the local host's point of view, it is happily communicating with a host on the Internet. From the Internet host's point of view, it is talking to the Linux router, and knows nothing about any machine within the router's network.

Because the remote host can't know about any machine behind the firewall (the IP masquerading router), IP Masquerading also provides a very secure way of offering Internet connectivity to hosts on the local network. The IPs used on the local network are not routed on the Internet, and so it is impossible for a remote host to access a machine behind the firewall directly.

Setting up IP Masquerading

Your kernel must first be compiled with IP Masquerading support (CONFIG_IP_MASQUERADE). Also, IP forwarding must be enabled for each boot. You must execute the line:

    echo 1 > /proc/sys/net/ipv4/ip_forward
at boot time. Many distributions have customized scripts to handle this. You can enable this in RedHat, for instance, by editing /etc/sysconfig/network and setting FORWARD_IPV4 to true. If you're not sure how your distribution does it, executing the above line in the local system startup script will always work.

Setting up IP masquerading with ipchains is literally a one-liner. Assuming your LAN's network address is, you can execute:

    ipchains -A forward -j MASQ -s
This tells IP chains to "append on the forward chain a rule that takes any packet coming from and masquerades it."

IP Masquerading Modules

Because remote hosts on the Intenet can't directly access a host behind the firewall, certain functionality (such as active-mode FTP transfers or DCC chat/file requests) will be broken. These functions open a listening port on the host and require that the remote host be able to connect. In order for these to work, the IP masquerading router must first know about the listening port so that it can create the necessary virtual connection. (For the same reason, applications that wait for the remote host to initiate a UDP stream will also fail.)

This is solved using a somewhat klugy but clever solution by using kernel modules. These kernel modules (prefixed with ip_masq_) monitor outgoing packets from the host, and adjust protocol-specific values to reflect the virtual connection created by the IP masquerading router. For example, for active-mode FTP transfers, the client creates a listening socket and tells the remote FTP server where to connect. In an IP masqueraded environment, the client would tell the FTP server via a PORT command to connect to 192.168.0.X, to which the remote host obviously can't connect. By loading the ip_masq_ftp module, the Linux router will intercept this PORT command and adjust the values appropriately. Thus, by simply loading the ip_masq_ftp module (by loading insmod ip_masq_ftp), active-mode FTP transfers work transparently for the client.

There are kernel modules for several popular applications, including FTP, IRC, Quake, Real Audio, and CUSeeMe. (These modules can be found in your ipv4 modules directory, usually /lib/modules/<version>/ipv4.) There is one caveat, however: if your client is connecting to these services on non-standard ports (a port other than 6667 for IRC, or other than 21 for FTP, etc.), the modules will have to be told this before the connection is established. For example,

    insmod ip_masq_irc ports=6660,6665,6667,6670

Further Reading

Jason Tackaberry is a Unix/Network administrator and has several years of experience administrating a variety of operating systems, some of which he'd care not to mention. Please email him at tack@linux.com, in particular if you'd like to send him large amounts of money for no reason

   Page 1 of 1