1、 SYN Flood介紹
前段時間網站被攻 擊多次,其中最猛烈的就是TCP洪水攻擊,即SYN Flood。
SYN Flood是當前最流行的DoS(拒絕服務攻擊)與DDoS(分布式拒絕服務攻擊)的方式之一,這是一種利用TCP協議缺陷,發送大量偽造的TCP連接請求,常用假冒的IP或IP號段發來海量的請求連接的第一個握手包(SYN包),被攻擊服務器回應第二個握手包(SYN+ACK包),因為對方是假冒IP,對方永遠收不到包且不會回應第三個握手包。導致被攻擊服務器保持大量SYN_RECV狀態的“半連接”,并且會重試默認5次回應第二個握手包,塞滿TCP等待連接隊列,資源耗盡(CPU滿負荷或內存不足),讓正常的業務請求連接不進來。
詳細的原理,網上有很多介紹,應對辦法也很多,但大部分沒什么效果,這里介紹我們是如何診斷和應對的。
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.
檢查連接數增多,并且SYN_RECV 連接特別多:
# netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' 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秒。
之所以可以把tcp_synack_retries改為0,因為客戶端還有tcp_syn_retries參數,默認是5,即使服務器端沒有重發SYN+ACK包,客戶端也會重發SYN握手包。
第二個參數 net.ipv4.tcp_max_syn_backlog = 200000也重要,具體多少數值受限于內存。
以下配置,第一段參數是最重要的,第二段參數是輔助的,其余參數是其他作用的:
# vi /etc/sysctl.conf
#最關鍵參數,默認為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狀態的連接對系統沒太大影響:
#當出現 半連接 隊列溢出時向對方發送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
基本防護策略就這些啦。