作者:高张远瞩(HiLoveS)
博客:http://www.cnblogs.com/hiloves/
转载请保留该信息
在《使用iptables限制黑客猜密码》中介绍了如何使用iptables的recent模块限制一段时间内的连接数。查阅网上文章,发现不同的文章有不同的recent命令,而且每位作者都言之凿凿说他的命令可以用。这些recent的命令大体有如下不同:1、set句在前,update(或rcheck)句在后,2、update(或rcheck)句在前,set句在后,3、set句带或不带-j ACCEPT。基本是上面这三项的排列组合。我用来用去不觉得很好用,这一篇就再深入一下。
先从一个案例开始:
案例:对连接到本机的SSH连接进行限制,每个IP每小时只限连接5次。
命令:
-A INPUT -p tcp --dport 22 -m state --state NEW -m recent --name SSHPOOL --rcheck --seconds 3600 --hitcount 5 -j DROP -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --name SSHPOOL --set -j ACCEPT
模拟一下某个电脑连接本机SSH服务的数据包流程,假设以下数据包是在一小时内到达本机的:
1、当这个电脑的第1个SSH包到达本机,规则1检查SSHPOOL列表中这个源IP是否有hitcount,因为是第一个包,显而易见,列表是0,规则1判定这个数据包不必执行DROP,并且也不处理这个数据包,将数据包转给下条规则。
2、规则2将这个数据包计入SSHPOOL列表,就是做+1,因为规则中有-j ACCEPT,规则2放行这个包。
3、第1个数据包进入本机,不用再在iptables里转了。
4、当第2个SSH包到达本机,规则1检查SSHPOOL列表的hitcount,发现是1没有超过5,于是判定不执行DROP并转给下条规则处理。
5、规则2在SSHPOOL中+1,并放行,第2个数据包进入本机。
6、第3、4、5个包同上。
7、第6个包到达本机,规则1检查SSHPOOL列表中的hitcount,发现是5了已经连接5次了,于是规则2执行DROP,不必再转给下条规则了丢弃该包。
8、第7、8…个包同上。
实际上recent的处理更为复杂,这里只是粗浅的描述一下,set也并非简单的做+1。详细的列表在/proc/net/xt_recent/目录下,用cat查看即可。
从上面的流程可以看出,--set的功能在于计录数据包,将源IP加入列表。--rcheck(update)的功能在于判定数据包在seconds和hitcount条件下是否要DROP。
我在CU论坛上看到另一个版本的命令,作者保证是从服务器上复制下来的,肯定能用:
-A INPUT -p tcp --dport 22 -m state --state NEW -m recent --name SSHPOOL --set -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --name SSHPOOL --rcheck --seconds 3600 --hitcount 5 -j DROP
但在我的服务器上这两行命令就是不能用,所有的SSH包都被丢弃了。最后问题找到了,这两行命令仅能工作在INPUT链的默认策略为ACCEPT的情况下,而我的INPUT链的默认策略为DROP。这个命令与我的命令的区别在于:set句在前,且set句不带-j ACCEPT。这导致数据包的流程也不同。
1、第1个数据包到达规则1后马上计入SSHPOOL列表,并且规则1因为没有-j ACCEPT,直接将数据包转给下条规则。规则2拿到这个数据包后检查SSHPOOL列表,发现是1,也不处理这个包,转给下条规则。如果后续的规则都没有再处理这个数据包,则最后由INPUT链的默认策略ACCEPT处理。由于我的策略是DROP,所以丢弃该包,于是这两行命令在我的服务器上不能用。
2、这里有个问题,由于set句在前,数据包进入是先计入列表,再判定是否合法。这导致第5个包到达后,先在列表中+1,结果是5,再由规则2判定,发现是5,结果丢弃该包,最后真正ACCEPT的只有4个包。本人认为这样写的代码不符合正常的思维逻辑。而且这样写只能正常工作于默认策略是ACCEPT的情况,所以本人不建议用这个版本的命令,我的版本ACCEPT、DROP策略都能用。
从上面可以看出,如果大家在网上看到不同的recent模块的命令,记得一定要先问问作者INPUT链的默认策略是什么。
最后,rcheck和update的区别:
rcheck从第1个包开始计算时间,update是在rcheck的基础上增加了从最近的DROP包开始计算阻断时间,具有准许时间和阻断时间,帮助中说update会更新last-seen时间戳。
放在案例中,rcheck是接收到第1个数据包时开始计时,一个小时内仅限5次连接,后续的包丢弃,直到一小时过后又可以继续连接。update则是接收到第1个数据包时计算准许时间,在一个小时的准许时间内仅限5次连接,当有包被丢弃时,从最近的丢弃包开始计算阻断时间,在一个小时的阻断时间内没有接收到包,才可以继续连接。所以rcheck类似令牌桶,一小时给你5次,用完了抱歉等下个小时吧。update类似网银,连续输错5次密码,停止一小时,只不过update更严格,阻断时间是从最近的一次输错时间开始算,比如输错了5次,过了半个小时又输错一次,这时阻断时间不是剩半小时,而是从第6次重新计算,剩一小时。这个区别我不太会表述,大家自己做做测试就明白了。
给大家一个测试用的命令,自行替换rcheck、update,然后ping一下就明白了:
-A INPUT -p icmp -m recent --name PINGPOOL --rcheck --seconds 30 --hitcount 5 -j DROP -A INPUT -p icmp -m recent --name PINGPOOL --set -j ACCEPT
最最后,再啰索一句
ICMP包和UDP包在iptables中的state情况是一样的,因为是无状态的,不同于TCP,iptables可以靠SYN等flags确定state,而iptables是基于ICMP包/UDP包到达服务器的间隔时间来确定state的。像我在做上面测试的时候,使用ping 192.168.1.1 -t时,除了第一个ICMP包state是NEW,后续的包state都是ESTABLISHED,结果因为前面有一句
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
结果ping一直是通的,搞了半天才弄明白。