This article describes how to create a Linux firewall
from scratch using IP packet filtering built into the kernel.
For other notes on design and testing of firewalls in
general, refer to Security -
firewalls.
There are several IP packet filtering methods for Linux
- ipfw, ipchains and iptables. I used ipchains(8)
originally, as it was available for my version of Linux (kernel version
2.2). Subsequently I rewrote the firewall using iptables after
I had upgraded to kernel version 2.4. Also ipchains is similar
to, but newer than, ipfw.
I started by following various articles on ipchains
which covered the basics of the input, output and forwarding chains.
Each chain is a series of rules you create to determine what happens to
packets received by a network interface (input) sent out from a network
interface (output) or forwarded from one network interface to another
(eg. from an external dial-up connection using ppp0 to an internal LAN
using eth0). Packets which do not match any rule in the chain are
handled according to a policy (in my case, simply rejected).
I was optimistic that a firewall could be written by a
few simple commands to set the policy to reject input packets and allow
only those associated with local connections from the loopback device
(lo) and the PPP device (ppp0).
I dialed my ISP, then phoned a friend supplying the IP
address dynamically allocated (shown in /var/log/messages) with an
invitation to try to break into my Apache WWW server. Within a few
seconds he managed to connect. Time to try again.
With quite a lot of suggestions from my friend and
further study of ipchains, we put together a firewall which worked as
follows:
- Flush the input, output and forwarding chains.
- The policy for the input, output and forwarding
chains is set to DENY.
- Add rules to allow ICMP and traceroute packets.
- Add rules to input and output chains to accept
packets from/to the loopback device (lo).
- A port range for ephemeral ports is explicitly
created.
- Add rule to output chain to allow UDP packets from
the ppp+ device with source port in the ephemeral port range. Add rule
to input chain to allow UDP packets to the ppp+ device with destination
port in the ephemeral port range. As local servers do not listen on
ephemeral ports (only on "well-known" ports) this prevents external UDP
packets being sent to local servers (ppp+ means ppp0, ppp1, etc).
- Similarly add rules to allow TCP packets from/to the
ppp+ device. A neat trick here is to exclude "SYN" packets on input
using the "! -y" command. This prevents reply packets from attempting
malicious connections.
- Add rules to log packets which don't match any
previous rule as they are potentially malicious. The log file is
/var/log/messages
- It is easy to add rules to allow input/output packets
for a specific well-known destination port (eg. port 80 to allow
external connections to a WWW server).
- A cautionary note: the above rules do not prevent a
malicious local process (a trojan) from making connections to the
Internet. Even if we restrict connections to well-known ports, a trojan
could still connect to a malicious server.
- Finally the required commands are put in a script
rc.fwsetup which is placed in /etc/rc.d (for Mandrake/Red Hat Linux)
and sourced on system startup. The script includes comment lines which
are understood by chkconfig(8) so running chkconfig creates all
required lines in the /etc directories for each run level where we want
the firewall to be running.
|