我们做如下讨论的前提是你的带宽足够,如果带宽被打满了,也谈不上什么抵御了。
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