01/02/2005
"that doesn't make me amiable, it makes me stupid"
This is the fourth in a series of back-dated site updates that have only just been published
firewalling and traffic shaping
Okay so like I said last time I had decided to write an init script for setting up iptables since the one that came with the package was now deprecated. I also thought, if I do this for iptables I might as well do traffic shaping with tc at the same time, I've got all the kernel modules and how hard can it be?
Anyway so firstly lets do iptables. There's three tables you have to worry about. They are named filter, nat and mangle. Then, there are five places, called hooks, where the kernel's networking code gives packets to iptables (well, more strictly, to netfilter) to process. These are called PREROUTING, INPUT, FORWARD, OUTPUT, and POSTROUTING. For each table, when a packet reaches a given hook, the corresponding chain of rules attached to that hook is called to process the packet.
The tables seem to exist to conceptually separate iptables rules into known tasks. Some tables don't even have chains on all hooks, and only specific types of rule are allowed into each table. For example, the filter table only has chains at the INPUT, OUTPUT and FORWARD hooks. The separation into tables is for two reasons, one, it makes things clearer, and two, if you don't need a whole table, you don't have to have it in your kernel, and you can save space.
(I don't see any reason why you couldn't implement iptables without the tables, by allowing any type of rule to be attached to one big chain at each hook. However this would make configuration more complicated, make your kernel bigger than it needs to be, and probably harm a load of other stuff I can't think of right now... but I digress)
Anyway rather than repeat the entire netfilter HOWTO documents here I'll just say what I did. Firstly the input chain
- Accept anything that the connection tracking believes is part of an already established connection, or related to an already established connection
- Accept new connections to a small selection of ports, e.g. 80 (else you wouldn't be able to see this...)
- Drop everything else. This is pretty much standard. It is safer to drop everything that doesn't match something you know about and are willing to handle. To do this you set the default policy of a chain.
I should note at this point that instead of just dropping packets I set it to "reject" them. This means you send back an ICMP packet saying the connection has been refused. You can also send back a TCP RESET for refused TCP connections. I have still yet to decide whether this is a good thing or not. It makes things like port scans faster (and I do like to portscan myself from time to time just to check it's all still working) but it could potentially be used to flood hosts with ICMP port unreachables or TCP resets by faking your source address. I don't know.
Anyway the OUTPUT chain, in contrast, can probably be left empty with a default polict of acceptance, since it is likely that you don't want to block anything your server does. One thing I have seen in OUTPUT chains is the dropping of all traffic leaving on port 135; ISPs such as plusnet interpret this as possible virus traffic and shut you down until you reboot your modem, even if it was actually just something harmless, like nmap. NTL don't do that though so at the moment I don't care.
The FORWARD chain is slightly more complicated. In some sense it needs to be the INPUT chain and the OUTPUT chain both at once. You have to be more careful and look at which way packets are heading when they reach it. You probably want to allow hosts on your network to connect to hosts outside it, but not hosts outside to connect to hosts within; although, if you have to use NAT you get that anyway.
That bring me nicely to NAT. How's that for a segué! It stands for Network Address Translation and means rewriting either the source or the destination address of packets as they enter or leave your network. A common use of NAT, and the only thing I use it for currently, is called masquerading:
- You have several computers which you wish to connect to the internet, but your ISP only gives you one globally IPv4 address
- Internally you give your network's hosts addresses that aren't globally valid (as specified by RFC 1918, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)
- When your server sends out packets to remote addresses it rewrites the source address to your global IP and makes a note of it
- When packets come back addressed to the server, instead of routing the packets to itself and dropping them as unrecognised it writes their previous address back in and forwards them
As you can see, connection tracking is inherently a part of NAT so if you use NAT at all you get the state matching stuff for the filter table for free!
Other uses of NAT include things like port forwarding: say you are trying to play an online game and running a server for your friends to connect to. More than likely this game is Windows-only so you have to run the server program on a Windows box. You don't want to connect a Windows box directly to the internet. So you open the game's port and use NAT to forward all traffic into that port to the windows box. I did this a few months ago so the girl and I could play Doom over the internet, because I don't have X or any Doom stuff installed on my router. (Don't try this though, prboom's network code is really meant for LANs and runs really slowly over the internet, we called it treacle mode, it was quite painful)
The final table is the mangle table, which on recent kernels at least puts chains on all possible hooks. It is meant for changing arbitrary variables in packets. It is usually not necessary and has quite specialised uses. I use it to mark packets to help the traffic shaping script.
Yeah speaking of which I'll save traffic shaping for another time as this update is long enough already. I nearly said "long and boring enough" but I'll get yelled at if I'm too negative about my things, so yeah