云計算

由Linux內核bug引起SSH登錄緩慢問題的排查與解決

今年7月,有一位用戶反饋,使用該鏡像創建出的快杰云主機每次啟動時,第一次SSH登錄會很慢,有時候需幾十秒甚至幾分鐘才能登錄成功,影響了使用體驗。經過排查,定位到是Linux內核隨機數熵池初始化慢的原因,且在多個條件組合下才會觸發。更深入調查則發現因為內核bug,凡使用了libssl 1.1.1的進程(如開啟了https的nginx)都有類似問題,會對系統安全產生不少潛在影響。最終我們通過升級自主維護的內核,很快妥善修復了該問題,保證了快杰云主機的體驗和安全性。

本文對排查過程加以梳理。

1

初步排查

該問題只在單個用戶上出現過,且只影響啟動后的首次SSH登錄,一旦登錄成功便恢復正?!,F場捕獲不易,不過我們設法將其復現。

ssh -v打開ssh用戶端的冗余日志模式嘗試登錄問題主機,發現總是會卡在”debug1: pledge: network”處,根據提示,sshd已經完成了用戶的身份認證過程。

可以看出,問題應當是發生在身份鑒定剛完成后,由此判斷,問題有較大可能是發生在 /etc/pam.d/sshd 定義的PAM過程中。
motd檢視/etc/pam.d/sshd文件,根據現象以及直覺,決定嘗試先屏蔽幾段配置,其中就包括motd行,motd(message of the day)是Ubuntu登錄后呈現給用戶看到的部分banner內容。隨后重啟主機,發現ssh登錄變快,不再卡住。

查閱資料可知,motd機制下,pam_motd.so會依次執行 /etc/update-motd.d/ 目錄下的全部腳本,而這些腳本的輸出則會被拼湊輸出到文件/run/motd.dynamic中,最終呈現在banner中。

因此,懷疑是這些腳本的執行過程中產生的卡頓,閱讀這些腳本,執行斷點echo 調試,最后發現,位于”50-landscape-sysinfo”腳本中的“/usr/bin/landscape-sysinfo”命令執行時就會造成卡頓。

landscape-sysinfo

該命令僅僅是一個用來搜集顯示banner中系統資源使用情況的工具,出現此問題有點難以置信,可實際上登錄進入后多次執行此命令也沒有出現卡頓。

嘗試進一步追蹤此命令的執行,使用strace追蹤此命令的執行,并記錄日志。

分析日志可以發現,啟動時,該命令被卡在了getrandom系統調用上,解除阻塞時間點為 23:10:48。

getrandom

etrandom封裝了對 /dev/urandom 字符設備文件的讀取操作,用于獲取高質量的隨機數,/dev/urandom 會以/dev/random 的值做為seed參考,/dev/random 值則來自硬件運行的噪音(隨機質量很高)。這種機制也決定了/dev/urandom在操作系統剛啟動時生成的隨機數質量不高(剛啟動,/dev/random中噪音不足,生成慢,隨機性差,容易被預測,間接導致了/dev/urandom的起始seed質量低下),所以 /dev/urandom 內部對其質量設置了三種狀態:

  • 0=未初始化,但是/dev/urandom已經可用;
  • 1=快速初始化,使用了少量熵數進行了快速初始化,在剛啟動時就盡快可以被用起來,質量還行,但是仍然不被建議用于加密場景,通常發生在操作系統啟動后的幾秒內;
  • 2=完全初始化,隨機數的質量達到最高,可以用于加密場景,操作系統啟動后約幾十秒-幾分鐘的時間才能達到。

在默認情況下,getrandom讀取/dev/urandom前會去檢測 /dev/urandom 的質量狀態,如果尚未完全初始化,則會阻塞,直到其完全初始化,以此來保障通過此接口獲得到的隨機數質量高且速度快,為安全領域提供可靠的依賴。了解了getrandom接口的作用和表現后,再去翻看內核的啟動日志,找到了時間相關性極高的點。

可以看到,23:10:48時 /dev/urandom 完全初始化后,隨即getrandom的調用阻塞也被解除了,再多次重復驗證后,關聯性被確認。此時的結論以及建議解決辦法為:原因:操作系統初始化隨機數熵池速度較慢,導致ssh登錄時使用到隨機數的一條命令時被阻塞。

建議:禁用motd或者刪除landscape-sysinfo來達到加速ssh登錄的目的。

2

深入調查

初步調查的結論有點違反常理,禁用或者刪除的措施也需謹慎。為此,我決定找出更多的證據,此外,也需要解釋為什么舊版本的Ubuntu并沒有此現象。嘗試查看表現正常的主機上 landscape-sysinfo 的 strace 表現,查閱日志后注意到,此環境下的strace記錄與問題主機中strace記錄在調用模式上存在不同,表現正常的主機上landscape-sysinfo中沒有這樣的調用“getrandom(“xxx”, 32, 0) ”,注意第三個flag參數值,此flag用于表明使用getrandom的默認行為,即/dev/urandom未完全初始化時則阻塞。所有getrandom的地方都使用了flag GRND_NONBLOCK,即如果沒有初始化完成不要阻塞,返回錯誤就好。至此,懷疑是landscape-sysinfo版本問題。landscape-sysinfo對比兩臺主機上的landscape-sysinfo版本,發現版本號確實不同,有問題的版本號較高,沒問題的版本號較低。

將沒問題的主機執行apt-get update & apt-get upgrade,升級后發現問題果然重現。得出臨時結論:landscape-sysinfo新版本使用了getrandom的阻塞模式獲取隨機數,不要升級landscape-sysinfo的版本即可。

開始嘗試在其它主機上進行復現和驗證,卻發現,在另一個高內核版本的鏡像中,低版本的landscape-sysinfo也能復現此問題,strace追蹤調用,發現其調用行為與高版本的landscape-sysinfo表現相似,鑒于此命令實際上是python3腳本,懷疑是其依賴的庫升級導致。檢查apt-get upgrade升級的package,找出與隨機數關聯度較大的幾個包,幾次排除嘗試后,定位發現,其實是由于libssl1.1這個庫的升級導致的問題,getrandom的調用也是源自于libssl1.1。

libssl1.1翻閱 。

可以看到,的確,libssl1.1.1的升級,重寫了內部隨機數的生成器,也符合前面的表現,更新為使用getrandom讀取更加安全的隨機數(代價是剛開機時使用就容易被阻塞)。繼續嘗試在其它主機上進行復現和驗證,又發現,在某個低內核版本的Ubuntu主機上,安裝的正是libssl1.1.1,卻不能復現問題。按照預期,libssl1.1.1的升級就是為了更安全,而如果一開機就能立刻得到隨機數,這根本就違背的getrandom接口的設計初衷,此時傾向于懷疑內核可能存在bug。

內核bug以libssl調用getrandom被阻塞為關鍵主題查閱資料,最終找到相關性較強的資料,其中CRNG指密碼學強度的隨機數發生器。根據此資料,證實了內核bug的猜測,內核在4.16時修正過這樣一個bug:getrandom在快速初始化完成后就不再阻塞,這與getrandom的接口設計違背,容易造成安全問題(CVE-2018-1108)

驗證主機的內核版本為4.15.0,與此情況符合,即很有可能是bug沒有被修復,此時,開始嘗試升級低內核版本主機的內核版本,如果此猜測正確,那么升級到高版本后應當同樣會發生卡頓問題。在apt源上挑選了一個5.0版本的內核,升級后發現,居然也沒有問題。翻閱內核日志,發現了一個新的現象,此前看到對于/dev/urandom的初始化,一般是會有一條“fast init done”日志,較長時間后會跟隨一個“crng init done”日志,正好對應著/dev/urandom的兩種質量狀態。

而此內核版本下,則是在剛啟動就立即出現了“crng done (trusting CPU’s manufacturer) ”的日志,明顯表明熵池被極速的初始化了,自然不會出現卡頓問題。

查詢此現象相關資料,找到了一個內核編譯選項:CONFIG_RANDOM_TRUST_CPU。CONFIG_RANDOM_TRUST_CPU此選項通過參考x86指令集中的RDRAND來初始化隨機數熵池,以此達到讓getrandom不再blocking的目的,前提是需要操作者信任CPU制造商。RDRAND指令由Intel在IVB世代中首次引入x86中,AMD在2015年6月時跟進,由于此指令并非標準指令,因此操作系統使用前需要做額外的功能檢測。

首次出現于4.19版本:檢查高版本內核的兩臺主機,的確,有問題的主機此flag并沒有enable,無問題的主機此flag顯式的enable了。

而有問題主機使用的內核版本,取自于Ubuntu官方的最新主線分支編譯,默認沒有啟動此flag做優化。結論此問題的出現需要同時滿足以下幾個條件:1. linux內核版本在4.17及以上2. linux內核編譯選項 CONFIG_RANDOM_TRUST_CPU is not set,或者CPU非IVB及以上的x863. linux內核bug未被社區revert(4.x的最新社區版本幾乎都被revert,5.x沒有)4. libssl版本在1.1.1及以上影響面事實上,SSH登錄卡頓僅僅是一種表象,這個問題的真實影響范圍,可以擴展得非常大。凡是使用了libssl1.1.1來生成隨機數的進程全都會受影響,導致重啟后3-5分鐘內會被隨機數block。比如,服務器上跑著nginx(開啟https),一般認為機器啟動后,nginx馬上啟動就能提供服務了,但是受此問題影響,nginx會被卡住三五分鐘。就是說,前三五分鐘內的用戶https請求全部會出現訪問失敗的現象。

問題修復

為了做到性能與安全兼顧,在編譯4.19及更高版本的內核時,啟用CONFIG_RANDOM_TRUST_CPU選項,我們采用此方法,enable選項并確保虛擬機可以訪問RDRAND指令集,很快重新發布了云主機鏡像。如果用戶使用自定義內核,應盡量避開4.17-4.19之間的版本,或者妥善處理好CVE-2018-1108。

3

總結

提起云主機,首先會想到計算、存儲、網絡,甚少有人關注內核。然而,內核構建也是云主機的核心工作,對性能和穩定性至關重要。SSH登錄緩慢起初是單一用戶的反饋,且限于Ubuntu啟動后的首次登錄,但通過堅持排查和順藤摸瓜,我們發現了潛在的影響面并予以修復,防患于未然。

我還沒有學會寫個人說明!

淺談微視推薦系統中的特征工程

上一篇

愛奇藝的數據庫選型大法,實用不糾結!

下一篇

你也可能喜歡

由Linux內核bug引起SSH登錄緩慢問題的排查與解決

長按儲存圖像,分享給朋友

ITPUB 每周精要將以郵件的形式發放至您的郵箱


微信掃一掃

微信掃一掃
30岁的男人干啥赚钱快赚钱多