The first section you should note within the example rc.firewall.txt is the
configuration section. This should always be changed since it contains the
information that is vital to your actual configuration. For example, your IP
address will always change, hence it is available here. The
$INET_IP should always be a fully valid IP address, if you
got one (if not, then you should probably look closer at the
rc.DHCP.firewall.txt,
however, read on since this script will introduce a lot of interesting stuff
anyways). Also, the $INET_IFACE variable should point to
the actual device used for your Internet connection. This could be
eth0, eth1,
ppp0, tr0, etc just to
name a few possible device names.
This script does not contain any special configuration options for DHCP or
PPPoE, hence these sections are empty. The same goes for all sections that are
empty, they are, however, left there so you can spot the differences between the
scripts in a more efficient way. If you need these parts, then you could always
create a mix of the different scripts, or (hold yourself) create your own from
scratch.
The Local Area Network section contains most of the
configuration options for your LAN, which are
necessary. For example, you need to specify the IP
address of the physical interface connected to the
LAN as well as the IP range
which the LAN uses and the interface that the box is
connected to the LAN through.
Also, as you may see there is a Localhost configuration section. We do
provide it, however you will with 99% certainty not change any of the values
within this section since you will almost always use the 127.0.0.1
IP address and the interface will almost certainly be
named lo. Also, just below the Localhost configuration,
you will find a brief section that pertains to the iptables. Mainly, this
section only consists of the $IPTABLES variable, which will
point the script to the exact location of the iptables
application. This may vary a bit, and the default location when compiling the
iptables package by hand is /usr/local/sbin/iptables.
However, many distributions put the actual application in another location such
as /usr/sbin/iptables and so on.
First, we see to it that the module dependencies files are up
to date by issuing a /sbin/depmod -a command. After
this we load the modules that we will require for this script. Always avoid
loading modules that you do not need, and if possible try to avoid having
modules lying around at all unless you will be using them. This is for security
reasons, since it will take some extra effort to make additional rules this
way. Now, for example, if you want to have support for the
LOG, REJECT and
MASQUERADE targets and don't have this compiled statically
into your kernel, we load these modules as follows:
In these scripts we forcedly load the modules, which could lead to
failures of loading the modules. If a module fails to load, it could depend
upon a lot of factors, and it will generate an error message. If some of the
more basic modules fail to load, its biggest probable error is that the module,
or functionality, is statically compiled into the kernel. For further
information on this subject, read the Problems loading modules section in the Common problems and questions appendix.
Next is the option to load ipt_owner
module, which could for example be used to only allow certain users to make
certain connections, etc. I will not use that module in this example but
basically, you could allow only root to do
FTP and HTTP connections to
redhat.com and DROP all the others. You could also disallow
all users but your own user and root to connect from your box to the Internet.
Might be boring for others, but you will be a bit more secure to bouncing
hacker attacks and attacks where the hacker will only use your host as an
intermediate host. For more information about the
ipt_owner match, look at the Owner match section within the
How a rule is built
chapter.
We may also load extra modules for the state matching code here. All modules
that extend the state matching code and connection tracking code are called
ip_conntrack_* and ip_nat_*.
Connection tracking helpers are special modules that tell the kernel how to
properly track the specific connections. Without these so called helpers, the
kernel would not know what to look for when it tries to track specific
connections. The NAT helpers on the other hand, are
extensions of the connection tracking helpers that tell the kernel what to look
for in specific packets and how to translate these so the connections will
actually work. For example, FTP is a complex protocol
by definition, and it sends connection information within the actual payload of
the packet. So, if one of your NATed boxes connect to
a FTP server on the Internet, it will send its own
local network IP address within the payload of the
packet, and tell the FTP server to connect to that
IP address. Since this local network address is not
valid outside your own network, the FTP server will not
know what to do with it and hence the connection will break down. The
FTP NAT helpers do all of the translations within these
connections so the FTP server will actually know where
to connect. The same thing applies for DCC file
transfers (sends) and chats. Creating these kind of connections requires the
IP address and ports to be sent within the
IRC protocol, which in turn requires some translation
to be done. Without these helpers, some FTP and
IRC stuff will work no doubt, however, some other
things will not work. For example, you may be able to receive files over
DCC, but not be able to send files. This is due to how
the DCC starts a connection. First off, you tell the
receiver that you want to send a file and where he should connect to. Without
the helpers, the DCC connection will look as if it
wants the receiver to connect to some host on the receiver's own local network.
In other words, the whole connection will be broken. However, the other way
around, it will work flawlessly since the sender will (most probably) give you
the correct address to connect to.
If you are experiencing problems with mIRC DCCs over your firewall
and everything works properly with other IRC clients, read the mIRC DCC problems section in the Common problems and questions appendix.
As of this writing, there is only the option to load modules which add support
for the FTP and IRC
protocols. For a long explanation of these conntrack and nat modules, read the
Common problems and questions
appendix. There are also H.323 conntrack helpers within
the patch-o-matic, as well as some other conntrack as
well as NAT helpers. To be able to use these helpers,
you need to use the patch-o-matic and compile your own
kernel. For a better explanation on how this is done, read the Preparations chapter.
Note that you need to load the ip_nat_irc and
ip_nat_ftp if you want Network Address
Translation to work properly on any of the
FTP and IRC protocols. You
will also need to load the ip_conntrack_irc and
ip_conntrack_ftp modules before actually loading the
NAT modules. They are used the same way as the
conntrack modules, but it will make it possible for the computer to do
NAT on these two protocols.
At this point we start the IP forwarding by
echoing a 1 to /proc/sys/net/ipv4/ip_forward in this
fashion:
echo "1" > /proc/sys/net/ipv4/ip_forward
It may be worth a thought where and when we turn on the
IP forwarding. In this script and all others
within the tutorial, we turn it on before actually creating any kind of
IP filters (i.e., iptables
rule-sets). This will lead to a brief period of time where the firewall
will accept forwarding of any kind of traffic for everything between a
millisecond to a minute depending on what script we are running and on
what box. This may give malicious people a small time-frame to actually get
through our firewall. In other words, this option should really be turned
on after creating all firewall rules, however, I have
chosen to turn it on before loading any rules to maintain consistency with
the script breakdown currently used in all scripts.
In case you need dynamic IP support, for example if
you use SLIP, PPP or
DHCP you may enable the next option,
ip_dynaddr by doing the following :
echo "1" > /proc/sys/net/ipv4/ip_dynaddr
If there is any other options you might need to turn on you should follow
that style. There's other documentation on how to do these things and this
is out of the scope of this documentation. There is a good but rather brief
document about the proc system available within the kernel, which is also
available within the Other resources and links appendix. The Other resources and links appendix is
generally a good place to start looking when you have specific areas that you
are looking for information on, that you do not find here.
The rc.firewall.txt script, and all other scripts
contained within this tutorial, do contain a small section of non-required proc
settings. These may be a good primer to look at when something is not working
exactly as you want it to, however, do not change these values before actually
knowing what they mean.
This section will briefly describe my choices within the tutorial
regarding user specified chains and some choices specific to the
rc.firewall.txt script. Some of the paths I have
chosen to go here may be wrong from one or another aspect. I hope to
point these aspects and possible problems out to you when and where they
occur. Also, this section contains a brief look back to the Traversing of tables and chains
chapter. Hopefully, this will remind you a little bit of how the specific
tables and chains are traversed in a real live example.
I have displaced all the different user-chains in the fashion I
have to save as much CPU as possible but at the same time put the main
weight on security and readability. Instead of letting a
TCP packet traverse
ICMP, UDP and
TCP rules, I simply match all
TCP packets and then let the
TCP packets traverse a user specified chain.
This way we do not get too much overhead out of it all. The following
picture will try to explain the basics of how an incoming packet traverses
Netfilter. With these pictures and explanations, I wish to explain and
clarify the goals of this script. We will not discuss any specific details
yet, but instead further on in the chapter. This is a really trivial
picture in comparison to the one in the Traversing of tables and chains chapter where we discussed the
whole traversal of chains and tables in depth.
Based upon this picture, let us make clear what our goals are. This whole
example script is based upon the assumption that we are looking at a scenario
containing one local network, one firewall and an Internet connection connected
to the firewall. This example is also based upon the assumption that we have a
static IP to the Internet (as opposed to
DHCP, PPP and
SLIP and others). In this case, we also want to allow
the firewall to act as a server for certain services on the Internet, and we
trust our local network fully and hence we will not block any of the traffic
from the local network. Also, this script has as a main priority to only allow
traffic that we explicitly want to allow. To do this, we want to set default
policies within the chains to DROP. This will
effectively kill all connections and all packets that we do not explicitly allow
inside our network or our firewall.
In the case of this scenario, we would also like to let our local network do
connections to the Internet. Since the local network is fully trusted, we want
to allow all kinds of traffic from the local network to the Internet. However,
the Internet is most definitely not a trusted network and hence we want to
block them from getting to our local network. Based upon these general
assumptions, let's look at what we need to do and what we do not need and want
to do.
First of all, we want the local network to be able to connect to the
Internet, of course. To do this, we will need to SNAT
all packets since none of the local computers have real IP addresses. All of
this is done within the POSTROUTING chain, which is
created last in this script. This means that we will also have to do some
filtering within the FORWARD chain since we will
otherwise allow outsiders full access to our local network. We trust our local
network to the fullest, and because of that we specifically allow all traffic
from our local network to the Internet. Since no one on the Internet should be
allowed to contact our local network computers, we will want to block all
traffic from the Internet to our local network except already established and
related connections, which in turn will allow all return traffic from the
Internet to our local network.
As for our firewall, we may be a bit low on funds perhaps, or we just want
to offer a few services to people on the Internet. Therefore, we have decided
to allow HTTP, FTP,
SSH and IDENTD access to the
actual firewall. All of these protocols are available on the actual firewall,
and hence it should be allowed through the INPUT chain,
and we need to allow the return traffic through the
OUTPUT chain. However, we also trust the local network
fully, and the loopback device and IP address are also
trusted. Because of this, we want to add special rules to allow all traffic from
the local network as well as the loopback network interface. Also, we do not
want to allow specific packets or packet headers in specific conjunctions, nor
do we want to allow some IP ranges to reach the firewall from the Internet. For
instance, the 10.0.0.0/8 address range is reserved for
local networks and hence we would normally not want to allow packets from such a
address range since they would with 90% certainty be spoofed. However, before we
implement this, we must note that certain Internet Service Providers actually
use these address ranges within their own networks. For a closer discussion of
this, read the Common problems and questions chapter.
Since we have an FTP server running on the server, as
well as the fact we want to traverse as few rules as possible, we add a rule
which lets all established and related traffic through at the top of the
INPUT chain. For the same reason, we want to split the
rules down into sub-chains. By doing this, our packets will hopefully only need
to traverse as few rules as possible. By traversing less rules, we make the
rule-set less time-consuming for each packet, and reduce latency within the
network.
In this script, we choose to split the different packets down by their
protocol family, for example TCP,
UDP or ICMP. All
TCP packets traverse a specific chain named
tcp_packets, which will contain rules for all
TCP ports and protocols that we want to allow. Also, we
want to do some extra checking on the TCP packets, so
we would like to create one more subchain for all packets that are accepted for
using valid port numbers to the firewall. This chain we choose to call the
allowed chain, and should contain a few extra checks
before finally accepting the packet. As for ICMP
packets, these will traverse the icmp_packets chain.
When we decided on how to create this chain, we could not see any specific needs
for extra checks before allowing the ICMP packets
through if we agree with the type and code of the ICMP
packet, and hence we accept them directly. Finally, we have the UDP packets which
need to be dealt with. These packets, we send to the
udp_packets chain which handles all incoming
UDP packets. All incoming UDP
packets should be sent to this chain, and if they are of an allowed type we
should accept them immediately without any further checking.
Since we are running on a relatively small network, this box is also used as a
secondary workstation and to give some extra leeway for this, we want to allow
certain specific protocols to make contact with the firewall itself, such as
speak freely and ICQ.
Finally, we have the firewalls OUTPUT chain. Since
we actually trust the firewall quite a lot, we allow pretty much all traffic
leaving the firewall. We do not do any specific user blocking, nor do we do any
blocking of specific protocols. However, we do not want people to use this box
to spoof packets leaving the firewall itself, and hence we only want to allow
traffic from the IP addresses assigned to the firewall itself. We would most
likely implement this by adding rules that ACCEPT all
packets leaving the firewall in case they come from one of the IP
addresses assigned to the firewall, and if not they will be dropped
by the default policy in the OUTPUT chain.
Quite early on in the process of creating our rule-set, we set up the default
policies. We set up the default policies on the different chains with a fairly
simple command, as described below.
iptables [-P {chain} {policy}]
The default policy is used every time the packets do not match a rule in the
chain. For example, let's say we get a packet that matches no single rule in our
whole rule-set. If this happens, we must decide what should happen to the packet
in question, and this is where the default policy comes into the picture. The
default policy is used on all packets that does not match with any other rule in
our rule-set.
Do be cautious with what default policy you set on chains in other
tables since they are simply not made for filtering, and it may lead to very
strange behaviors.
Now you have a good picture of what we want to accomplish with this firewall, so
let us get on to the actual implementation of the rule-set. It is now high time
that we take care of setting up all the rules and chains that we wish to create
and to use, as well as all of the rule-sets within the chains.
After this, we create the different special chains that we want to use
with the -N command. The new chains are created and set up
with no rules inside of them. The chains we will use are, as previously
described, icmp_packets,
tcp_packets,
udp_packets and the
allowed chain, which is used by the
tcp_packets chain. Incoming packets on
$INET_IFACE, of ICMP type, will be
redirected to the chain icmp_packets. Packets of
TCP type, will be redirected to the
tcp_packets chain and incoming packets of
UDP type from $INET_IFACE go to
udp_packets chain. All of this will be
explained more in detail in the INPUT chain section below. To create a chain is quite
simple and only consists of a short declaration of the chain as this:
iptables [-N chain]
In the upcoming sections we will have a closer look at each of the
user defined chains that we have by now created. Let us have a closer look at
how they look and what rules they contain and what we will accomplish within
them.
The bad_tcp_packets chain is devoted to contain
rules that inspect incoming packets for malformed headers or other problems. As
it is, we have only chosen to include a packet filter which blocks all incoming
TCP packets that are considered as
NEW but do not have the SYN bit
set, as well as a rule that blocks
SYN/ACK packets that are
considered NEW. This chain could be used to check
for all possible inconsistencies, such as above or XMAS
port-scans etc. We could also add rules that looks for state
INVALID.
If you want to fully understand the NEW not SYN, you need to look at the
State NEW packets but no SYN bit set section in the
Common problems and questions appendix
regarding state NEW and non-SYN packets getting through other rules. These
packets could be allowed under certain circumstances but in 99% of the cases we
wouldn't want these packets to get through. Hence, we log them to our logs and
then we DROP them.
The reason that we REJECT SYN/ACK packets that are
considered NEW is also very simple. It is described in more depth in the
SYN/ACK and NEW packets section in the
Common problems and questions appendix.
Basically, we do this out of courtesy to other hosts, since we will prevent them
from being attacked in a sequence number prediction attack.
If a packet comes in on $INET_IFACE and is of
TCP type, it travels through the
tcp_packets chain and if the connection is against a
port that we want to allow traffic on, we want to do some final
checks on it to see if we actually do want to allow it or not. All of these
final checks are done within the allowed chain.
First of all, we check if the packet is a SYN packet.
If it is a SYN packet, it is most likely to be the
first packet in a new connection so, of course, we allow this. Then we check if
the packet comes from an ESTABLISHED or
RELATED connection, if it does, then we, again of course,
allow it. An ESTABLISHED connection is a connection that has
seen traffic in both directions, and since we have seen a
SYN packet, the connection then must be in state
ESTABLISHED, according to the state machine. The last rule in
this chain will DROP everything else. In this case this
pretty much means everything that has not seen traffic in both directions,
i.e., we didn't reply to the SYN packet, or they are
trying to start the connection with a non SYN packet.
There is no practical use of not starting a connection
with a SYN packet, except to port scan people pretty
much. There is no currently available
TCP/IP implementation that
supports opening a TCP connection with something else
than a SYN packet to my knowledge, hence,
DROP it since it is 99% sure to be a port scan.
The tcp_packets chain specifies what ports are allowed
to use on the firewall from the Internet. There is, however, even more checks
to do, hence we send each and every one of the packets on to the allowed chain,
which we described previously.
-A tcp_packets tells iptables in which
chain to add the new rule, the rule will be added to the end of the chain.
-p TCP tells it to match TCP packets
and -s 0/0 matches all source addresses from 0.0.0.0 with
netmask 0.0.0.0, in other words all source addresses. This
is actually the default behavior but I am using it just to make everything as
clear as possible. --dport 21 means destination port
21, in other words if the packet is destined for port 21 they also match. If all
the criteria are matched, then the packet will be targeted for the
allowed chain. If it doesn't match any of the rules,
they will be passed back to the original chain that sent the packet to the
tcp_packets chain.
As it is now, I allow TCP port 21, or
FTP control port, which is used to control
FTP connections and later on I also allow all
RELATED connections, and that way we allow PASSIVE and ACTIVE
connections since the ip_conntrack_ftp module is,
hopefully, loaded. If we do not want to allow FTP at
all, we can unload the ip_conntrack_ftp module and
delete the $IPTABLES -A tcp_packets -p TCP -s 0/0 --dport
21 -j allowed line from the rc.firewall.txt file.
Port 22 is SSH, which is much better than allowing
telnet on port 23 if you want to allow anyone from the outside to use a shell on
your box at all. Note that you are dealing with a firewall. It is always a bad
idea to give others than yourself any kind of access to a firewall box.
Firewalls should always be kept to a bare minimum and no more.
Port 80 is HTTP, in other words your web server, delete
it if you do not want to run a web server directly on your firewall.
And finally we allow port 113, which is IDENTD and
might be necessary for some protocols like IRC, etc to work properly. Do note
that it may be worth it to use the oidentd package if you
NAT several hosts on your local network.
oidentd has support for relaying
IDENTD requests on to the correct boxes within your
local network.
If you feel like adding more open ports with this script, well, it should
be quite obvious how to do that by now. Just cut and paste one of the other
lines in the tcp_packets chain and change it to the
port you want to open.
If we do get a UDP packet on the
INPUT chain, we send them on to
udp_packets where we once again do a match for
the UDP protocol with -p UDP and
then match everything with a source address of 0.0.0.0 and netmask 0.0.0.0, in
other words everything again. Except this time, we only accept specific
UDP ports that we want to be open for hosts on the
Internet. Do note that we do not need to open up holes depending on the sending
hosts source port, since this should be taken care of by the state machine. We
only need to open up ports on our host if we are to run a server on any
UDP port, such as DNS etc.
Packets that are entering the firewall and that are part of an already
established connection (by our local network) will automatically be accepted
back in by the --state ESTABLISHED,RELATED rules at
the top of the INPUT chain.
As it is, we do not ACCEPT incoming
UDP packets from port 53, which is what we use to do
DNS lookups. The rule is there, but it is per
default commented out. If you want your firewall to act as a
DNS server, uncomment this line.
I personally also allow port 123, which is NTP or
network time protocol. This protocol is used to set
your computer clock to the same time as certain other time servers which have
very accurate clocks. Most of you probably do not
use this protocol and hence I am not allowing it per default. The same thing
applies here, however, the rule is there and it is simple to uncomment to get it
working.
We do not currently allow port 2074, which is used for certain real-time
multimedia applications like speak
freely which you can use to talk to other people in real-time by using
speakers and a microphone, or even better, a headset. If you would like to
use this, you could turn it on quite simply by removing the comment.
Port 4000 is the ICQ protocol. This should be an
extremely well known protocol that is used by the Mirabilis application named
ICQ. There are at least 2-3 different ICQ
clones for Linux and it is one of the most widely used chat programs in the
world. I doubt there is any further need to explain what it is.
At this point, two extra rules are available if you are experiencing a lot
of log entries due to different circumstances. The first rule will block
broadcast packets to destination ports 135 through 139. These are used by
NetBIOS, or SMB for most
Microsoft users. This will block all log entries we may get from iptables
logging Microsoft network activity on the outside of our firewall.
The second rule was also created to take care of excessive logging problems,
but instead takes care of DHCP queries from the
outside. This is specifically true if your outside network consists of a
non-switched Ethernet type of network, where the clients receive their
IP addresses by DHCP. During
these circumstances, you could wind up with a lot of logs from just that.
Do note that the last two rules are specifically opted out since some
people may be interested in these kind of logs. If you are experiencing
problems with excessive legit logging, try to drop these types of packages
at this point. There are also more rules of this type just before the log
rules in the INPUT chain.
This is where we decide what ICMP types to
allow. If a packet of ICMP type comes in on
eth0 on the INPUT chain, we then redirect it
to the icmp_packets chain as explained
before. Here we check what kind of ICMP types
to allow. For now, I only allow incoming ICMPEcho requests, TTL equals 0 during
transit and TTL equals 0 during
reassembly. The reason that we do not allow any other ICMP types
per default here, is that almost all other ICMP types should be covered by the
RELATED state rules.
If an ICMP packet is sent as a reply to an already existing packet or
packet stream it is considered RELATED to the original stream. For more
information on the states, read the The state machine chapter.
The reason that I allow these ICMP packets
is as follows, Echo Requests are used to request an echo reply, which in turn
is used to mainly ping other hosts to see if they are available on any of the
networks. Without this rule, other hosts will not be able to ping us to see if
we are available on any network connection. Do note that some people would tend
to erase this rule, since they simply do not want to be seen on the Internet.
Deleting this rule will effectively render any pings to our firewall totally
useless from the Internet since the firewall will simply not respond to them.
Time Exceeded (i.e., TTL equals 0 during transit and
TTL equals 0 during reassembly), is allowed in the
case we want to trace-route some host or if a packet gets its Time To Live set
to 0, we will get a reply about this. For example, when you trace-route
someone, you start out with TTL = 1, and it gets
down to 0 at the first hop on the way out, and a Time Exceeded is sent back
from the first gateway en route to the host we are trying to trace-route, then
TTL = 2 and the second gateway sends Time Exceeded,
and so on until we get an actual reply from the host we finally want to get
to. This way, we will get a reply from each host on our way to the actual host
we want to reach, and we can see every host in between and find out what host
is broken.
For a complete listing of all ICMP types, see the
ICMP types appendix . For more
information on ICMP types and their usage, i suggest
reading the following documents and reports:
As a side-note, I might be wrong in blocking some of these
ICMP types for you, but in my case, everything works
perfectly while blocking all the ICMP types that
I do not allow.
The INPUT chain, as I have written it, uses mostly
other chains to do the hard work. This way we do not get too much load from
iptables, and it will work much better on slow machines which might otherwise
drop packets at high loads. This is done by checking for specific details that
should be the same for a lot of different packets, and then sending those
packets into specific user specified chains. By doing this, we can split down
our rule-set to contain much less rules that need to be traversed by each
packet and hence the firewall will be put through a lot less overhead by packet
filtering.
First of all we do certain checks for bad packets. This is done by sending all
TCP packets to the
bad_tcp_packets chain. This chain contains a few rules
that will check for badly formed packets or other anomalies that we do not want
to accept. For a full explanation of the
bad_tcp_packets chain, take a look in the
The bad_tcp_packets chain section in
this chapter.
At this point we start looking for traffic from generally trusted networks.
These include the local network adapter and all traffic coming from
there, all traffic to and from our loopback interface,
including all our currently assigned IP addresses (this
means all of them, including our Internet IP address).
As it is, we have chosen to put the rule that allows
LAN activity to the firewall at the top, since our
local network generates more traffic than the Internet connection. This allows
for less overhead used to try and match each packet with each rule and it is
always a good idea to look through what kind of traffic mostly traverses the
firewall. By doing this, we can shuffle around the rules to be more efficient,
leading to less overhead on the firewall and less congestion on your network.
Before we start touching the "real" rules which decide what we allow from the
Internet interface and not, we have a related rule set up to reduce our
overhead. This is a state rule which allows all packets part of an already
ESTABLISHED or RELATED stream
to the Internet IP address. This rule has an equivalent
rule in the allowed chain, which are made rather redundant by this rule, which
will be evaluated before the allowed ones are. However,
the --state ESTABLISHED,RELATED rule in the allowed chain has
been retained for several reasons, such as people wanting to cut and paste the
function.
After this, we match all TCP packets in the
INPUT chain that comes in on the
$INET_IFACE interface, and send those to the
tcp_packets, which was previously described.
Now we do the same match for UDP packets on the
$INET_IFACE and send those to the
udp_packets chain, and after this all
ICMP packets are sent to the
icmp_packets chain. Normally, a firewall would be
hardest hit by TCP packets, than
UDP and last of them all ICMP
packets. This is in normal case, mind you, and it may be wrong for you. The
absolute same thing should be looked upon here, as with the network specific
rules. Which causes the most traffic? Should the rules be thrown around to
generate less overhead? On networks sending huge amounts of data, this
is an absolute necessity since a Pentium III
equivalent machine may be brought to its knees by a simple rule-set containing
100 rules and a single 100mbit Ethernet card running
at full capacity if the rule-set is badly written. This is an important piece
to look at when writing a rule-set for your own local network.
At this point we have one extra rule, that is per default opted out, that
can be used to get rid of some excessive logging in case we have some
Microsoft network on the outside of our Linux firewall. Microsoft clients
have a bad habit of sending out tons of multicast packets to the
224.0.0.0/8 range, and hence we have the opportunity to block those
packets here so we don't fill our logs with them. There are also two more
rules doing something similar to tasks in the
udp_packets chain described in the The UDP chain.
Before we hit the default policy of the INPUT
chain, we log it so we may be able to find out about possible problems and/or
bugs. Either it might be a packet that we just do not want to allow or it might
be someone who is doing something bad to us, or finally it might be a problem in
our firewall not allowing traffic that should be allowed. In either case we want
to know about it so it can be dealt with. Though, we do not log more than 3
packets per minute as we do not want to flood our logs with crap which in turn
may fill up our whole logging partition, also we set a prefix to all log entries
so we know where it came from.
Everything that has not yet been caught will be DROPed by
the default policy on the INPUT chain. The default
policy was set quite some time back, in the Setting up default policies section, in this chapter.
The FORWARD chain contains quite a few rules in this
scenario. We have a single rule which sends all packets to the
bad_tcp_packets chain, which was also used in the
INPUT chain as described previously. The
bad_tcp_packets chain is constructed in such a fashion
that it can be used recycled in several calling chains, regardless of what
packet traverses it.
After this first check for bad TCP packets, we
have the main rules in the FORWARD chain. The first
rule will allow all traffic from our $LAN_IFACE to any other
interface to flow freely, without restrictions. This rule will in other words
allow all traffic from our LAN to the Internet. The
second rule will allow ESTABLISHED and
RELATED traffic back through the firewall. This will in
other words allow packets belonging to connections that were initiated from our
internal network to flow freely back to our local network. These rules are
required for our local network to be able to access the Internet, since the
default policy of the FORWARD chain was previously set
to DROP. This is quite clever, since it will allow hosts on
our local network to connect to hosts on the Internet, but at the same time
block hosts on the Internet from connecting to the hosts on our internal network.
Finally we also have a logging rule which will log packets that are not
allowed in one or another way to pass through the
FORWARD chain. This will most likely show one or
another occurrence of a badly formed packet or other problem. One cause may be
hacker attacks, and others may be malformed packets. This is exactly the same
rule as the one used in the INPUT chain except for the
logging prefix, "IPT FORWARD packet died: ". The logging
prefix is mainly used to separate log entries, and may be used to distinguish
log entries to find out where the packet was logged from and some header
options.
Since I know that there is pretty much no one but me using this box which is
partially used as a Firewall and a workstation currently, I allow almost
everything that goes out from it that has a source address
$LOCALHOST_IP, $LAN_IP or
$STATIC_IP. Everything else might be spoofed in some fashion,
even though I doubt anyone that I know would do it on my box. Last of all we log
everything that gets dropped. If it does get dropped, we will most
definitely want to know about it so we may take action against the problem.
Either it is a nasty error, or it is a weird packet that is spoofed. Finally we
DROP the packet in the default policy.
The PREROUTING chain is pretty much what it says, it
does network address translation on packets before they actually hit the
routing decision that sends them onward to the INPUT
or FORWARD chains in the filter table. The only
reason that we talk about this chain in this script is that we once again feel
obliged to point out that you should not do any filtering in it. The
PREROUTING chain is only traversed by the first packet
in a stream, which means that all subsequent packets will go totally unchecked
in this chain. As it is with this script, we do not use the
PREROUTING chain at all, however, this is the place we
would be working in right now if we wanted to do DNAT
on any specific packets, for example if you want to host your web
server within your local network. For more information about the
PREROUTING chain, read the Traversing of tables and chains chapter.
The PREROUTING chain should not be used for any
filtering since, among other things, this chain is only traversed by the first
packet in a stream. The PREROUTING chain should be used
for network address translation only, unless you really know what you are
doing.
So, our final mission would be to get the Network
Address Translation up, correct? At least to me. First of all
we add a rule to the nat table, in the
POSTROUTING chain that will
NAT all packets going out on our interface
connected to the Internet. For me this would be
eth0. However, there are specific variables added
to all of the example scripts that may be used to automatically configure
these settings. The -t option tells
iptables which table to insert the rule in, in this
case the nat table. The -A command tells us that
we want to Append a new rule to an existing chain named
POSTROUTING and -o $INET_IFACE
tells us to match all outgoing packets on the
INET_IFACE interface (or eth0,
per default settings in this script) and finally we set the target to
SNAT the packets. So all packets that match this rule
will be SNAT'ed to look
as if they came from your Internet interface. Do note that you must set which
IP address to give outgoing packets with the
--to-source option sent to the SNAT target.
In this script we have chosen to use the SNAT
target instead of MASQUERADE for a couple of reasons.
The first one is that this script was supposed to run on a firewall that
has a static IP address. A follow up reason to
the first one, would hence be that it is faster and more efficient to use
the SNAT target if possible. Of course, it was
also used to show how it would work and how it would be used in a real
live example. If you do not have a static IP
address, you should definitely give thought to use the
MASQUERADE target instead which provides a simple and
easy facility that will also do NAT for you, but
that will automatically grab the IP address that
it should use. This takes a little bit extra computing power, but it may
most definitely be worth it if you use DHCP for
instance. If you would like to have a closer look at how the
MASQUERADE target may look, you should look at the
rc.DHCP.firewall.txt script.