TCP洪水攻擊(SYN Flood)的診斷和處理

1. SYN Flood介紹

   前段時間網站被攻擊屢次,其中最猛烈的就是TCP洪水攻擊,即SYN Flood。html

   SYN Flood是當前最流行的DoS(拒絕服務攻擊)與DDoS(分佈式拒絕服務攻擊)的方式之一,這是一種利用TCP協議缺陷,發送大量僞造的TCP鏈接請求,經常使用假冒的IP或IP號段發來海量的請求鏈接的第一個握手包(SYN包),被攻擊服務器迴應第二個握手包(SYN+ACK包),由於對方是假冒IP,對方永遠收不到包且不會迴應第三個握手包。致使被攻擊服務器保持大量SYN_RECV狀態的「半鏈接」,而且會重試默認5次迴應第二個握手包,塞滿TCP等待鏈接隊列,資源耗盡(CPU滿負荷或內存不足),讓正常的業務請求鏈接不進來。linux

   詳細的原理,網上有不少介紹,應對辦法也不少,但大部分沒什麼效果,這裏介紹咱們是如何診斷和應對的。web

2. 診斷

   咱們看到業務曲線大跌時,檢查機器和DNS,發現只是對外的web機響應慢、CPU負載高、ssh登錄慢甚至有些機器登錄不上,檢查系統syslog:後端

   # tail -f /var/log/messages數組

   Apr 18 11:21:56 web5 kernel: possible SYN flooding on port 80. Sending cookies.bash

   檢查鏈接數增多,而且SYN_RECV 鏈接特別多:服務器

   # netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'cookie

   TIME_WAIT 16855網絡

   CLOSE_WAIT 21併發

   SYN_SENT 99

   FIN_WAIT1 229

   FIN_WAIT2 113

   ESTABLISHED 8358

   SYN_RECV 48965

   CLOSING 3

   LAST_ACK 313

   根據經驗,正常時檢查鏈接數以下:

   # netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

   TIME_WAIT 42349

   CLOSE_WAIT 1

   SYN_SENT 4

   FIN_WAIT1 298

   FIN_WAIT2 33

   ESTABLISHED 12775

   SYN_RECV 259

   CLOSING 6

   LAST_ACK 432

   以上就是TCP洪水攻擊的兩大特徵。執行netstat -na>指定文件,保留罪證。

3. 應急處理

   根據netstat查看到的對方IP特徵:

   # netstat -na |grep SYN_RECV|more

   利用iptables臨時封掉最大嫌疑攻擊的IP或IP號段,例如對方假冒173.*.*.*號段來攻擊,短時間禁用173.*.*.*這個大號段(要確認當心不要封掉本身的本地IP了!)

   # iptables -A INPUT -s  173.0.0.0/8  -p tcp  -dport 80 -j DROP

   再分析剛纔保留的罪證,分析業務,用iptables解封正常173.*.*.*號段內正常的ip和子網段。這樣應急處理很容易誤傷,甚至可能由於封錯了致使ssh登錄不了服務器,並非理想方式。

4. 使用F5擋攻擊    

   應急處理畢竟太被動,由於本機房的F5比較空閒,運維利用F5來擋攻擊,採用方式:讓客戶端先和F5三次握手,鏈接創建以後F5才轉發到後端業務服務器。後來被攻擊時F5上看到的現象:

   1. 鏈接數比平時多了500萬,攻擊中止後恢復。

   2. 修改F5上咱們業務的VS模式後,F5的CPU消耗比平時多7%,攻擊中止後恢復。

   3. 用F5擋效果明顯,後來因攻擊無效後,用戶不多來攻擊了,畢竟攻擊也是有成本的。

5. 調整系統參數擋攻擊    

   沒有F5這種高級且昂貴的設備怎麼辦?我測試過如下參數組合能明顯減少影響,準備之後不用F5抗攻擊。

   第一個參數tcp_synack_retries = 0是關鍵,表示迴應第二個握手包(SYN+ACK包)給客戶端IP後,若是收不到第三次握手包(ACK包)後,不進行重試,加快回收「半鏈接」,不要耗光資源。

   不修改這個參數,模擬攻擊,10秒後被攻擊的80端口即沒法服務,機器難以ssh登陸; 用命令netstat -na |grep SYN_RECV檢測「半鏈接」hold住180秒;

   修改這個參數爲0,再模擬攻擊,持續10分鐘後被攻擊的80端口均可以服務,響應稍慢些而已,只是ssh有時也登陸不上;檢測「半鏈接」只hold住3秒即釋放掉。

   修改這個參數爲0的反作用:網絡情況不好時,若是對方沒收到第二個握手包,可能鏈接服務器失敗,但對於通常網站,用戶刷新一次頁面便可。這些能夠在高峯期或網絡情況很差時tcpdump抓包驗證下。

   根據之前的抓包經驗,這種狀況不多,但爲了保險起見,能夠只在被tcp洪水攻擊時臨時啓用這個參數。

   tcp_synack_retries默認爲5,表示重發5次,每次等待30~40秒,即「半鏈接」默認hold住大約180秒。詳細解釋:

