快傑雲主機 SSH 登錄緩慢的排查和解決

快傑雲主機是 UCloud 推出的具備優秀性能與極高性價比的新一代主機,網絡最高可達 1000 萬 PPS,存儲最高可達 120 萬 IOPS。爲了提升產品綜合表現,Host 內核、KVM 和 Guest 內核等做了大量調優。“高內核 Ubuntu18.04” 鏡像就是其中一款經優化的雲主機鏡像,集成了官方 linux 5.0.1 主線版本內核。

今年 7 月,有一位用戶反饋,使用該鏡像創建出的快傑雲主機每次啓動時,第一次 SSH 登錄會很慢,有時候需幾十秒甚至幾分鐘才能登錄成功,影響了使用體驗。

經過排查,定位到是 Linux 內核隨機數熵池初始化慢的原因,且在多個條件組合下才會觸發。更深入調查則發現因爲內核 bug,凡使用了 libssl 1.1.1 的進程(如開啓了 https 的 nginx)都有類似問題,會對系統安全產生不少潛在影響。

最終我們通過升級自主維護的內核,很快妥善修復了該問題,保證了快傑雲主機的體驗和安全性。

快傑雲主機 SSH 登錄緩慢的排查和解決

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

初步排查

該問題只在單個用戶上出現過,且隻影響啓動後的首次 SSH 登錄,一旦登錄成功便恢復正常。現場捕獲不易,不過我們設法將其復現。
ssh -v

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

快傑雲主機 SSH 登錄緩慢的排查和解決

可以看出,問題應當是發生在身份鑑定剛完成後,由此判斷,問題有較大可能是發生在 /etc/pam.d/sshd 定義的 PAM 過程中。

motd

檢視 /etc/pam.d/sshd 文件,根據現象以及直覺,決定嘗試先屏蔽幾段配置,其中就包括 motd 行,motd (message of the day) 是 Ubuntu 登錄後呈現給用戶看到的部分 banner 內容。隨後重啓主機,發現 ssh 登錄變快,不再卡住。

快傑雲主機 SSH 登錄緩慢的排查和解決

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

快傑雲主機 SSH 登錄緩慢的排查和解決

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

快傑雲主機 SSH 登錄緩慢的排查和解決

landscape-sysinfo

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

快傑雲主機 SSH 登錄緩慢的排查和解決

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

快傑雲主機 SSH 登錄緩慢的排查和解決

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

快傑雲主機 SSH 登錄緩慢的排查和解決

getrandom

點擊查看參考資料 http://man7.org/linux/man-pages/man2/getrandom.2.html

getrandom 封裝了對 /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 接口的作用和表現後,再去翻看內核的啓動日誌,找到了時間相關性極高的點。

快傑雲主機 SSH 登錄緩慢的排查和解決

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

深入調查

初步調查的結論有點違反常理,禁用或者刪除的措施也需謹慎。爲此,我決定找出更多的證據,此外,也需要解釋爲什麼舊版本的 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 版本,發現版本號確實不同,有問題的版本號較高,沒問題的版本號較低。

快傑雲主機 SSH 登錄緩慢的排查和解決

將沒問題的主機執行 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。

快傑雲主機 SSH 登錄緩慢的排查和解決

libssl1.1

翻閱

libssl1.1 的 release note https://www.openssl.org/news/openssl-1.1.1-notes.html

快傑雲主機 SSH 登錄緩慢的排查和解決

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

內核 bug

以 libssl 調用 getrandom 被阻塞爲關鍵主題查閱資料,最終找到相關性較強的資料

點擊查看相關資料 https://unix.stackexchange.com/questions/442698/when-i-log-in-it-hangs-until-crng-init-done

,其中 CRNG 指密碼學強度的隨機數發生器。
根據此資料,證實了內核 bug 的猜測,內核在 4.16 時修正過這樣一個 bug:getrandom 在快速初始化完成後就不再阻塞,這與 getrandom 的接口設計違背,容易造成安全問題(CVE-2018-1108)

內核 bug fix commit https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=43838a23a05fbd13e47d750d3dfd77001536dd33

快傑雲主機 SSH 登錄緩慢的排查和解決

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

快傑雲主機 SSH 登錄緩慢的排查和解決

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

快傑雲主機 SSH 登錄緩慢的排查和解決

查詢此現象相關資料,找到了一個內核編譯選項:CONFIG_RANDOM_TRUST_CPU。

CONFIG_RANDOM_TRUST_CPU

點此查看詳細選項說明 https://lwn.net/Articles/760121/

快傑雲主機 SSH 登錄緩慢的排查和解決

首次出現於 4.19 版本:https://cateee.net/lkddb/web-lkddb/RANDOM_TRUST_CPU.html

檢查高版本內核的兩臺主機,的確,有問題的主機此 flag 並沒有 enable,無問題的主機此 flag 顯式的 enable 了。

快傑雲主機 SSH 登錄緩慢的排查和解決

而有問題主機使用的內核版本,取自於 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 沒有 https://www.mail-archive.com/[email protected]/msg1602769.html

)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。

總結

提起雲主機,首先會想到計算、存儲、網絡,甚少有人關注內核。然而,內核構建也是雲主機的核心工作,對性能和穩定性至關重要。

SSH 登錄緩慢起初是單一用戶的反饋,且限於 Ubuntu 啓動後的首次登錄,但通過堅持排查和順藤摸瓜,我們發現了潛在的影響面並予以修復,防患於未然。
UCloud 團隊通過自主維護雲主機的內核源碼,一方面可以不斷調優性能來匹配產品的發展;另一方面保證了遇到現網各種問題時,有能力迅速排查和解決,並及時預防更大的系統安全風險。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章