分类:技术分享

看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)查询。

 
所以,从本质上讲它解决了全部的TCP-flooding包,但是,除SYN-flooding以外。

 

为什么它解决不了SYN-flooding?

实际上,连接跟踪系统也和之前讲到的“listen”lock面临一样的窘境,即它仍然需要面临连接的创建(或者删除)问题。即便是在解决掉conntrack lock之后,SYN包仍然被继续发送至socket进而导致“listen” lock被触发。常规的缓解技术是发送SYN-cookies来阻止产生任何的state,直到收到SYN-ACK包为止。

然而不幸的是,SYN-cookies是在同样的“listen”状态锁下发送的,所以这样的缓解方式并不能根本上解决伸缩性问题,如何围绕这些限制做工作将在后面讨论。
 


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

 

 




原文地址:

http://rhelblog.redhat.com/2014/04/11/mitigate-tcp-syn-flood-attacks-with-red-hat-enterprise-linux-7-beta/
 
延伸阅读:
http://www.study-area.org/tips/syn_flood.htm
http://blog.aliyun.com/232
http://blog.aliyun.com/243
http://blog.aliyun.com/250