Learning iptables
from scratch.
By José Roberto Negreira a.k.a Xous
Version 2005-MAR-09
Disclaimer
This document is provided AS IS without any warranty. (By the way,
you'll hardly find free docs on internet where authors do provide
some warranty about things that may go wrong). The eventual reader
is responsible about his/her actions and consequences performed
after reading this document. I hope this disclaimer hasn't blamed
anyone, but it is really needed to avoid any kind of problems, just
because a reader decided to misconfigure, touch, break his enterprise
firewall following this article's instructions.
Revision History
2005-xxx-xx: published on iptableslinux.com
2005-MAR-08: 3 years after! translated to english
2002-MAR-02: Published on XousLAB (Thanks to myself :p)
2002-FEB-27: Fixed an error in a rule (Gracias Kremar)
2002-FEB-24: Published on PsicoFXP, security Forum
2002-FEB-16: Published on nnl (thanks Cyrano)
Intro
When we connect to the internet at home, we're connecting 'naked'
if the analogy is permitted. The goal of this document is, using
iptables, try to reach some kind of protection. Note that a firewall
doesn't guarantee 100% security (nor any product does!)
To read this, it's required basic/intermediate knowledge about linux
and TCP/IP (the linux implementation of TCP/IP obviously)
To use iptables, the ip_tables module must be loaded. This is default
on most linux distributions. If you are using ipchains, you will
need to unload its modules before loading the iptables ones.
iptables is a nice linux utility, used to send to the kernel some
directives. What kind of directives? You can command your kernel
about filtering TCP/IP packets. A TCP/IP packet consists of serveral
fields with additional information beside data itself. Describing
each of them goes beside this article. We'll use the more relevants,
the ones that will be useful for us and that our linux box will
start watching carefully. E.G.: source IP, destination IP, source
port, destination port, etc.
How does it work?
The linux kernel has 3 predefined (empty) chains of rules: INPUT,
FORWARD and OUTPUT. Each TCP/IP packet that enters/travels/leaves
from/to a computer with iptables working, will also traverse the
INPUT, FORWARD, or OUTPUT chain respectively. The tables, are just
a chunk of rules, which we will control each one of the passing
packets. Now you may wonder: what is a rule? Just to avoid confussion,
I'll try to simplify its definition as much as possible, and then
show you some examples. A rule consists of just 2 parts, a CONDITION
and an ACTION. If the packet matches the condition, the ACTION is
executed. Simple, really?
Take note for beyond: if a packet traversed all the chain rules
without match (or we could say, the packed 'survived' the chain),
iptables will check the packet on the chain's default policy
Kernel? forward? packets? chains? rules? I feel a bit dizzy...
Do not panic. To sum up:
1) the most important thing...you must have previous knowledge,
about linux and TCP/IP
2) The linux kernel has built-in 3 primary chains (INPUT, FORWARD
and OUTPUT)
3) With iptables, you can add/modify/delete rules (among other things)
This is a great moment to write on your prompt "man iptables",
if you haven't done it yet, to have a greater idea about this great
utility. Nevertheless, it's not a must to continue reading this
=)
Let's suppose the following scenario:
Once upon a time, there was a local network (192.168.1.0) with 50
workstations, and of those we'll distinguish these ones:
1) intruder.iptableslinux.lan (192.168.1.15) <--- an excesively
curious guy
2) neutral.iptableslinux.lan (192.168.1.18) <--- someone that
doesn't matter
3) jose.iptableslinux.lan (192.168.1.20) <-- our beloved linux
box
We are root on "jose.iptableslinux.lan", we're running
an ftp service, and we certainly know that the owner of "intruder",
enjoys investigating on other people's pc's using his network interface
card.
Experimenting with icmp packets
This is the real first approach with iptables. Let's see the current
state of our 3 chains, listing their contents. We'll execute this
command:
[root@jose /]# /sbin/iptables -nL
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
If the command output isn't like the above stuff, means than probably
you don't have installed the needed modules on the kernel, or that
there's still a working firewall, or to confuse you even more: this
means that a script has been executed (probably on system startup)
where the firewall rules are loaded. You should check the docs of
your current linux distribution.
Assuming that the command output is exactly the same as above, your
situation is the following:
All packets are accepted by the firewall, or in other words, nothing
at all is filtered. This indicates us 2 things:
1) the three chains (INPUT/FORWARD/OUTPUT) are empty.
2) the three chains have set "ACCEPT" action as the default
policy.
So, it is normal that if we ping localhost (ourselves), we'll get
an answer:
[root@jose /]# ping localhost
PING localhost.localdomain (127.0.0.1) from 127.0.0.1 : 56(84) bytes
of data.
64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=0 ttl=255
time=0.3 ms
64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=1 ttl=255
time=0.1 ms
64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=2 ttl=255
time=0.1 ms
(and so forth...)
Let's add a rule, to understand how things work:
[root@jose /]# /sbin/iptables --append INPUT --protocol icmp --source
127.0.0.1 --jump DROP
What was that? Simple: Append the following rule to the INPUT chain:
when an icmp type (--protocol icmp) packet which source is 127.0.0.1
(--source 127.0.0.1) and with any destination (it is not specified),
we'll ignore that packet, or just...drop that packet (--jump DROP).
What happend? nothing! Really? are you sure? Then let's check again
the rules on our chains (did you forget how to do that?)
[root@jose /]# /sbin/iptables -nL
Chain INPUT (policy ACCEPT)
target prot opt source destination
DROP icmp -- 127.0.0.1 0.0.0.0/0
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
We'll notice that on the INPUT chain, we've got a rule now, which
goal is avoid pinging ourselves. You'll also notice that this rule
isn't very useful itself, but it's a nice example to understand
how things work, as we said before. So? what's next? Checking that
things work, of course!!!
[root@jose /]# ping localhost
PING localhost.localdomain (127.0.0.1) from 127.0.0.1 : 56(84) bytes
of data.
(and it'll stay quietly waiting an answer that'll never arrives...)
Our firewall is invisible? Of course not! In fact, we could check
from "intruder" workstation, that ping has no problem
at all. Intruder pings, and our machine answers.
[root@intruder /]# ping jose.iptableslinux.lan
PING jose.iptableslinux.lan (192.168.1.20) from 192.168.1.15 : 56(84)
bytes of data.
64 bytes from 192.168.1.20: icmp_seq=0 ttl=255 time=2.524 msec
64 bytes from 192.168.1.20: icmp_seq=1 ttl=255 time=1.319 msec
So, using just ping, our friend from "intruder" can easily
notice our presence. Pay attention, this is just the beginning.
Filtering packets from outside
Our firewall is currently filtering the local ICMP packets, and
it allows the remote ones. Let's change the
game rules, or our firewall rules ;)
We'll delete the recently added rule, this can be done with the
"--delete", and the rest is the EXACT transcription of
the rule that we want to delete:
[root@jose /]# /sbin/iptables --delete INPUT --protocol icmp --source
127.0.0.1 --jump DROP
Note: knowing that the rule was the only one, we would get the
same effect with this command (instead of repeating the rule, we
put the rule number):
[root@jose /]# /sbin/iptables --delete INPUT 1
Now the stuff that matters: filter the icmp packets from network!!
[root@jose /]# /sbin/iptables -A INPUT -p icmp -s 192.168.1.15
-j DROP
Also note that writing -A is the same as --append, also -D or --delete,
-p o --protocol, an so on.
Now we'll list again our INPUT chain:
[root@jose /]# /sbin/iptables -nL INPUT
Chain INPUT (policy ACCEPT)
target prot opt source destination
DROP icmp -- 192.168.1.15 0.0.0.0/0
Great! now any icmp packet which source is 192.168.1.15 will be
dropped. That's why our curious friend tries to ping from his machine
to ours, he'll never get a response.
[root@intruder /]# ping jose.mired.lan
PING jose.mired.lan (192.168.1.20) from 192.168.1.15 : 56(84) bytes
of data.
Fantastic! Our linux box is invisible to our friend, we've done
it. Our machine is safe, isn't it?
No! NO NO NO!!
Our friend has turned even more curious because our machine don't
reply ping, thought that our PC was turned off, but after that he
wrote:
[root@intruder /]# ftp jose.mired.lan
Connected to jose.mired.lan.
220 jose FTP server (Version wu-2.6.1-16) ready.
Name (jose:root):
Oh oh! We've forgotten our FTP server running...then in a hurry
we add the following rule:
[root@jose /]# /sbin/iptables -A INPUT -p tcp -s 192.168.1.15 --dport
21 -j DROP
We list once again the INPUT chain
[root@jose /]# /sbin/iptables -nL INPUT
Chain INPUT (policy ACCEPT)
target prot opt source destination
DROP icmp -- 192.168.1.15 0.0.0.0/0
DROP tcp -- 192.168.1.15 0.0.0.0/0 tcp dpt:21
Our friend will get bored waiting any response from our ftp (but
will never happen). Interesting, isn't it?
Now: let's think a bit: what happens if on most of our 50 PC operators,
are "curious friends". Should we add a rule for each one
of them? It would be a tedious work, and hardly efficient.
Beside that, a really important detail: even though all these rules
could be applied without any trouble and are sintacticall correct,
a _CONSIDERABLE_ suggestion is follow the "all that is not
explicitly permited, then is prohibited" philosophy.
How can we achieve this? The first step, is set the default policy
on "DROP". Any packet that doesn't match any rule will
be dropped. We change our way to think, now each packet survives
the chain if it matches a rule.
In case that you are accesing locally to your linux box, just ignore
the following paragraph:
IMPORTANT NOTICE: for those that are accessing your box remotely
(via telnet or ssh), if you set your INPUT policy as "DROP",
you'll suffer a disconnection. This actually happened to me. That's
why you should previously add the following rule:
[root@jose /]# /sbin/iptables -A INPUT -p tcp -s #SOURCE# --dport
#PORT# -j DROP
Where #SOURCE# is the IP Address from where you're connecting to
the linux box, and #PORT# should be 22 (if you connect via ssh),
or 23 (if it's telnet).
Now, as we said, we'll continue setting the INPUT policy on "DROP",
(with the -P option)
[root@jose /]# /sbin/iptables -P INPUT -j DROP
We'll empty all chains with the -F (Flush) Option.
[root@jose /]# /sbin/iptables -F
We list the rules of our INPUT chain
[root@jose /root]# /sbin/iptables -nL INPUT
Chain INPUT (policy DROP)
target prot opt source destination
Even though we don't have any rule, nobody will be able to access
to our PC. Any packet tha arrives, is exposed to each rule on the
INPUT chain (in our case, it's empty!), as no match was produced,
the chain policy decides the packet destiny, and it is: "DROP".
The current problem is that even though we can make outgoing connections,
all responses will be dropped. To solve this, we'll add the following
rule:
[root@jose /root]# /sbin/iptables -A INPUT -m state --state ESTABLISHED,RELATED
-j ACCEPT
What does it means? we allow any packet which connection was already
established, or which connection is new, but is related to an established
connection. This is what iptables man pages say (I told you to read
them, didn't I?)
Again, yes...again we list our INPUT chain:
[root@jose /root]# /sbin/iptables -nL INPUT
Chain INPUT (policy DROP)
target prot opt source destination
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
Nobody will be able to initiate a connection to our machine, unless
we specify it. We have a DROP by default. Now we can start allowing
connections.
Local access:
[root@jose /root]# /sbin/iptables -A INPUT -i lo -s 127.0.0.1 -j
ACCEPT
-i lo : we specify the packets coming from the localhost interface.
[root@jose /root]# /sbin/iptables -nL INPUT
Chain INPUT (policy DROP)
target prot opt source destination
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
ACCEPT all -- 127.0.0.1 0.0.0.0/0
We allow to "neutral" ftp access (port 21)
[root@jose /root]# /sbin/iptables -A INPUT -p tcp -s 192.168.1.18
--dport 21 -j ACCEPT
[root@jose /root]# /sbin/iptables -nL INPUT
Chain INPUT (policy DROP)
target prot opt source destination
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
ACCEPT all -- 127.0.0.1 0.0.0.0/0
ACCEPT tcp -- 192.168.1.18 0.0.0.0/0 tcp dpt:21
Useful advices and final comments
=================================
As long as you start learning how to use iptables, you'll start
allowing (or not) the access to your PC, network, services, matching
by source or destination port, source or destination host (or network),
etc. It's just a minimal part of the whole you can do with iptables.
Don't turn off your linux box. All changes to the iptables chains
on-the-fly are NOT saved in any files, just on the RAM, and as you
may know, when you reboot, you lose changes.
1) Store all your rules in a tidy script, with comments explaining
each applied rule or each group of rules.
2) if you consider running the script automatically after booting,
you should start it before starting any service, or even better:
before starting up any network interface. Don't worry about rules
that reference interfaces not yet started, they're still accepted
by iptables.
3) Keep on reading iptables man pages. Notice that it is *NOT*
an usual RTFM advice, because the iptables man pages are a really
complete, excellent source of information, well and of course, the
infinite documentation that you'll find on the net.
As the last words, I hope that you've enjoyed reading this doc,
as much as I've enjoyed writing, and translating them. :)
I would be glad to receive feedback (no flames please!), just to
know if this document has been useful, and of course, any advice,
corrections, etc.
feedback at iptableslinux.com
Look for the lastest version of this document at:
http://www.iptableslinux.com
NOTE
You must notice that FTP protocol is atypical, or at least, different
to other protocols (TELNET, SSH, etc). It has 2 connection modes:
active and passive. The "active" mode is supported by
any FTP client, opens 2 connections. Beside the one from the client
to the server, the server opens an additional connection to the
client (from and to any port, unless the server allows to specify
some range). On the other hand, on passive mode, both connections
are opened from the client and the server (ports 21 and 20 by default).
You should consider this when you, being the client and behind a
firewall in a "protected" network, need to connect to
an FTP server outside the firewall.
|