Skip to main content
Welcome guest. | Register | Login | Post

A tiny decision-issue regarding an IPTables-script

2 replies [Last post]
Offline
Joined: 2006-03-28

Here in the office I have a firewall-script with a couple of chains for different purposes. I like my firewall-scripts pretty modular so I group rules that belong together into one chain and link these chains into the default chains. There are 3 chains which are related to logging (on Linux 2.6 that would be just 2, but my server here is running Debian with a 2.4-kernel), 4 are in charge of accepting and rejecting packages.
I like to follow a few principles here. Of course I do what I like to call "the first rule of firewalling", which is blocking everything and just allowing what I need. Also I tend to follow the KISS principle, to ensure easy readability in case somebody else, somebody who doesn't know IPTables at all, has to have a look into this (which is unlikely, but not impossible).

Letting aside logging, which for this scenario is not important I have these chains, linked in INPUT in the order shown.

Quote:

common
external
internal
send2hell

Yes, the fourth chain is really called send2hell, because that's where all packets can go if they're not accepted in one of the previous chains.

The calls look like this:

$IPTABLES -A INPUT -j common
$IPTABLES -A INPUT -j external
$IPTABLES -A INPUT -j internal
$IPTABLES -A INPUT -j send2hell

So here are no checks at all if a package is supposed to go into a specific chain, which in most cases is totally okay, with one exception - internal. I added a value $INT_NET which holds the local network-address. Before I had the script jump into internal and each rule there had a check for $INT_NET. After realizing that this was nonsense, because I could just add the check for internal origin once by putting it into INPUT I could optimize performance because the packet would not have to be checked against every single rule in internal if it's not from an internal source.
The call looked like this:

$IPTABLES -A INPUT -s $INT_NET -j internal

Recently I thought I restructure a little bit and make the links in INPUT easier to read again, the way it was before, but still wanted to have a check that not every rule in internal has to contain the check for $INT_NET and that a package not from the local network doesn't need to be checked against every single rule there (internal is the longest chain in my firewall).
So now I have it again like before and added a rule at the top of internal that does the check. If it fails it jumps back out of the chain.

$IPTABLES -A internal -s ! $INT_NET -j RETURN

Unlike most other targets RETURN will not take the package out of the process, but just pass it back to where it came from, which in this case is INPUT. Processing then will continue there, which here means that it will jump into send2hell and the package will finally get blasted into space, where nobody can hear it scream.

Now my question is, is this really easier to read than having the check as jump-condition (see 2nd call) or not?
Because if not I will change it back to remove the additional jump that happens in case the packages is not from an internal origin. This propably is not a big difference, but when handling lots of packages (the machine is still mostly unknown, but that might change pretty soon) it could make a little difference.

a thing's picture
Offline
Joined: 2005-12-20
file needed

You'll need to put the different internal file versions somewhere (like here) then we can say what's easier to read.

Offline
Joined: 2006-03-28
Okay, here's the current

Okay, here's the current version of the script. I had to remove the part for marking and logging packages because the script would have been to wide for it's box. That's the chains logging, marking, in_marking and out_marking. Just a little explanation for those. With Linux 2.6 there is connection-marking, which I really like. But since my box here is running Linux 2.4 I have to try to resemble it as good as possible with normal marking. It seems to work okay, but takes a few more lines.

Since nobody knows where this is used it's propably not such a risk to post it here.
If you find anything that can still be improved, readability, performance or especially security, please let me know. But I guess that at least in terms of security it should be pretty tight already.

#!/bin/sh
IPTABLES=/sbin/iptables
INT_NET=192.168.0.0/24

