What is the difference between these 2 iptables rules?

csi asked:

Trying to allow incoming ssh traffic on port 22. Default behavior is to DROP all incoming traffic.

I came across 2 articles on how to allow traffic. However, they are different.

## open port ssh tcp port 22 ##
iptables -A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -s 192.168.1.0/24 -m state --state NEW -p tcp --dport 22 -j ACCEPT

Vs

# Allow all incoming SSH
iptables -A INPUT -i eth0 -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -o eth0 -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT

It appears that the 1st one allows all traffic and then specifes a specific network. Seems like those are mutually exclusive?

What are the differences between these 2 and which one should I use?

My answer:


Both of those sets of rules have problems, and I wouldn’t use either of them as-is.


In the first set, the first rule allows new incoming traffic to destination port 22 from anywhere. This isn’t a problem.

The first problem is that the second rule allows new incoming traffic to destination port 22 from a specific subnet. This is completely redundant since the first rule has allowed traffic from anywhere.

My guess is that you read some tutorial which used these rules as (mutually exclusive) examples, advising you to select one or the other, but not both.

The second problem is that another rule is required before this rule to make the firewall fully stateful, and that rule is missing here. Without this rule, only the SYN packet would be allowed, and the connection would never complete.

-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

The second set of rules has a similar problem. I don’t know who originally wrote this ruleset, but it’s been widely copied all over the Internet. It seems to have been written by someone who was unfamiliar with stateful firewalls in general, or iptables in particular.

In this ruleset, the input rule allows new and established incoming traffic to destination port 22 from anywhere. Then, the output rule allows established traffic from source port 22 to anywhere. This is essentially a mirror of the first rule and something like it is required if your default policy on outgoing traffic is to drop or reject it.

The problem is that these outbound rules become redundant very fast, which leads to performance issues as well as comprehension problems for any humans who have to read the rules later. If you are dropping outgoing traffic, only one rule is needed for established outgoing traffic (matching allowed incoming traffic) on any port, regardless of how many incoming ports you allow.

-A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

For a host firewall where the default input policy is to deny trafic and the default output policy is to permit traffic, most all of your rules will be in the INPUT table. It’s sufficient to have the stateful rule and then rules to open ports for any incoming traffic you need.

For example, to allow ssh and http connections:

-P INPUT DROP
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT

If you’re also denying output traffic by default, then you need to allow the return traffic for these allowed inbound connections as well.

-P OUTPUT DROP
-A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

And that is a firewall that’s about as simple and effective as iptables can be.


View the full question and answer on Server Fault.

Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.