看RHEL7如何防御TCP SYN Flood攻击
DDOS(Distributed Denial of Service)即分布式拒绝服务攻击,在这个互联网高度发达的年代已经是变成了一个老生长谈的话题,DDOS攻击中非常有代表性的一类攻击是SYN-flood攻击,这种攻击通过耗尽系统资源,使其出现性能瓶颈以达到拒绝服务的目的,攻击最终导致服务器无法响应任何新的连接请求。
在最近的DevConf.cz 2014会议上,本文作者分享了一个如何利用新版本内核的netfilter/iptables中实现的防御机制来实现对TCP-SYN-flooding的防御的议题,此文将是该议题的一个浓缩版,向大家展示如何利用RHEL7中相关的特性来实现对攻击的防御。
这里需要提醒大家的是,尽管在RHEL7中已经包含了对此特性,但默认是处于关闭状态的,要让其发挥作用必须先开启若干设置以及添加一些额外的iptables规则才能启用。
内核与SYN-flood有何恩怨?
对于内核来说,最起码的TCP可伸缩性问题表现在每秒创建的最大连接数上,这与每个socket连接常见时产生的“listen”lock有直接关系,而对于“established”状态的连接来说,可以很好的伸缩。
“listen”lock不仅仅是在处理SYN包时会遇到,且在其他初始连接态包如SYN-ACK和ACK包(3次握手的最后一个)时也会遇到。在flooding攻击时,攻击者就是利用发送伪造的包来让”listen”lock出现故障。据此,我们需要构建一种机制,在socket进入”listen”状态并拒绝接入新的连接前,过滤掉这些虚假的初始连接请求。
基本的conntrack过滤
利用netfilter的连接跟踪系统(conntrack)我们可以实现当socket进入listen状态之前,就把错误的SYN-ACK和ACK包阻挡掉。这个功能已经由来已久了,但是默认并没有被启用。
开启此功能仅仅需要两条命令:
iptables -A INPUT -m state --state INVALID -j DROP /bin/sysctl -w net/netfilter/nf_conntrack_tcp_loose=0
第一条iptables规则用来捕捉在conntrack系统中被归类为“INVALID”,不属于任何已知状态的连接。
第二条sysctl设置使conntrack对此分类的连接更加严格,具体来讲此设置(排除其他因素)将有助于ACK-floods的捕捉。
conntrack性能如何?
其结果是在处理基于SYN-ACK和ACK的攻击时有20倍的性能提升。
netfilter的conntrack在很久以前确实由于性能不好而没啥好的口碑,现如今它能够对“established”状态的连接表现出极其惊艳的伸缩性。
针对既存的conntrack条目来说,它不但速度快而且完全的可伸缩,conntrack系统事实上已经做到针对既存连接的无锁RCU(read-copy update)查询。
为什么它解决不了SYN-flooding?
实际上,连接跟踪系统也和之前讲到的“listen”lock面临一样的窘境,即它仍然需要面临连接的创建(或者删除)问题。即便是在解决掉conntrack lock之后,SYN包仍然被继续发送至socket进而导致“listen” lock被触发。常规的缓解技术是发送SYN-cookies来阻止产生任何的state,直到收到SYN-ACK包为止。
RHEL7中有何新招?
在参加Netfilter workshop 2013会议时,本文作者和Patrick McHardy,Martin Topholm三人一起,提出了称为“基于网络的对抗措施”的想法,通过在防火墙层面解决SYN flooding问题,这直接促成了后来的SYNPROXY iptables模块以及与之相关的netfilter核心的改动,这些特性现在被包含在RHEL7中。
SYNPROXY模块可以用来解决前面提到的两方面的伸缩性问题。首先它使用了并行SYN-cookies技术,其次,在SYN-ACK包被接收之前并不会创建conntrack条目,因此,避免了新建连接时的锁问题。
SYNPROXY模块可以用作本机以及对防火墙后的网络的防护,一旦连接初始化完成,正常的conntrack系统将接管连接状态并且操作所有必要的转换(重用了部分NAT功能的代码)。
测试表明在处理SYN攻击中有10倍的性能提升表现。
配置SYNPROXY
如果不看任何帮助文档的话,SYNPROXY配置起来算是比较复杂的,所以这里将一步一步的引导大家如何配置(或者也可以使用这个脚本来简化步骤)
下面将以保护web服务为例,我们先声明一下export PORT=80
第一步:在raw表中增加规则以避免为SYN包建立conntrack。
iptables -t raw -I PREROUTING -i $DEV -p tcp -m tcp --syn --dport $PORT -j CT --notrack
第二步:开启严格conntracking模式,必须这样设置才能保证将三次握手中的ACK包标记为INVALID状态。
/sbin/sysctl -w net/netfilter/nf_conntrack_tcp_loose=0
第三步:现在我们需要捕捉这些包并把他们重定向到SYNPROXY模块中,使用下面的规则来捕捉UNTRACKED SYN和包含有ACK INVALID状态的包。
iptables -I INPUT -i $DEV -p tcp -m tcp --dport $PORT \ -m state --state INVALID,UNTRACKED -j SYNPROXY \ --sack-perm --timestamp --wscale 7 --mss 1460
第四步:捕捉通过SYNPROXY的INVALID状态的包并丢弃之,这会丢掉基于SYN-ACK的floods。
iptables -A INPUT -m state --state INVALID -j DROP
第五步:记得还要打开TCP时间戳用于SYN cookies,充分利用这个TCP选项。
/sbin/sysctl -w net/ipv4/tcp_timestamps=1
第六步:如果是高负载的服务器,还应该优化以下参数以提高默认的64k连接的限制,另外增加conntrack hash size对性能来说也至关重要。
echo 2000000 >/sys/module/nf_conntrack/parameters/hashsize /sbin/sysctl -w net/netfilter/nf_conntrack_max=1000000
一定要按照你自己的情况来设置并计算内存的消耗,在这个例子中,2000000条目乘以288 bytes = 最大576.0MB的基准内存消耗,对于hash,每个hash表“头”只占用8bytes乘以1000000也就8MB的内存大小(设置此参数时还要注意你的CPU L3缓存大小)。
使用SYNPROXY模块时的注意事项
启用SYNPROXY模块是会带来额外开销的。由于额外增加的连接检查将导致连接的建立过程有所减慢。如果连接目标是本机,那么这些额外的操作显然是很快就可以完成的,但尽管如此也仍然避免不了会增加延迟。
SYNPROXY target模块的参数必须与TCP选项保持一致,且和被代理TCP连接的目标机器的设置相匹配。检查和设置都是靠手工完成。(一个辅助工具“nfsynproxy”作为iptables 1.4.21版本的一部分发布)。不幸的是在基于DHCP的防火墙环境中部署起来并不方便。
今后的计划是支持目标机器的TCP选项自适应,这个已经记录在RHBZ1059679中,该问题正在等待来自用户的需求反馈。
了解更多信息请查看作者在DevConf.cz 2014的题为“用Netfilter/iptables进行DDOS防护”的议题,也可以观看视频或者观看slides。
原文地址: