Quantcast
Channel: polygun2000的博客
Viewing all articles
Browse latest Browse all 55

使用Linux抵御TCP Flooding类型的DDoS攻击

$
0
0
我们做如下讨论的前提是你的带宽足够,如果带宽被打满了,也谈不上什么抵御了。

TCP Flooding类型的DDoS攻击,其原理是攻击TCP的3次握手过程,其主要目的是耗尽被攻击者的资源。

那么对于Linux内核来说,这种攻击是如何产生效果的呢?

对于Linux内核来说,每秒钟可以新创建多少个TCP连接直接与每一个进入"listen"状态的socket锁直接相关。
"listen"状态下的socket锁不止会在收到SYN包时会产生,在收到SYN-ACK,ACK(3次握手的最后一个包)时也会产生。
在SYN Flood攻击的时候,攻击者发送大量虚假的SYN包,促使被攻击者产生大量"listen"状态的socket锁,使系统资源耗尽,不能再产生新的TCP连接。可以参考下图的TCP状态机。



针对上述攻击的原理,我们需要一个方法来过滤这些虚假的数据包,在攻击包使系统进入"listen"状态前就将其DROP掉。

一、使用conntrack状态机制过滤SYN-ACK Flood和ACK Flood攻击

Linux内核中的conntrack状态机制,可以很有效的防范SYN-ACK和ACK类型的攻击。但是conntrack模块的低效一直是大家诟病的重点。

conntrack本身的查找效率其实是相当高的,所谓的低效是由于中心自旋锁"nf_conntrack_lock"导致的,即每插入或删除一条conntrack的entry,都会有锁产生,所以效率低下。
但是自3.15版本的内核开始,这个中心自旋锁已经被移除了。
其效率已经有了质的飞跃,所以再说conntrack效率低已经是老黄历了,具体数据对比见参考文档。

conntrack的状态机制,可以轻松处理除SYN Flood以外所有的TCP Flooding攻击。

具体使用如下两条命令:

第一个命令用来捕捉所有被系统认为是"INVALID"的数据包,予以丢弃
# iptables -A INPUT -m state --state INVALID -j DROP

第二条命令用来使conntrack系统判断更为严格,禁止ACK包产生新的连接
这个参数只对防范ACK Flood类型攻击有效果,对于SYN-ACK Flood类型攻击没有几乎没有影响。
# /sbin/sysctl -w net/netfilter/nf_conntrack_tcp_loose=0

二、使用SYNPROXY防范SYN Flood类型攻击

使用conntrack的状态机制不能阻止SYN Flood类型的攻击,因为即使去掉了nf_conntrack_lock中心锁,SYN类型的包依然会导致"listen"状态的socket锁产生。
现有的SYN-cookies方法不能有效解决这个问题,首先SYN-cookies做SHA计算的代价非常大,另外SYN-cookies本身依然是处于"listen" socket锁下被发送出去的。

Redhat的工程师Jesper Dangaard Brouer提出了SYNPROXY方法来解决这个问题。
其工作原理如下图:



使用SYNPROXY稍微有点复杂,如果不能理解,就按黑魔法的方法来使用吧(照抄的文雅说法)

第一步: 在raw这张表中,确保我们需要保护的连接的SYN包不会在conntrack中产生表项
# iptables -t raw -I PREROUTING -i $DEV -p tcp -m tcp --syn --dport $PORT -j CT --notrack

netfilter中,"raw"表可以看到原始的网络数据,其存在的主要目的就是为了针对特定的数据包关闭连接跟踪(conntrack)。"CT"这个target就是"conntrack",加上"--notrack" 选项的就表示不进行连接跟踪。

第二步: 设置严格的conntrack规则
# /sbin/sysctl -w net/netfilter/nf_conntrack_tcp_loose=0

第三步: 将INVALID(ACK)和UNTRACKED(SYN)状态的数据包导向SYNPROXY模块
# iptables -A INPUT -i $DEV -p tcp -m tcp --dport $PORT -m state --state INVALID,UNTRACKED -j SYNPROXY --sack-perm --timestamp --wscale 7 --mss 1460

第四步: 丢弃INVALID的数据包
# iptables -A INPUT -m state --state INVALID -j DROP

第五步: 打开内核TCP timestamps选项
# /sbin/sysctl -w net/ipv4/tcp_timestamps=1

第六步: 根据攻击强度,对conntrack做一些优化,这个可以参考我之前的blog文章
# echo 1000000 > /sys/module/nf_conntrack/parameters/hashsize
# /sbin/sysctl -w net/netfilter/nf_conntrack_max=2000000

本例中200万个连接大概占用内存为:  288Bytes*200W = 576MB
系统的hashsize,每个hash列表的占用8Bytes,100W个表项只占用8MB,这个hashsize的内存使用尽量小于你的CPU L3缓存大小。

第七步: 使用了上述方法以后,还是不能完全阻止攻击,因此我们必须提高攻击者的代价

使用hashlimit模块限制每个/24子网,每秒最多发起200个SYN连接

iptables -t raw -A PREROUTING -i $DEV \
 -p tcp -m tcp --dport 80 --syn \
 -m hashlimit \
 --hashlimit-above 200/sec --hashlimit-burst 1000 \
 --hashlimit-mode srcip --hashlimit-name syn \
 --hashlimit-htable-size 2097152 \
 --hashlimit-srcmask 24 -j DROP

从其他人的文档中扒了一张测试数据图,供参考。

参考文档
http://people.netfilter.org/hawk/presentations/nfws2014/nfws2014-conntrack-locking.pdf
http://people.netfilter.org/hawk/presentations/devconf2014/iptables-ddos-mitigation_JesperBrouer.pdf
http://rhelblog.redhat.com/2014/04/11/mitigate-tcp-syn-flood-attacks-with-red-hat-enterprise-linux-7-beta/
https://github.com/firehol/firehol/wiki/Working-with-SYNPROXY
https://r00t-services.net/knowledgebase/14/Homemade-DDoS-Protection-Using-IPTables-SYNPROXY.html
https://kb.isc.org/article/AA-01183/0/Linux-connection-tracking-and-DNS.html

 

Viewing all articles
Browse latest Browse all 55

Trending Articles