天天看點

【原創】線上環境 SYN flooding 問題排查

問題描述: 

      線上環境中,公司自研即時通訊軟體不定時掉線。 

問題排查: 

      由運維和測試人員發現并報告,線上環境出現網絡異常,具體表現為登入伺服器虛拟 ip 位址無法 ping 通,即時通訊工具不定時掉線; 

      在此情況下,現場人員第一反應就是受到了外部攻擊(因為以前遇到過攻擊情況),因為看到了如下資訊 

以及抓包内容 

【原創】線上環境 SYN flooding 問題排查

經過研發人員的初步排查,排除了遭受外部攻擊的可能,因為發現 

大量的 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 flooding 問題排查
【原創】線上環境 SYN flooding 問題排查

可以看到,在抓包最開始的時候,并非隻有終端發起的 syn 包,而是經曆了  syn->syn,ack->rst 過程;在運作了一段時間後,才變成了隻有 syn 包被發送,而沒有其他回應的。 

上述抓包提供了很高的價值,經過排查得到了以下結論: 

      終端在發送 syn 後,會在另外的線程中啟動定時器對目前 fd 是否可寫進行逾時判定(據說為 10 秒),在特定情況下(由于核心參數沒有進行過調整,是以應該很容易達到所謂的特定情況 ),會觸發此逾時,導緻業務層認為目前連接配接未建立成功,于是通過 close 關閉該 socket 。另外,由于此時并未成功建立 tcp 連接配接,故用戶端側協定棧不會發送 fin 包。而之後當收到來自伺服器端的 syn,ack 時(因為伺服器側并不知道目前連接配接已經被關閉 ),則直接由用戶端底層 tcp 協定棧回複 rst 。 

問題到此已經得到了解決,而此時還剩最後一個問題:線上問題是如何被觸發的?按道理說應該一直存在該問題的呀~~ 

      結論大緻如下(推斷出來的):nginx 所在機器在未經過核心參數優化的情況下,能夠處理一定量的并發連接配接,在請求量不大,每個請求的通路時間較短的情況下,是能夠正常提供服務的。由于近期存在一些其他服務的版本更新,懷疑部分業務的請求處理耗時有所增長,導緻請求處理速度的整體下降,另外由于針對 nginx 的狀态 檢測腳本本身也是依賴 http 接口進行的狀态檢測,也就必然會導緻連接配接資源的緊張,而一旦檢測失敗後進行虛位址切換,又會導緻問題的加劇。綜上,導緻了最終上述問題的爆發。 

相關參數說明:

man 2 listen 

man 7 tcp 

繼續閱讀