hashlimit是iptables的一个匹配模块,用它结合iptables的其它命令可以实现限速的功能(注意,单独hashlimit模块是无法限速的)。
不过首先必须明确,hashlimit本身只是一个“匹配”模块。我们知道,iptables的基本原理是“匹配--处理”,hashlimit在这个工作过程中只能起到匹配的作用,它本身是无法对网络数据包进行任何处理的。我看到网上有些hashlimit的例子里面说只 用一条包含hashlimit匹配规则的iptables语句就可以实现限速,那是错误的。
实际上,利用hashlimit来限速需要包括两个步骤:
1.对符合hashlimit匹配规则包放行
2.丢弃/拒绝未放行的包
下面是一个简单的例子:
iptables -A INPUT -p tcp --dport 22 -m hashlimit --hashlimit-name ssh --hashlimit 5/sec --hashlimit-burst 10 --hashlimit-mode srcip --hashlimit-htable-expire 90000 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j DROP
然后,我们来着重讲讲hashlimit模块具体是如何工作的。
hashlimit的匹配是基于令牌桶 (Token bucket)模型的。令牌桶是一种网络通讯中常见的缓冲区工作原理,它有两个重要的参数,令牌桶容量n和令牌产生速率s。我们可以把令牌当成是门票,而令牌桶则是负责制作和发放门票的管理员,它手里最多有n张令牌。一开始,管理员开始手里有n张令牌。每当一个数据包到达后,管理员就看看手里是否还有可用的令牌。如果有,就把令牌发给这个数据包,hashlimit就告诉iptables,这个数据包被匹配了。而当管理员把手上所有的令牌都发完了,再来的数据包就拿不到令牌了。这时,hashlimit模块就告诉iptables,这个数据包不能被匹配。除了发放令牌之外,只要令牌桶中的令牌数量少于n,它就会以速率s来产生新的令牌,直到令牌数量到达n为止。通过令牌桶机制,即可以有效的控制单位时间内通过(匹配)的数据包数量,又可以容许短时间内突发的大量数据包的通过(只要数据包数量不超过令牌桶n)。
hashlimit模块提供了两个参数--hashlimit和--hashlimit-burst,分别对应于令牌产生速率和令牌桶容量。除了令牌桶模型外,hashlimit匹配的另外一个重要概念是匹配项。在hashlimit中,每个匹配项拥有一个单独的令牌桶,执行独立的匹配计算。通过hashlimit的--hashlimit-mode参数,你可以指定四种匹配项及其组合,即:srcip(每个源地址IP为一个匹配项),dstip(每个目的地址IP为一个匹配项),srcport(每个源端口为一个匹配项),dstport(每个目的端口为一个匹配项)
除了前面介绍的三个参数外,hashlimit还有一个必须要用的参数,即--hashlimit-name。 hashlimit会在/proc/net/ipt_hashlimit目录中,为每个调用了hashlimit模块的iptables 命令建立一个文件,其中保存着各匹配项的信息。--hashlimit-name参数即用来指定该文件的文件名。
好了,以上我们已经介绍了hashlimit的工作原理和相应的参数,下面我们来看几个例子。
首先是前面的那个例子:
iptables -A INPUT -p tcp --dport 22 -m hashlimit --hashlimit-name ssh --hashlimit 5/sec --hashlimit-burst 10 --hashlimit-mode srcip -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j DROP
在了解了hashlimit各参数的含义之后,我们现在就可以知道这两条iptables命令的作用。
第一条的作用是,为所有访问本机22端口的不同IP建立一个匹配项,匹配项对应的令牌桶容量为10,令牌产生速率为5个每秒。放行通过匹配的数据包。
第二条的作用是,丢弃所有其它访问本机22端口的数据包。
通过这两条命令,我们就实现了限制其它机器对本机22端口(ssh服务)频繁访问的功能.
再来我们看一个复杂点的限速。假设我们现在在一台NAT网关上,想限制内部网某个网段 192.168.1.2/24对外的访问频率。(这个的主要作用是限制内部中毒主机对外的flood攻击)
那我们可以这么做:
iptables -N DEFLOOD
iptables -A FORWARD -s 192.168.1.2/24 -m state --state NEW -j DEFLOOD
iptables -A DEFLOOD -m hashlimit --hashlimit-name deflood --hashlimit 10/sec --hashlimit-burst 10 --hashlimit-mode srcip -j ACCEPT
iptables -P DEFLOOD -j DROP
第一条命令建立了一个自定义的处理链
第二条命令,所有来自192.168.1.2/24网段,并且打算新建网络连接的数据包,都进入DEFLOOD链处理
第三条命令,在DEFLOOD链中,为每个IP建立一个匹配项,对应令牌桶容量为10,产生速率为10个每秒。放行通过匹配的数据包。
第四条命令,在DEFLOOD链中丢弃所有其它的数据包
当然,hashlimit还有一些其他的参数,比如
--hashlimit-htable-expire
--hashlimit-htable-size
--hashlimit-htable-max
具体可以man iptables
以上我们介绍了hashlimit模块的原理和使用。希望能对大家有所帮助:)
/
某机器有一条防DNS攻击的规则:
iptables -t raw -I dns_limit -m string --algo bm --icase \
--hex-string "|${hex_domain}|" \
-m hashlimit \
--hashlimit-name DNS \
--hashlimit-mode srcip \
--hashlimit-above 1/second \
--hashlimit-burst 1 \
--hashlimit-htable-max 1000000 \
--hashlimit-htable-expire 180000 \
--hashlimit-htable-gcinterval 30000 \
--hashlimit-srcmask 28 \
-m comment --comment "${domain}" -j DROP
当时机器上测试dig查询, 发现某个域名被完全封禁了, 而不是预想中的 限速
.
查看iptables的规则链dns_limit, 发现这个域名有两条这样的规则, 删除一条后则和预想一致, 实现了限速.
先说下最主要的, hashlimit 模块的核心是 令牌桶算法(Token Bucket) , 这个模块的作用是匹配, 限速是根据匹配结果以及target操作而实现的功能. 当时了解到这个后, 问题就迎刃而解了.
几个参数:
--hashlimit-name
: 定义这条hashlimit规则的名称, 所有的条目(entry)都存放在/proc/net/ipt_hashlimit/{hashlimit-name}
里--hashlimit-mode
: 限制的类型,可以是源地址/源端口/目标地址/目标端口--hashlimit-srcmask
: 当mode设置为srcip时, 配置相应的掩码表示一个网段--hashlimit-above
: mount/quantum, 允许进来的包速率(令牌恢复速率)--hashlimit-burst
: 允许突发的个数(其实就是令牌桶最大容量)--hashlimit-htable-max
: hash的最大条目数--hashlimit-htable-expire
: hash规则失效时间, 单位毫秒(milliseconds)--hashlimit-htable-gcinterval
: 垃圾回收器回收的间隔时间, 单位毫秒
上面是man手册比较正式的解释.
关于 expire 和 gcinterval, 如果在这个时间内没有再次触发规则, 则时间逐渐减为0, 进而负数, 但是并不会从hash中删除, 直到垃圾回收器执行后, 才会删除.
gcinterval 一般设置会比 expire 小, 这个值应配合 expire 选取合适值, 太小会导致频繁占用资源, 太大会导致封禁条目达到失效时间后还需要等待很久才会被删除.
失效时间到达后未被删除, 还是会被封禁.
查看 /proc/net/ipt_hashlimit/DNS 文件:
$ cat /proc/net/ipt_hashlimit/DNS
180 X.X.X.X:0->0.0.0.0:0 32000 32000 32000
这里第一个字段是expire倒计时时间(单位是秒), 比如这里设置180000毫秒, 即180s, 如果180s内没有再次触发这个规则, 则会一直减到0 (见上面关于expire解释); 如果触发则再次变为180.
第二个字段是 srcip:port->dstip:port, 这里mode只设置了srcip
第三个字段是当前剩余的令牌数
第四个字段是令牌桶最大容量, 是一个定值
第五个字段是一次触发使用的令牌数, 也是令牌产生速率, 也是一个定值
而最大的令牌数就是 令牌产生速率 * {hashlimit-burst}
, 比如 2req/sec, burst是5, 则第四个字段就是 32000/2*5 = 80000
第三个字段每触发一次规则, 都会减去 令牌产生速率 * 1
个令牌, 并以这个速率恢复. 如果长时间没有触发, 会一直处于和最大令牌数一样的值.
关于hashlimit的匹配结果: 当查询包进来时, 如果令牌足够, 则会减去一次令牌数, 接着恢复, 且接着去下一条规则; 如果在剩余的令牌不足以减去一次查询的令牌, 则匹配这条hashlimit规则, target是DROP时, 则丢弃这个包.
模拟DNS攻击, 查看第三个字段的值, 发现两条规则时, 就是减少两次令牌, 因为一次会减少32000个令牌, 两次减少64000个, 而令牌桶的最大数目是32000, 也就是说这是一个永远无法完成的操作, 当然也就会造成一种完全封禁的情况.
实验测试中, 比如把速率改为 2seq/sec, burst改为3, 一遍dig一遍抓包并查看/proc/net/ipt_hashlimit/DNS文件, 可以看到当令牌不够时, 匹配这个域名后的包确实丢掉了.
简单小结下: 开头的这个规则, 主要就是 hashlimit-above 和 hashlimit-burst 这两个参数的设置. 首先匹配上域名, 然后hashlimit会新建一个entry, 用令牌桶管理包速. hashlimit-above 决定了一秒允许多少个包经过, 相应也就是令牌产生的速率, hashlimit-burst决定令牌桶的最大容量, 如果查询包超过这个限制(令牌桶剩余令牌不够), 则匹配上这条规则, DROP掉包, 否则包继续进入下一条规则查看是否匹配.