iptables rules
Can someone explain to me the differences between these two rule sets?
Rule set 1:
1: Chain INPUT (policy ACCEPT)
2: target prot opt source destination
3: ACCEPT all -- anywhere anywhere
4: ACCEPT udp -- anywhere anywhere udp spt:domain dpts:1024:65535
5: ACCEPT tcp -- anywhere anywhere tcp spts:1024:65535 dpt:ssh state NEW
6: ACCEPT tcp -- anywhere anywhere tcp spts:1024:65535 dpt:www state NEW
7: ACCEPT tcp -- anywhere anywhere state RELATED,ESTABLISHED
8: DROP all -- anywhere anywhere
9:
10: Chain FORWARD (policy ACCEPT)
11: target prot opt source destination
12: DROP all -- anywhere anywhere
13:
14: Chain OUTPUT (policy ACCEPT)
15: target prot opt source destination
16: ACCEPT all -- anywhere anywhere
17: ACCEPT udp -- anywhere anywhere udp spts:1024:65535 dpt:domain
18: ACCEPT all -- anywhere anywhere state RELATED,ESTABLISHED
19: ACCEPT tcp -- anywhere anywhere state NEW,RELATED,ESTABLISHED multiport dports www,https multiport sports 1024:65535
20: DROP all -- anywhere anywhere
Rule set 2:
This rule set seems to be either more widely used or one person wrote it and a lot of people copied and talked about it….
1: Chain INPUT (policy ACCEPT)
2: target prot opt source destination
3: ACCEPT all -- anywhere anywhere
4: REJECT all -- anywhere loopback/8 reject-with icmp-port-unreachable
5: ACCEPT all -- anywhere anywhere state RELATED,ESTABLISHED
6: ACCEPT tcp -- anywhere anywhere tcp dpt:www
7: ACCEPT tcp -- anywhere anywhere tcp dpt:https
8: ACCEPT tcp -- anywhere anywhere state NEW tcp dpt:30000
9: ACCEPT icmp -- anywhere anywhere icmp echo-request
10: LOG all -- anywhere anywhere limit: avg 5/min burst 5 LOG level debug prefix `iptables denied: '
11: 11REJECT all -- anywhere anywhere reject-with icmp-port-unreachable
12:
13: Chain FORWARD (policy ACCEPT)
14: target prot opt source destination
15: REJECT all -- anywhere anywhere reject-with icmp-port-unreachable
16:
17: Chain OUTPUT (policy ACCEPT)
18: target prot opt source destination
19: ACCEPT all -- anywhere anywhere
Is there a difference between specifying multiple ports in 1 rule or having each port have it's own rule?
I don't understand the LOG part on line 10 of the 2nd rule set.
I also don't understand why the 1st rule set is specifying source ports. What does that do exactly?
What are you opinions on DROP vs REJECT? Do you REJECT packets and inform the user that the connection was refused or do you just DROP them?
Is there something else I should be looking at?
11 Replies
> Is there a difference between specifying multiple ports in 1 rule or having each port have it's own rule?
Technically no, but I'm not sure there is a way to specify multiple non-sequential ports in one line. It'd be fine if you wanted to say "allow traffic on destination ports 1-1000" (please don't do that), but usually you separate rules when saying allow traffic on port 80, and on port 443, etc.
You can also see the byte/packet counts for each rule.
> I don't understand the LOG part on line 10 of the 2nd rule set.
This simply logs anything that has gotten to that point in your ruleset to syslog. In this case, it logs anything that gets rejected. The limit stuff is there so that a maximum of 5 entries per minute will be logged, this prevents your logs from growing dramatically and slowing your system down.
> I also don't understand why the 1st rule set is specifying source ports. What does that do exactly?
Typically, operating systems make outbound connections on non-privileged ports (port 1024 and higher). This rule enforces that connections to services such as ssh and www come from one of these ports. I don't do this on my iptables, so I'm not 100% sure the reasoning behind blocking requests coming from lower numbered ports. Generally "good" traffic will only come from higher ports, but "bad" traffic will not just come from lower ports.
> What are you opinions on DROP vs REJECT? Do you REJECT packets and inform the user that the connection was refused or do you just DROP them?
Drop makes it look like a server isn't even there. Reject says "yes I'm here, but no you can't come it". I tend to use DROP so that someone doing a port scan of random IPs (say a Linode block) doesn't necessarily know I'm there unless they hit on one of my open ports (not that hard to do).
> Is there something else I should be looking at?
The iptables man page is the best thing.
There is tons of stuff on iptables out there. My personal favorite is rate limiting SSH connections to make brute force attacks more difficult:
> -A INPUT -m state –state ESTABLISHED,RELATED -j ACCEPT
And later:
> -A INPUT -p tcp -m state –state NEW –dport 30000 -j ACCEPT
The oversight here is that after the ESTABLISHED,RELATED rule, the only packets moving forward are NEW and INVALID. For every SSH connection, the ruleset is needlessly checking the state, as it's only going to be NEW or INVALID (and you should have a rule to drop INVALID anyway). The method with which I preamble my iptables ruleset is as follows:
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -m state --state INVALID -j DROP
After those two rules every packet will be NEW (for TCP, a SYN). I'm not the biggest fan of rejecting unexpected traffic, either; I know it's The Right Thing to do, but in a DDoS situation you're going to be compounding the problem with the rejection packets.
Another thing that really bothers me is blind rejection of all ICMP packets except for type 8. Particularly for IPv6, ICMP serves a lot of uses that naive administrators overlook by dropping them all.
A good rule of thumb to keep in mind when you're dealing with Netfilter is that your most popular traffic should have a decision made on it as soon as possible, to decrease overhead. Another important means of decreasing overhead is doing as few compares as possible before a decision is made. Your most popular traffic is almost always RELATED,ESTABLISHED and that should be the first rule in the table. In these rulesets, the loopback rules are first…which means that for every active connection, you're doing a compare against loopback for every packet, which is a waste of overhead.
The first ruleset doesn't fare much better there, as ESTABLISHED,RELATED is pushed 5 rules down.
Another problem I noticed with both rulesets it that each author adds a rule to the end of INPUT and OUTPUT, without even addressing the chain's policy itself. Every chain has a default policy which, when packets run off the end, gets used (including a friendly counter on the top). For example, to set default DROP on everything:
-P INPUT DROP
-P OUTPUT DROP
-P FORWARD DROP
…yet, these authors add a pointless rule to do the same thing.
I don't like either ruleset (because each strikes me as written by someone not very conscious of Netfilter's workings), and my advice to you would be to play with the rulesets themselves on a non-production box. Verbosely log your traffic at various spots in the chains and see what does what. That's how I taught myself iptables (I didn't adopt Linux early enough to get into ipchains, its predecessor), and once it clicks, it's not that bad.
Edit: Oh, forgot to add – you can look at something like Shorewall
# flush all chains and zero the counters
iptables -F INPUT
iptables -F FORWARD
iptables -F OUTPUT
iptables -Z
# easy log/drop chain (so you don't pollute your real chains)
iptables -N KILL
iptables -A KILL -m limit --limit 6/min -j LOG --log-prefix "Drop: "
iptables -A KILL -j DROP
# create privileged chain: stuff in this chain only I should have access to
iptables -N ME
iptables -A ME -s home.workstation.org/32 -j ACCEPT
iptables -A ME -s office.outgoing/32 -j ACCEPT
iptables -A ME -j KILL
# create a BAD chain for hosts that are abusing you
# (let packets run off this one)
iptables -N BAD
iptables -A BAD -s bad.boy/24 -j KILL
# create side chains for new connections
iptables -N ICMP
iptables -N TCP
iptables -N UDP
# now let's actually create rules
# preamble (take care of everything but state NEW)
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -m state --state INVALID -j DROP
# take care of localhost
iptables -A INPUT -i lo -s 127.0.0.1/8 -d 127.0.0.1/8 j ACCEPT
# take care of everything else
# note I use eth0 for sanity, feel free to adjust at will or drop the -i rule
# not to us? kill it
iptables -A INPUT -i eth0 ! -d this.server/32 -j KILL
# check for a bad host (drop in that chain, if it runs off it returns here)
iptables -A INPUT -i eth0 -j BAD
# go to our separate chains
iptables -A INPUT -i eth0 -p udp -j UDP
iptables -A INPUT -i eth0 -p icmp -j ICMP
# since all packets at this point TCP and state NEW, the packet should be SYN
iptables -A INPUT -i eth0 -p tcp ! --syn -j KILL
iptables -A INPUT -i eth0 -p tcp -j TCP
# odd, shouldn't get here, log it
# (some still do, it's usually proto 41 stuff or some other Martian packets)
iptables -A INPUT -j LOG --log-prefix "Wut?: "
# (don't forget, run off INPUT chain = DROP)
# TCP chain -- I sort by port number because I'm anal
iptables -A TCP -p tcp --dport 22 -j ME
iptables -A TCP -p tcp --dport 25 -j ACCEPT
iptables -A TCP -p tcp --dport 80 -j ACCEPT
iptables -A TCP -p tcp --dport 587 -j ME
# log failed TCP connections instead of returning to main chain
iptables -A TCP -j KILL
# ...etc
# UDP chain
iptables -A UDP -p udp --dport 53 -j ACCEPT
# silently drop UDP we don't care about (you may wish to log)
iptables -A UDP -j DROP
# ICMP chain
# filter out specific stuff you don't want, and I prefer to allow the rest
# the chain is there for you to customize either way
iptables -A ICMP -j ACCEPT
Don't forget ip6tables, too, if you're IPv6-enabled.
The 1st ruleset seems to be someone just blindly copying info from the source:
I don't know iptables (yet) so I was looking for a "standard" ruleset that's good to use. Since I'm really only using my Linode for web sites, port 80, 443 and 22 are really all I care about. So I just want (I think) to allow any outgoing traffic and for inbound only allow 80, 443 and 22, block/drop all other traffic. Well I do want to allow ICMP. I've never understood why "administrators" block ICMP traffic.
ufw
> After those two rules every packet will be NEW (for TCP, a SYN). I'm not the biggest fan of rejecting unexpected traffic, either; I know it's The Right Thing to do, but in a DDoS situation you're going to be compounding the problem with the rejection packets.
I prefer to reject packets over dropping. There are two reasons. One is security through obscurity. I use "REJECT –reject-with icmp-host-unreachable" at the end of the INPUT and FORWARD chains. When somebody tries to connect on a port that isn't listening (or ping) they receive:
C:\Users\Me>ping 192.168.1.1
Pinging 192.168.1.1 with 32 bytes of data:
Reply from 192.168.1.1: Destination host unreachable.
Reply from 192.168.1.1: Destination host unreachable.
Reply from 192.168.1.1: Destination host unreachable.
Most tools simply look to see if they receive a destination host unreachable, but if they look closely the system they are attempting to connect to is the one saying the host is unreachable. This will stop most hacking tools
The other reason is that you aren't really compounding the problem by telling the remote system that you aren't unreachable. Yes, you are sending even more traffic, but if you don't send a response then they are holding memory open waiting for your response. If you do send a response then they will process it and release that memory.
Thought I'd post something I found on the internet helping to stop ssh brute force attacks. You can set your ssh rule as:
iptables -A INPUT -p tcp –dport ssh -m state --state NEW -m recent --set -j ACCEPT
iptables -I INPUT -p tcp --dport ssh -i eth0 -m state --state NEW -m recent --update --seconds 60 --hitcount 4 -j DROP
Then if someone attempts to log in with an invalid username/password more than 3 times in a minute it will drop them. Lol, I just realized this has been posted here before quite recently.
@davidwelch:
Hi,
Thought I'd post something I found on the internet helping to stop ssh brute force attacks. You can set your ssh rule as:
iptables -A INPUT -p tcp –dport ssh -m state --state NEW -m recent --set -j ACCEPT
iptables -I INPUT -p tcp --dport ssh -i eth0 -m state --state NEW -m recent --update --seconds 60 --hitcount 4 -j DROP
Then if someone attempts to log in with an invalid username/password more than 3 times in a minute it will drop them. Lol, I just realized this has been posted here before quite recently.
Actually, I don't think those rules will work unless you reverse their order.
@davidwelch:
Then if someone attempts to log in with an invalid username/password more than 3 times in a minute it will drop them.
If someone even connects 3 times in a minute, it will drop them, even if they successfully logged in.
Yes, I bang this drum quite hard.
For the most part, scanners are typically looking for active service ports. This generally includes stuff you're already going to be listening on for services.
What's the point of an iptables rule to DROP packets to ports 54-79 when port 80 is listening for web traffic?
Any decent web scanner is only going to look for known services ports (this includes applications you're running and scanning for trojan service ports).
That said, a good iptables ruleset is possibly a good idea if you want that much control over your system. Logging potentially damaging traffic is a great idea in a lot of cases, if you're seeing problems. And if you're seeing problems from certain hosts, having a rule to block them isn't a bad idea either.
That said, iptables isn't as important for security as other technologies you can implement.
If you want to block service scanners, a good idea is to run the utility portsentry (and of course iptables) on common service ports below your "protected" ports.
So for sequential port scans of 1-79, you can have portsentry add a drop rule to that host in iptables, so when they get to 80–they don't see that you're running a webserver.
It's an interesting concept, and I've used it in practice, but in the larger scheme of things there are much more security issues to worry about and guard against.
What good is a solid iptables ruleset if your apache is letting every web user write to /etc? (Exaggerated example, but you get the point)
@zibeli:
@davidwelch:Hi,
Thought I'd post something I found on the internet helping to stop ssh brute force attacks. You can set your ssh rule as:
iptables -A INPUT -p tcp –dport ssh -m state --state NEW -m recent --set -j ACCEPT
iptables -I INPUT -p tcp --dport ssh -i eth0 -m state --state NEW -m recent --update --seconds 60 --hitcount 4 -j DROP
Then if someone attempts to log in with an invalid username/password more than 3 times in a minute it will drop them. Lol, I just realized this has been posted here before quite recently.
Actually, I don't think those rules will work unless you reverse their order.
Nevermind, I just noticed that you're inserting rather than appending the 2nd rule above (-I instead of -A), so since the default insert position is at the start of the chain the second (above) rule will be first in the chain. Sorry for the noise.