Folks,
Adam's mail regarding which front-end to use for a firewall raises a topic
which I'm sure we will see again and again on the list, especially as DSL
becomes more widespread in Cork.
So, what options are available to us Linux junkies? Well, there are many
many front ends to the Linux firewalling system. Really all most of these
do is try and make "iptables" (the front end to the Kernel's "netfilter")
a little more friendly. Unfortunately its a slight catch 22 situation
though - the more simple the interface usually is, the more functionality
you tend to loose[1].
In light of this, I discuss below the basics of setting up your own
firewall on a gateway using iptables its self. It is by no means as
advanced as some of us would use or as technically accurate as it could
be, but it is a definite starting point, explaining in simple English what
iptables means and does. It is also my little way of demonstrating that
iptables is not the scary monster that some people[2] may tell you it is.
In fact, it is a lot easier to use than its predecessors ipchains and
ipfwadm[3], with one reason being that it is a stateful firewall (i.e. it
can track connections properly). This makes rules a lot easier to write
for services which you wish to allow.
This is of course intended as a starting point and comes with no
guarantees etcetera etcetera waffle waffle. It does however come with a
desire to see this expanded on.
So, first thing we want to do: Enable source address verification in the
kernel. This goes a long way toward protecting you from spoofed packets,
smurf attacks and other such fun things. So add the following line...
net.ipv4.conf.default.rp_filter = 1
...in to /etc/sysctl.conf. This will make the change permanent and active
after you reboot. This can be done on a boot by boot basis by echoing
directly in to the underlying subsystem - but this much is easier.
Now we want to enable forwarding, permanently. So in the same file as
before, /etc/sysctl.conf, add the following line...
net.ipv4.ip_forward = 1
...you can save and close the file for now. We will add more to it later.
Lets start by clearing any rules that might be in memory already...
# iptables -F
...and setting some defaults we will come back to later...
# iptables --policy INPUT DROP
# iptables --policy OUTPUT ACCEPT
# iptables --policy FORWARD ACCEPT
Now to the firewall itself. Being paranoid bunnies, by default we drop all
traffic. It is important to note that I said "drop" and not reject. When
you drop a packet, it literally does just that. No message or
acknowledgment of any kind is sent back to the originator. Where as a
reject will send back an error message to the originator. Now, while it
might seem a little rude not to reply, if someone is flooding you with
lots of traffic, you can use a lot of precious bandwidth by sending back
errors. Better just to ignore it all. Also, this is part of being
invisible to the world (so to speak). If someone is scanning your IP
address, which does not reply in any way shape or form, people will
generally assume there is simply nothing there. Thus not drawing any
unwanted attention to one's self. So, in light of this, lets start by
dropping ALL incoming traffic...
# iptables -A INPUT -i eth0 -j DROP
...this discussion assumes that "eth0" is your connection to the Internet
or outside world and that "eth1" is your internal facing interface. The
internal network is assumed as being 192.168.1.0/24 a.k.a.
192.168.1.0/255.255.255.0 a.k.a 192.168.1.0 -> 192.168.1.255.
For the sake of paranoia, lets drop all traffic from what should be
loopback addresses on all interfaces except local...
# iptables -A INPUT -s 127.0.0.0/255.0.0.0 -i ! lo -j DROP
...and while we're at it, lets drop ICMP traffic too, this means people
won't be able to ping you (plus a little bit more)...
# iptables -A INPUT -i eth0 -p icmp -j DROP
At this stage, it should be becoming clear what the various flags mean.
"-A" specifies the chain we're using. For the most part, we will deal with
the INPUT chain, which is built in to iptables by default and deals with
incoming traffic. "-i" specifies the incoming interface we're talking
about. This could be anything - eth0 as above, or ppp0 if its a dialup or
PPP based connection etcetera. "-p" specifies the protocol. You don't
always have to specify this. But when you do it can be tcp, udp, icmp and
so on. "-j" for all intents and purposes means "do what comes after me",
i.e. the action comes next. The action we will mainly be dealing with is
DROP. But it could be accept, log, reject - the list goes on. See the
iptables man page for a full list.
As it stands now, traffic from the Internet cannot get in to your network.
Unfortunately at the moment, this also means that your requested
information cannot get back in to you. i.e. if you try and check your
mail, it can't get in. For this to work, we must enable whats called
connection tracking, which put simply, watches any requests you make to
the Internet (like for mail or a web page) and allows the replies back in
to you. Thus greatly enhancing your Internet experience :o) This is done
by the following...
# iptables -A INPUT -m state --state ESTABLISHED -j ACCEPT
...this is fairly self explanatory. Now is also a good time to do some
protection from the common tactics of a creature known as "El Script
Kiddy"...
# iptables -A INPUT -i eth0 -p tcp -m tcp ! --tcp-flags SYN,RST,ACK SYN -j
ACCEPT
...the use of "!" is quite interesting and allows you a lot of
flexibility. For all intents and purposes it means "the opposite of what
comes after me". So in the case above its almost like inserting the words
"ARE NOT" between "--tcp-flags" and "SYN,R...". To put it one final way,
the "!" argument inverts the test or condition which it comes before.
We are assuming here that all outbound traffic is allowed (actually, we
set it to be so at the beginning), i.e. that there are no restrictions on
what you can send to the Internet. That makes this an ingress firewall but
not an egress. This may not necessarily be desirable in a corporate
situation or on a sensitive network, but for this home scenario its fine.
Now, while you don't need to explicitly tell the kernel that its ok to
allow outbound traffic, lets do it anyway so we can see what it looks
like...
# iptables -A OUTPUT -o eth0 -j ACCEPT
...as you can see, we are now dealing with the OUTPUT chain which
obviously is for outbound traffic. Now instead of "-i" which earlier meant
"coming in on interface xyz" we now have "-o" which means "going out on
interface xyz". I'll state the obvious and say that with the OUTPUT chain
you couldn't use "-i" because the output chain will never have incoming
traffic to deal with, its simply not possible. The visa versa also
applies.
Of course what we also don't want is any nasty broadcast traffic getting
from your network to the gateway or outside world. Nor do we want it to
activate a dialup or dial-on-demand (if you are using ISDN or PSTN instead
of DSL). You may for some strange reason have a Windows workstation
sitting on your network, which often blast out broadcast traffic and
trigger dialups on gateways. So lets cater for this in a simple basic
way...
# iptables -A INPUT -i eth1 -d 192.168.1.255 -j DROP
...what we're doing here is dropping broadcast traffic. There are a few
important things to note. First of all note the interface used in the
rule, eth1 (the internal facing interface) not eth0 (the Internet facing
interface). The reason for this is we don't want the gateway PC to ever
see this evil broadcast traffic in case it brings up the line (if
applicable). The IP address used is the broadcast address of the IP range
used in this example.
Right then, if this were just a workstation and not and gateway/firewall,
we'd have an almost workable (albeit basic) firewall at this stage. But
this is a gateway, more over, it probably has to do NAT (Network Address
Translation) too. Most people will only have 1 public IP address given to
them by their ISP, so NAT is pretty handy. Having just the 1 public IP
does not mean you can't run publicly accessible servers - we'll come back
to that later. For now, lets get NAT and forwarding going...
# iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
...here "-t" tells iptables we are using the "nat" table, which here we
are using for "postrouting". We can also do "prerouting", in fact we will
use this toward the end. Basically postrouting is a chain (part of the nat
table) which is for adjusting packets as they are about to go out or leave
the network. No prizes for guessing that prerouting is for adjusting
packets are they enter a network.
If you like, you could leave it there. The above should work, but its
still a bit lacking, even for a basic firewall. We could however go a
little further and gain a lot more. So, lets continue with some likely
scenarios.
Lets say you want to be able to SSH back to your gateway from the outside
world. Maybe so you can access your personal mail...
# iptables -A INPUT -i eth0 -p tcp --dport 22 -j ACCEPT
...having said that, you really only want to collect it from work. So
there is no point in risking leaving SSH open to the world, when you know
the IP address your work network comes from. So instead of the above rule,
lets use this one...
# iptables -A INPUT -i eth0 -s 123.123.123.123 -p tcp --dport 22 -j ACCEPT
...what we're saying here is that any traffic coming in on eth0, who's
source IP address ("-s") is 123.123.123.123 coming in on TCP port 22
(which is SSH's port) should be allowed. Obviously you should change the
IP address to the appropriate one or network. In the same way the above
rule uses "-s" to specify source address or network, we can also use "-d"
to specify destination address if we needed to. The same applies to
"--dport" which represents destination port, you can specify "--sport"
which, you guessed it, means source port. The only point to note when
specifying source or destination ports is that you have to state which
protocol you mean. Thats why we have used "-p tcp", i.e. protocol tcp.
Now, we still haven't discussed that clunky old 486 in the corner you want
to use as your web server. Obviously if people are going to be able to
browse to your sites, the box has to be accessible to the public Internet,
or to be more precise, port 80 on it must be accessible to the public
Internet. The simple way we do it in a nat situation like this is to
forward port 80 from your gateway's external IP to port 80 on your
internal clunky old 486. In other words, your website will be on the IP
your ISP gave you at port 80 (where web servers normally listen, or 443
for HTTPS). This is done with the following, where 192.168.1.3 is the IP
private address you've given to the 486 and you will remember that we are
calling 121.121.121.121 is real public IP that your ISP assigned you...
# iptables -t nat -A PREROUTING -p tcp -d 121.121.121.121 --dport 80 -j
DNAT --to 192.168.1.3:80
...the most interesting thing to note above this rule is that in fact, we
can change the port we send it in to internally. As you can see, we
specify the destination port "--dport" is 80, as that is where web
requests will come in. But would could forward that to any port on the
internal server if you wanted, just change the port which comes after the
IP address, like ":80" in this case. A little food for though: you could
take this rule a step further by specifying a source IP address too
("-s"), so the rule would only apply if traffic was coming from that
address. This could be handy if you only wanted the web server to be
available to your college network and not the whole Internet for example.
You could reverse that and make it available to everyone except your
college network by using "! -s" as mentioned earlier.
Its time for one last iptables rule, just to make sure you have complete
access to your gateway PC from your local network without restrictions.
This is obviously not suitable for all situations.
# iptables -A INPUT -i eth1 -s 192.168.1.0/24 -j ACCEPT
Now, lets reopen /etc/sysctl.conf and add a little more protection. First
of all add the following line...
net.ipv4.conf.default.send_redirects = 0
...which will make sure we don't send any ICMP redirect messages. On that
note, lets also make sure we don't act on any we receive either...
net.ipv4.conf.default.accept_redirects = 0
For pure pig iron, lets turn on the kernel's inbuilt syn cookie
protection. This simply put affords you some protection against syn
floods...
net.ipv4.tcp_syncookies = 1
The last thing we will add to this file as a little trick to make sure we
ignore ICMP broadcast requests. If left enabled it is a way people could
discover your existence. Block this by adding...
net.ipv4.icmp_echo_ignore_broadcasts = 1
...now save and close the file. While we're at it, lets save the iptables
rules too. We will do this in such a way that they will reload when the
computer reboots...
# iptables-save > /etc/sysconfig/iptables
...if you want to edit the rules in the future, you can do so using your
favorite editor and attacking the file /etc/sysconfig/iptables, which is
quite simple when you think it all through.
When these rules are put in memory / active, your system should
automatically load any related modules like ip_conntrack.
This is quite a basic firewall, but it involves enough flags and
attributes to allow you to make it in to something a lot more functional.
Don't be afraid to play around. For example where we have used the INPUT
and OUTPUT chains, you can apply many of the same rules to the FORWARD
chain, i.e. the chain which passes the traffic back and forth for your
network.
A few things won't work so well, like FTP and Voice Over IP. They can be
made to function properly though. You need to look in to ip_conntract_ftp
(tldp.org and google.ie are your friends). You can also examine the state
of traffic in considerably more detail than just ESTABLISHED or RELATED
like we have used above. In fact its incredibly powerful. have a look in
to the man page (man iptables) for more information. Depending on your
kernel, you can mirror ports too! Also, using the "type of service" field
in the packet headers, you can assign priority to packets and services as
they go out (see TOS in iptables). This is great for giving priority to
interactive traffic like web, ssh and so on.
Once your comfortable with all this, you can start using logging too.
Basically you would repeat each rule you want to log, but with the action
set to log ("-j LOG ..."). Then someday when you have nothing to do, you
can play with using Intrusion Detection Systems (IDS) like wonderfully
named services such as SNORT :o)
Anyway, the point of this is to try and demonstrate that IPTables is not
some mean nasty beast thats hard to use and write rules for. Once you
think things through logically, you can do pretty much anything you want!
So don't bother with your fancy front-end. They can often be more
confusing still!
Linux is natively an incredibly powerful firewall system. You'd be amazed
how many of the big commercial firewall appliances are actually just
running embedded Linux! I have yet to come across anything more flexible
and powerful.
(I acknowledge and accept that there are probably mistakes in the above.
This was done on a laptop, offline, in a lovely place called the Squirrel
Inn[4]. A lot of it the descriptions given for various functions have been
purposely simplified - so no grief please :o)
If you have questions, just ask! We're a Linux User Group. We're here to
help each other with Linux!
Y'all take care now.
- Ronan
[1] I know, I know. You don't have to say it. But for the most part,
alright?
[2] ...daaa da daaa da Uber Alles... ;o)
[3] Dear God, how did we survive having to use that?
[4] Fantastic place to stay if your in Surrey -
http://www.thesquirrelinn.co.uk/
Maintained by the ILUG website team. The aim of Linux.ie is to
support and help commercial and private users of Linux in Ireland. You can
display ILUG news in your own webpages, read backend
information to find out how. Networking services kindly provided by HEAnet, server kindly donated by
Dell. Linux is a trademark of Linus Torvalds,
used with permission. No penguins were harmed in the production or maintenance
of this highly praised website. Looking for the
Indian Linux Users' Group? Try here. If you've read all this and aren't a lawyer: you should be!