The tcp_synack_retries setting tells the kernel how many times to retransmit the SYN,ACK reply to

   an SYN request. In other words, this tells the system how many times to try to establish a passive

   TCP connection that was started by another host.

   This variable takes an integer value, but should under no circumstances be larger than 255 for the

   same reasons as for the tcp_syn_retries variable. Each retransmission will take aproximately 30-40

   seconds. The default value of the tcp_synack_retries variable is 5, and hence the default timeout

   of passive TCP connections is aproximately 180 seconds.

   之因此能夠把tcp_synack_retries改成0,由於客戶端還有tcp_syn_retries參數,默認是5,即便服務器端沒有重發SYN+ACK包,客戶端也會重發SYN握手包。詳細解釋:

The tcp_syn_retries variable tells the kernel how many times to try to retransmit the initial SYN

   packet for an active TCP connection attempt.

   This variable takes an integer value, but should not be set higher than 255 since each

   retransmission will consume huge amounts of time as well as some amounts of bandwidth. Each

   connection retransmission takes aproximately 30-40 seconds. The default setting is 5, which

   would lead to an aproximate of 180 seconds delay before the connection times out.

   第二個參數net.ipv4.tcp_max_syn_backlog = 200000也重要,具體多少數值受限於內存。

   如下配置,第一段參數是最重要的,第二段參數是輔助的,其他參數是其餘做用的:

   # vi /etc/sysctl.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#最關鍵參數,默認爲5,修改成0 表示不要重發
net.ipv4.tcp_synack_retries = 0
#半鏈接隊列長度
net.ipv4.tcp_max_syn_backlog = 200000
 
#系統容許的文件句柄的最大數目,由於鏈接須要佔用文件句柄
fs.file-max = 819200
#用來應對突發的大併發connect 請求
net.core.somaxconn = 65536
#最大的TCP 數據接收緩衝(字節)
net.core.rmem_max = 1024123000
 
#最大的TCP 數據發送緩衝(字節)
net.core.wmem_max = 16777216
#網絡設備接收數據包的速率比內核處理這些包的速率快時,容許送到隊列的數據包的最大數目
net.core.netdev_max_backlog = 165536
#本機主動鏈接其餘機器時的端口分配範圍
net.ipv4.ip_local_port_range = 10000 65535
 
# ……省略其它……

   使配置生效:

   # sysctl -p

   注意,如下參數面對外網時,不要打開。由於反作用很明顯,具體緣由請google,若是已打開請顯式改成0,而後執行sysctl -p關閉。由於通過試驗,大量TIME_WAIT狀態的鏈接對系統沒太大影響:

1
2
3
4
5
6
7
8
#當出現 半鏈接 隊列溢出時向對方發送syncookies,調大 半鏈接 隊列後不必
net.ipv4.tcp_syncookies = 0
#TIME_WAIT狀態的鏈接重用功能
net.ipv4.tcp_tw_reuse = 0
#時間戳選項,與前面net.ipv4.tcp_tw_reuse參數配合
net.ipv4.tcp_timestamps = 0
#TIME_WAIT狀態的鏈接回收功能
net.ipv4.tcp_tw_recycle = 0

   爲了處理大量鏈接,還需改大另外一個參數:

   # vi /etc/security/limits.conf

   在底下添加一行表示容許每一個用戶都最大可打開409600個文件句柄(包括鏈接):

   *                -       nofile          409600

6. 參考資料    

   文件句柄不要超過系統限制/usr/include/linux/fs.h,相關連接:http://blog.yufeng.info/archives/1380

   #define NR_OPEN (1024*1024)     /* Absolute upper limit on fd num */

   內核參數詳細解釋:http://www.frozentux.net/ipsysctl-tutorial/chunkyhtml/tcpvariables.html

   

7. 結束語    

   TCP洪水攻擊還沒完美解決方案,但願本文對您有所幫助,讓您快速瞭解。

相關文章
相關標籤/搜索