【原创】线上环境 SYN flooding 问题排查

问题描述: 
      线上环境中,公司自研即时通讯软件不定时掉线。 

问题排查: 
      由运维和测试人员发现并报告,线上环境出现网络异常,具体表现为登录服务器虚拟 IP 地址无法 ping 通,即时通讯工具不定时掉线; 
      在此情况下,现场人员第一反应就是受到了外部攻击(因为以前遇到过攻击情况),因为看到了如下信息 

...
Apr 20 18:21:48 localhost kernel: possible SYN flooding on port 80. Sending cookies.
Apr 20 18:24:37 localhost kernel: possible SYN flooding on port 80. Sending cookies.
Apr 20 18:25:50 localhost kernel: possible SYN flooding on port 80. Sending cookies.
Apr 20 18:27:02 localhost kernel: possible SYN flooding on port 80. Sending cookies.
Apr 20 18:29:01 localhost kernel: possible SYN flooding on port 80. Sending cookies.
Apr 20 18:30:14 localhost kernel: possible SYN flooding on port 80. Sending cookies.
Apr 20 18:31:28 localhost kernel: possible SYN flooding on port 80. Sending cookies.
Apr 20 18:32:44 localhost kernel: possible SYN flooding on port 80. Sending cookies.
Apr 20 18:35:33 localhost kernel: possible SYN flooding on port 80. Sending cookies.
Apr 20 18:37:06 localhost kernel: possible SYN flooding on port 80. Sending cookies.
Apr 20 18:37:52 localhost ntpd_intres[1732]: host name not found: 0.centos.pool.ntp.org
Apr 20 18:38:12 localhost ntpd_intres[1732]: host name not found: 1.centos.pool.ntp.org
Apr 20 18:38:20 localhost kernel: possible SYN flooding on port 80. Sending cookies.
Apr 20 18:38:32 localhost ntpd_intres[1732]: host name not found: 2.centos.pool.ntp.org
Apr 20 18:38:52 localhost ntpd_intres[1732]: host name not found: 3.centos.pool.ntp.org
Apr 20 18:39:29 localhost kernel: possible SYN flooding on port 80. Sending cookies.
Apr 20 18:40:43 localhost kernel: possible SYN flooding on port 80. Sending cookies.
...

以及抓包内容 
 
经过研发人员的初步排查,排除了遭受外部攻击的可能,因为发现 

  • 大量的 HTTP 请求均来自属于公司终端设备的地址;
  • 从抓包中可以看到,问题终端大约每秒会发送 1000 个 TCP SYN 包(实际发现存在不止一个问题终端);
  • nginx 未进行 backlog 配置,即使用的是 nginx 默认值 NGX_LISTEN_BACKLOG ,即 511;
  • 后台检测脚本基于 nginx 的 /status 页面进行 HTTP 状态检测,会得到 500 状态码;
  • 后台检测脚本在 3 次检测异常后,主动触发虚地址切换,在切换过程中通过虚拟 IP 地址进行访问的服务无法正常使用(这个是正常情况);

综上,基本上可以断定问题的原因为: 

  • 问题终端会在一定场景下不断发起 TCP SYN 请求,导致看似发生了 DOS 攻击行为;
  • 系统内核参数以及 nginx 配置参数均未进行过优化调整,存在性能瓶颈;

当时的 系统配置为: 

  • net.core.somaxconn = 128 
  • net.ipv4.tcp_max_syn_backlog = 2048
  • net.ipv4.tcp_syncookies = 1

后续再邮件讨论中,某大牛给出了如下结论: 

平台开启了 SYN 攻击检测,故平台会认为 80 端口受到 SYN flood 攻击,而在这种情况下连正常的 keepalive 心跳检测也会受到影响。昨天的问题,是因为 SYN 攻击检测导致的,不是 TCP SYN 缓存队列占满的原因。

      上述结论刚给出的时候,我没有提出任何异议(因为哥也没深入研究过~)。然而经过后续研究,我发现上述结论实际上是存在问题的: 

  • 针对 keepalive 来说,正如其他内核参数没有调整过一样,net.ipv4.tcp_keepalive_time 也一样没有进行过调整,而且一般也不建议做调整,可以查看默认值为 7200 秒;而这个时间长度在面临类似 SYN flood 攻击行为时,肯定已经可以不考虑 keepalive 问题了;
  • 针对 TCP SYN 队列是否占满的问题,从相关资料或源码中可以看到“只有在 SYN 队列已满的情况下才会触发 SYN cookies 机制”,所以上面的说法其实是错误的;

       虽然上述结论存在一定偏差,但对问题的整体推进还是有好处的,而 如何进行问题修复其实比较简单,因为在这次事件中,可以很明显的看出“主犯”是问题终端,而未经优化的内核参数以及 nginx 配置则是“从犯”,所以,优先枪决“主犯”,基本上就能解决问题了。而“从犯”理论上讲是可以缓刑处理的。 

随着排查的展开,陆续又有更进一步的结论产生: 

  • nginx 在转发终端请求时,存在转发到错误地址的情况(这个问题的原因未知),进而导致终端获取 token 值失效。
  • 同时,根据终端逻辑,其会不断重新建链进行 token 获取,所以在抓包中才看到终端一直在持续发送大量的 tcp 包(其实终端的重连逻辑中还存在更大的问题,此处就呵呵吧)。

另外在问题复现过程中,还抓到了如下内容 
 
 
可以看到,在抓包最开始的时候,并非只有终端发起的 SYN 包,而是经历了  SYN->SYN,ACK->RST 过程;在运行了一段时间后,才变成了只有 SYN 包被发送,而没有其他回应的。 

上述抓包提供了很高的价值,经过排查得到了以下结论: 
      终端在发送 SYN 后,会在另外的线程中启动定时器对当前 fd 是否可写进行超时判定(据说为 10 秒),在特定情况下(由于内核参数没有进行过调整,所以应该很容易达到所谓的特定情况 ),会触发此超时,导致业务层认为当前连接未建立成功,于是通过 close 关闭该 socket 。另外,由于此时并未成功建立 TCP 连接,故客户端侧协议栈不会发送 FIN 包。而之后当收到来自服务器端的 SYN,ACK 时(因为服务器侧并不知道当前连接已经被关闭 ),则直接由客户端底层 TCP 协议栈回复 RST 。 

问题到此已经得到了解决,而此时还剩最后一个问题:线上问题是如何被触发的?按道理说应该一直存在该问题的呀~~ 
      结论大致如下(推断出来的):nginx 所在机器在未经过内核参数优化的情况下,能够处理一定量的并发连接,在请求量不大,每个请求的访问时间较短的情况下,是能够正常提供服务的。由于近期存在一些其他服务的版本升级,怀疑部分业务的请求处理耗时有所增长,导致请求处理速度的整体下降,另外由于针对 nginx 的状态 检测脚本本身也是依赖 HTTP 接口进行的状态检测,也就必然会导致连接资源的紧张,而一旦检测失败后进行虚地址切换,又会导致问题的加剧。综上,导致了最终上述问题的爆发。 

相关参数说明:

man 2 listen 

...
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int listen(int sockfd, int backlog);

...
       The behavior of the backlog argument on TCP sockets changed with Linux 2.2.
  Now it specifies the queue length for completely established sockets waiting to be accepted, instead of the number of incomplete connection requests.
  The maximum length of the queue for incomplete sockets can be set using /proc/sys/net/ipv4/tcp_max_syn_backlog.
  When syncookies are enabled there is no logical maximum length and this setting is ignored.
  See tcp(7) for more information.

       If the backlog argument is greater than the value in /proc/sys/net/core/somaxconn, then it is silently truncated to that value;
  the default value in this file is 128. In kernels before 2.4.25, this limit was a hard coded value, SOMAXCONN, with the value 128.

man 7 tcp 

tcp_abort_on_overflow (Boolean; default: disabled; since Linux 2.4)
   Enable resetting connections if the listening service is too slow and unable to keep up and accept them.
   It means that if overflow occurred due to a burst, the connection will recover.
   Enable this option only if you are really sure that the listening daemon cannot be tuned to accept connections faster.
   Enabling this option can harm the clients of your server.
...
tcp_max_syn_backlog (integer; default: see below; since Linux 2.2)
   The maximum number of queued connection requests which have still not received an acknowledgement from the connecting client.
   If this number is exceeded, the kernel will begin dropping requests.
   The default value of 256 is increased to 1024 when the memory present in the system is adequate or greater (>= 128Mb),
   and reduced to 128 for those systems with very low memory (<= 32Mb).
   It is recommended that if this needs to be increased above 1024, TCP_SYNQ_HSIZE in include/net/tcp.h be modified to keep TCP_SYNQ_HSIZE*16<=tcp_max_syn_backlog, and the kernel be recompiled.
...
tcp_synack_retries (integer; default: 5; since Linux 2.2)
   The maximum number of times a SYN/ACK segment for a passive TCP connection will be retransmitted.
   This number should not be higher than 255.

tcp_syncookies (Boolean; since Linux 2.2)
   Enable TCP syncookies.
   The kernel must be compiled with CONFIG_SYN_COOKIES.
   Send out syncookies when the syn backlog queue of a socket overflows.
   The syncookies feature attempts to protect a socket from a SYN flood attack.
   This should be used as a last resort, if at all.
   This is a violation of the TCP protocol, and conflicts with other areas of TCP such as TCP extensions.
   It can cause problems for clients and relays.
   It is not recommended as a tuning mechanism for heavily loaded servers to help with overloaded or misconfigured conditions.
   For recommended alternatives see tcp_max_syn_backlog, tcp_synack_retries, and tcp_abort_on_overflow.
...
时间: 2024-11-16 00:08:55

【原创】线上环境 SYN flooding 问题排查的相关文章

一则线上MySql连接异常的排查过程

Mysql作为一个常用数据库,在互联网系统应用很多.有些故障是其自身的bug,有些则不是,这里以前段时间遇到的问题举例. 问题 当时遇到的症状是这样的,我们的应用在线上测试环境,JMeter测试过程中,发现每次压力测试开始时访问低前几个http request请求会超时,而之后的请求持续测试中都不会.最后一点是Tomcat的log并没有报什么错误. 压测的内容就是起200线程不停的向这个http页面发送请求,这个页面逻辑也比较简单,会在后端向数据库插入一条数据,连接池采用阿里的Druid(这个坑

小程序如何判断测试环境和线上环境

因测试环境与线上环境的域名不同,上线时每次都需要该域名配置,是否有啥方法可以判断小程序当前运行环境呢? 用wx.getSystemInfoSync()判断systemInfo.platform == 'devtools'

Linux集群和自动化维3.7.2 线上环境中的Fabric应用实例

3.7.2 线上环境中的Fabric应用实例 笔者线上的核心业务机器统一都是AWS EC2主机,机器数量较多,每个数据中心都部署了Fabric跳板机(物理拓扑图可参考图3-3),系统为Amazon Linux,内核版本为3.14.34-27.48.amzn1.x86_64,Python版本为Python 2.6.9. 如果公司项目组核心开发人员离职,线上机器就都要更改密钥,由于密钥一般是以组的形式存在的,再加上机器数量繁多,因此单纯通过技术人员手工操作,基本上是一项不可能完成的任务,但若是通过F

利用硬链接和truncate降低drop table对线上环境的影响

作者简介 肖鹏 微博研发中心数据库技术负责人,主要负责微博数据库(MySQL/Reids/HBase/Memcached)相关的业务保障,性能优化,架构设计以及周边的自动化系统建设.10年互联网数据库架构和管理经验,专注于数据库的高性能和高可用技术保障方向. 众所周知drop table会严重的消耗服务器IO性能,如果被drop的table容量较大,甚至会影响到线上的正常. 首先,我们看一下为什么drop容量大的table会影响线上服务 直接执行drop table,mysql会将表定义和表数据

mysql主从复制安装配置2 — 从线上环境配置主从

主机 172.16.0.21 172.16.0.22 centos6.2 分别使用yum安装mysql 给21上的mysql新建库 CREATE DATABASE `replytest1` /*!40100 DEFAULT CHARACTER SET utf8 */; 建表 CREATE TABLE `replytest1`.`test1` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `test1col` VARCHAR(45) NULL, PRIM

线上mysql数据库不停机的环境下如何添加新的从机

  在工作中,主从环境搭的多了,但是,基本上都是在DB SERVER停机(游戏公司)的情况下搭建的,今天突然被一技术官问,如何在线添加主从,回答的大概思路,但是没有实践过,下面,我就测试一下.各位也可以先想想自己的思路:mysql 5.1版本,二进制日志文件(时间长了,有些二进制日志定期清除了),pos号 注:在mysql 5.6版本中,已经有基于GTID的主从复制(即:不需要知道日志文件和position号),只需还原最新的备份就可实现,这里只讨论mysql 5.1 一.目前的基本环境: 主D

如何从开发环境直连线上(IPTables)

假如你在生产环境有一个内网可访问的端口,Let's say: 80,而且有生产机器的应用管理员权限.在这样的情况下,其实是可以做到从内网直接连接到线上环境任意端口的.链接SSH,链接数据库都不在话下.当然,安全性和便利性永远是不可调和的一对矛盾.为了避免有人用它来干坏事,我们也至少应当对这种方式有所了解. x00 环境描述 假设有生产环境机器10.x.x.1.可在内网通过VIP:123.123.123.1访问80端口. 坏蛋拥有线上环境sudo权限,现在希望在生产环境中直接访问任意端口. x01

适应各种开发,测试,线上,线下环境的Spring配置方式

背景 假设开发了一个中间件,比如是一个缓存系统,这个中间件要配置一个IP地址,还要配置一个Factory,从这个Factory里得到一个client,如: <bean name="cacheFactory" class="com.test.cache.Factory"> <property name="address" value="192.168.1.100"/> </bean> <

如何利用docker 构建golang线上部署环境

公司最近开发了一个项目是用golang 写的,现在要部署到线上环境去,又不想在服务器上装单独的golang,决定用docker 封装下,直接打到镜像里面,然后就直接在hub.docker.com上面搜了下golang的镜像,直接就docker pull golang 最新的是1.9的版本 然后参考官方的文档弄了下Dockerfile大概是这样:   FROM golang MAINTAINER jackluo #指定工作目录 WORKDIR /go/src/ActivitApi COPY . .