case "$1" in
start|restart)
	$0 stop

	$IPTABLES -N common
	$IPTABLES -A common -i lo -j ACCEPT
	$IPTABLES -A common -m state --state INVALID -j DROP
	$IPTABLES -A common -m state --state RELATED,ESTABLISHED -j ACCEPT

	$IPTABLES -N external
	$IPTABLES -A external -p tcp --dport http -j ACCEPT

	$IPTABLES -N internal
	$IPTABLES -A internal -s ! $INT_NET -j RETURN
	$IPTABLES -A internal -p icmp -j ACCEPT
	$IPTABLES -A internal -p tcp --dport ftp -j ACCEPT
	$IPTABLES -A internal -p tcp --dport ssh -j ACCEPT
	$IPTABLES -A internal -p tcp --dport smtp -j ACCEPT
	$IPTABLES -A internal -p tcp --dport imap -j ACCEPT
	$IPTABLES -A internal -p tcp --dport pop3 -j ACCEPT
	$IPTABLES -A internal -p tcp --dport mysql -j ACCEPT
	$IPTABLES -A internal -p tcp --dport netbios-ssn -j ACCEPT
	$IPTABLES -A internal -p tcp --dport microsoft-ds -j ACCEPT
	$IPTABLES -A internal -p udp --dport netbios-ns -j ACCEPT
	$IPTABLES -A internal -p udp --dport netbios-dgm -j ACCEPT

	$IPTABLES -N send2hell
	$IPTABLES -A send2hell -p tcp -j REJECT --reject-with tcp-reset
	$IPTABLES -A send2hell -p udp -j REJECT --reject-with icmp-port-unreachable
	$IPTABLES -A send2hell -j REJECT --reject-with icmp-proto-unreachable

	$IPTABLES -A INPUT -j logging
	$IPTABLES -A INPUT -j common
	$IPTABLES -A INPUT -j external
	$IPTABLES -A INPUT -j internal
	$IPTABLES -A INPUT -j send2hell

	$IPTABLES -A OUTPUT -j logging

	#$IPTABLES -t mangle -A INPUT -j marking
	$IPTABLES -t mangle -A INPUT -j in_marking
	$IPTABLES -t mangle -A OUTPUT -j out_marking

	;;
stop)
	$IPTABLES -F
	$IPTABLES -X
	$IPTABLES -t mangle -F
	$IPTABLES -t mangle -X
	;;
*)
	exit 1
	;;
esac
exit 0

And here is the previous version, which had the check for the internal network in the jump-condition in INPUT. Also here I removed the marking- and logging-code.

#!/bin/sh
IPTABLES=/sbin/iptables
INT_NET=192.168.0.0/24

case "$1" in
start|restart)
	$0 stop

	$IPTABLES -N common
	$IPTABLES -A common -i lo -j ACCEPT
	$IPTABLES -A common -m state --state INVALID -j DROP
	$IPTABLES -A common -m state --state RELATED,ESTABLISHED -j ACCEPT

	$IPTABLES -N external
	$IPTABLES -A external -p tcp --dport http -j ACCEPT

	$IPTABLES -N internal
	$IPTABLES -A internal -p icmp -j ACCEPT
	$IPTABLES -A internal -p tcp --dport ftp -j ACCEPT
	$IPTABLES -A internal -p tcp --dport ssh -j ACCEPT
	$IPTABLES -A internal -p tcp --dport smtp -j ACCEPT
	$IPTABLES -A internal -p tcp --dport imap -j ACCEPT
	$IPTABLES -A internal -p tcp --dport pop3 -j ACCEPT
	$IPTABLES -A internal -p tcp --dport mysql -j ACCEPT
	$IPTABLES -A internal -p tcp --dport netbios-ssn -j ACCEPT
	$IPTABLES -A internal -p tcp --dport microsoft-ds -j ACCEPT
	$IPTABLES -A internal -p udp --dport netbios-ns -j ACCEPT
	$IPTABLES -A internal -p udp --dport netbios-dgm -j ACCEPT

	$IPTABLES -N send2hell
	$IPTABLES -A send2hell -p tcp -j REJECT --reject-with tcp-reset
	$IPTABLES -A send2hell -p udp -j REJECT --reject-with icmp-port-unreachable
	$IPTABLES -A send2hell -j REJECT --reject-with icmp-proto-unreachable

	$IPTABLES -A INPUT -j logging
	$IPTABLES -A INPUT -j common
	$IPTABLES -A INPUT -j external
	$IPTABLES -A INPUT -s $INT_NET -j internal
	$IPTABLES -A INPUT -j send2hell

	$IPTABLES -A OUTPUT -j logging

	#$IPTABLES -t mangle -A INPUT -j marking
	$IPTABLES -t mangle -A INPUT -j in_marking
	$IPTABLES -t mangle -A OUTPUT -j out_marking

	;;
stop)
	$IPTABLES -F
	$IPTABLES -X
	$IPTABLES -t mangle -F
	$IPTABLES -t mangle -X
	;;
*)
	exit 1
	;;
esac
exit 0

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.