天天看点

SYN flooding处理--内核调优

    实际生产环境中出现syn flooding的情况好多次了,之前虽然解决了,但一直没好好整理一下。直到上周五又出现该问题,这次利用周末的空闲时间好好查阅资料研究了翻,整理一篇博文。想说的是,养成写博客或者整理文档的习惯真的很重要,因为当你把它写成一篇文章的时候,首先你自己必须要先理解,然后思路必须要清晰,有时候可能需要花上一整天的时间查资料,对自己也是一种提高,才算是对自己和读者的负责。

    先介绍下什么是syn flooding。

  syn flood是当前最流行的dos(拒绝服务攻击)与ddos(分布式拒绝服务攻击)的方式之一,这是一种利用tcp协议缺陷,发送大量伪造的tcp连接请求,塞满tcp等待连接队列,导致资源耗尽(cpu满负荷或内存不足),让正常的业务请求连接不进来,从而间接达到攻击的目的。

  说起tcp协议,不得不提三次握手(three-way handshake),之前文章‘理解tcp三次握手--大白话’有介绍,记不起来的可以先看一下。syn flood攻击利用的正是ipv4中tcp协议的三次握手过程进行的攻击。如果一端想向另一端发起tcp连接,它需要首先发送tcp syn 包到对方,对方收到后发送一个tcp syn+ack包回来,发起方再发送tcp ack包回去,这样三次握手就结束了。我们把tcp连接的发起方叫作"tcp客户机(tcp client)",tcp连接的接收方叫作"tcp服务器(tcp server)"。值得注意的是在tcp服务器收到tcp syn request包时,在发送tcp syn+ack包回tcp客户机前,tcp服务器要先分配好一个数据区专门服务于这个即将形成的tcp连接。一般把收到syn包而还未收到ack包时的连 接状态成为半开连接(half-open connection)。

  在最常见的syn flood攻击中,攻击者在短时间内发送大量的tcp syn包给受害者,这时攻击者是tcp客户机,受害者是tcp服务器。根据上面的描述,受害者会为每个tcp syn包分配一个特定的数据区,只要这些syn包具有不同的源地址(这一点对于攻击者来说是很容易伪造的)。这将给tcp服务器系统造成很大的系统负担, 最终导致系统不能正常工作。

  那如何防御呢?

  通常会把tcp cookie的功能打开。那又为什么发送cookie呢?这个cookie是什么呢?

  这个cookie是指syn cookie。在目前以ipv4为支撑的网络协议上搭建的网络环境中,syn flood是一种非常危险而常见的dos攻击方式。到目前为止,能够有效防范syn flood攻击的手段并不多,而syn cookie就是其中最著名的一种。syn cookie原理由d.j.bernstain和eric schenk发明。在很多操作系统上都有各种各样的实现。其中包括linux。

 我查了kernel documention,里面有syn_cookies的官方介绍:

  官方明确说明了当看到有syn flood warning的时候并不一定真的是flooded,有可能是你的服务器没有正确的配置。同理,有时你的服务器有此报警的时候也并不一定是真的有攻击,这时就需要你调整你的内核参数。

  上张图,用dmesg或者在系统的syslog里能够查看有关syn flooding的报错信息:

SYN flooding处理--内核调优

  内核调优

  上面官方文档中也提到当出现此问题的时候,可以调整内核的三个参数:tcp_max_syn_backlog, tcp_synack_retries, tcp_abort_on_overflow。

tcp_max_syn_backlog变量告诉你在内存中可以缓存多少个syn请求。该变量需要打开tcp_syncookies才有效。如果服务器负载很高,可以尝试提高该变量的值。

tcp_synack_retries变量用于tcp三次握手机制中第二次握手,当收到客户端发来的syn连接请求后,服务端将回复syn+ack包,这时服务端处于syn_rcvd状态,并等 待客户端发来的回复ack包。如果服务端没有收到客户端的ack包,会重新发送syn+ack包,直到收到客户端的ack包。该变量设置发送 syn+ack包的次数,超过这个次数,服务端将放弃连接。默认值是5。

tcp_abort_on_overflow变量的值是个布尔值,默认值为0(false关闭)。如果开启,当服务端接收新连接的速度变慢时,服务端会发送rst包(reset包)给客户端,令客户端 重新连接。这意味着如果突然发生溢出,将重获连接。仅当你真的确定不能通过调整监听进程使接收连接的速度变快,可以启用该选项。该选项会影响到客户的连接。

  实际测试,只更改这几个参数有时是不管用的。解决办法如下:

我目前的解决办法是调整以下几个内核参数,实践证明调整之后暂时未再复现问题。而官方文档中提到的retry和overflow的两个参数,建议还是不要调整,用默认的就好,以免产生副作用。

参数说明如下:

net.ipv4.tcp_syncookies = 1

#表示开启syn cookies。当出现syn等待队列溢出时,启用cookies来处理,可防范少量syn攻击,默认为0,表示关闭;

net.ipv4.tcp_tw_reuse = 1

#表示开启重用。允许将time-wait sockets重新用于新的tcp连接,默认为0,表示关闭;为1,开启;

net.ipv4.tcp_tw_recycle = 1

#表示开启tcp连接中time-wait sockets的快速回收,默认为0,表示关闭;为1,开启;

net.ipv4.tcp_fin_timeout

#修改系統默认的 timeout 时间,这里根据服务器的实际情况设置。

  另外细心的朋友可能发现了,报错信息: possible syn flooding on port 13370. sending cookies.后面跟了句"check snmp counters"。这句我当时差点被误导,因为我的服务器上正好跑了一个snmp抓流量的服务,开始以为是它导致的,后来一想那是udp的协议,和tcp没关系呀。查了kernel的代码发现,原来那是print打印的固定info输出: