Linux has long had the capability for filtering
packets, and it has come a long way since the early days in terms of both power
and flexibility. The first generation of packet-filtering code was called
ipfw (for "IP firewall") and provided basic filtering capability. Since
it was somewhat inflexible and inefficient for complex configurations, ipfw
is rarely used now. The second generation of IP filtering was called IP
chains. It improved greatly on ipfw and is still in common use. The
latest generation of filtering is called Netfilter and is manipulated with the
iptables command. It is used
exclusively with the 2.4.x and later series of kernels. Although Netfilter is the kernel component and
iptables is the user-space configuration tool, these terms are often used
interchangeably.
An important concept in Netfilter is the chain , which consists of a list of rules that
are applied to packets as they enter, leave, or traverse through the system. The
kernel defines three chains by default, but new chains of rules can be specified
and linked to the predefined chains. The INPUT chain applies to packets that are
received and are destined for the local system, and the OUTPUT chain applies to packets that are
transmitted by the local system. Finally, the FORWARD chain applies whenever a packet will be
routed from one network interface to another through the system. It is used
whenever the system is acting as a packet router or gateway, and applies to
packets that are neither originating from nor destined for this system.
The iptables command is used to make changes to
the Netfilter chains and rulesets. You can create new chains, delete chains,
list the rules in a chain, flush chains (that is, remove all rules from a
chain), and set the default action for a chain. iptables also allows
you to insert, append, delete, and replace rules in a chain.
Before we get started with some example rules, it's important
to set a default behavior for all the chains. To do this we'll use the -P
command-line switch, which stands for "policy":
# iptables -P INPUT DROP # iptables -P FORWARD DROP
This will ensure that only those packets covered by subsequent
rules that we specify will make it past our firewall. After all, with the
relatively small number of services that will be provided by the network, it is
far easier to explicitly specify all the types of traffic that we want to allow,
rather than all the traffic that we don't. Note that a default policy was not
specified for the OUTPUT chain; this is because we want to allow
traffic to proceed out of the firewall itself in a normal manner.
With the default policy set to DROP, we'll specify
what is actually allowed. Here's where we'll need to figure out what services
will have to be accessible to the outside world. For the rest of these examples,
we'll assume that eth0 is the external interface on our firewall and
that eth1 is the internal one. Our network will contain a web server
(192.168.1.20), a mail
server (192.168.1.21),
and a DNS server (192.168.1.18)—a fairly minimal setup for a self-managed
Internet presence.
# iptables -P INPUT -i lo -j ACCEPT # iptables -P OUTPUT -o lo -j ACCEPT
Now let's construct some rules to allow this traffic through.
First, we'll make a rule to allow traffic on TCP port 80—the standard port for
web servers—to pass to the web server unfettered by our firewall:
# iptables -A FORWARD -m state --state NEW -p tcp \ -d 192.168.1.20 --dport 80 -j ACCEPT
And now for the mail server, which uses TCP port 25 for
SMTP:
# iptables -A FORWARD -m state --state NEW -p tcp \ -d 192.168.1.21 --dport 25 -j ACCEPT
Additionally, we might want to allow remote POP3, IMAP, and
IMAP+SSL access as well:
- POP3
# iptables -A FORWARD -m state --state NEW -p tcp \ -d 192.168.1.21 --dport 110 -j ACCEPT
- IMAP
# iptables -A FORWARD -m state --state NEW -p tcp \ -d 192.168.1.21 --dport 143 -j ACCEPT
- IMAP+SSL
# iptables -A FORWARD -m state --state NEW -p tcp \ -d 192.168.1.21 --dport 993 -j ACCEPT
Unlike the other services, DNS can use both TCP and UDP port
53:
# iptables -A FORWARD -m state --state NEW -p tcp \ -d 192.168.1.21 --dport 53 -j ACCEPT
Since we're using a default deny policy, it makes it slightly
more difficult to use UDP for DNS. This
is because our policy relies on the use of state tracking rules, and since UDP
is a stateless protocol, there is no way to track it. In this case, we can
configure our DNS server either to use only TCP, or to use a UDP source port of
53 for any response that it sends back to clients that were using UDP to query
the nameserver.
If the DNS server is configured to respond to clients using UDP
port 53, we can allow this traffic through with the following two rules:
# iptables -A FORWARD -p udp -d 192.168.1.18 --dport 53 -j ACCEPT # iptables -A FORWARD -p udp -s 192.168.1.18 --sport 53 -j ACCEPT
The first rule allows traffic into our network destined for the
DNS server, and the second rule allows responses from the DNS server to leave
the network.
You may be wondering
what the -m state and --state arguments are about. These two
options allow us to use Netfilter's stateful packet-inspection engine. Using
these options tells Netfilter that we want to allow only new connections to the
destination IP and port pairs that we have specified. When these rules are in
place, the triggering packet is accepted and its information is entered into a
state table.
Now we can specify that we want to allow any outbound traffic
that is associated with these connections by adding a rule like this:
# iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
The only thing left now is to allow traffic from machines
behind the firewall to reach the outside world. To do this, we'll use a rule
like the following:
# iptables -A FORWARD -m state --state NEW -i eth1 -j ACCEPT
This rule enters any outbound connections from the internal
network into the state table. It works by matching packets coming into the
internal interface of our firewall that are creating new connections. If we were
setting up a firewall that had multiple internal interfaces, we could have used
a Boolean NOT operator on the external interface (e.g., -i !
eth0). Now any traffic that comes into the firewall through the external
interface that corresponds to an outbound connection will be accepted by the
preceding rule, because this rule will have put the corresponding connection
into the state table.
In these examples, the
order in which the rules were entered does not really matter. Since we're
operating with a default DENY policy, all our rules have an
ACCEPT target. However, if we had specified targets of DROP or
REJECT as arguments to the -j option, then we would have to
take a little extra care to ensure that the order of those rules would result in
the desired effect. Remember that the first rule that matches a packet is always
triggered as the rule chains are traversed, so rule order can sometimes be
critically important.
It should also be noted that rule order can have a performance
impact in some circumstances. For example, the rule shown earlier that matches
ESTABLISHED and RELATED states should be specified before any
of the other rules, since that particular rule will be matched far more often
than any of the rules that will match only on new connections. By putting that
rule first, it will prevent any packets that are already associated with a
connection from having to traverse the rest of the rule chain before finding a
match.
To complete our firewall configuration, we'll want to enable
packet forwarding. Run this command:
# echo 1 > /proc/sys/net/ipv4/ip_forward
This tells the kernel to forward packets between interfaces
whenever appropriate. To have this done automatically at boot time, add the
following line to /etc/sysctl.conf:
net.ipv4.ip_forward=1
If your system doesn't support /etc/sysctl.conf, you can
put the preceding echo command in one of your startup rc scripts, such
as /etc/rc.local. Another useful kernel parameter is rp_filter,
which helps prevent IP spoofing. This enables source address verification by
checking that the IP address for any given packet has arrived on the expected
network interface.
This can be enabled by running the following command:
# echo 1 > /proc/sys/net/ipv4/conf/default/rp_filter
Much like how we enabled IP forwarding, we can also enable
source address verification by editing /etc/sysctl.conf on systems that
support it, or else put the changes in your rc.local. To enable
rp_filter in your sysctl.conf, add the following line:
net.ipv4.conf.all.rp_filter=1
To save all of our
rules, we can either write all of our rules to a shell script or use our Linux
distribution's particular way of saving them.
We can do this in Red Hat by
running the following command:
# /sbin/service iptables save
This will save all currently active filter rules to
/etc/sysconfig/iptables. To achieve the same effect under Debian, edit
/etc/default/iptables and set enable_iptables_initd=true.
After doing this, run the following command:
# /etc/init.d/iptables save_active
When the machine reboots, your iptables configuration will be automatically
restored.
Happy Hacking!!
No comments:
Post a Comment