本文在前半部分叙述一個聽起來十分吸引人且合理的故事,然後緊接着告訴你這個美好的故事事實上幾乎不會發生,最後來個總結。在接下來的一篇文章中,我提出一個比較自我的方案。
在 xtables-addons中,有一個特别有意思的小子產品,那就是xt_SYSRQ,它作為一個iptables的target加載進核心,可以在遠端 為本機發送sysrq指令,這個功能可謂強大。在去年的項目中中,我已經将其部署到了實際的産品中,然而今日再看,發現還是有些美中不足,确實需要改進:
雖然你可以遠端觸發一個sysrq指令,但是你得不到任何回報資訊,包括,指令執行成功與否,核心環形緩沖區的内容。有的時候,我僅僅是需要看看dmesg的資訊而已,而有的時候,我想看看核心stack的dump資訊。我要做的并不僅僅是重新開機機器這麼簡單的事。
在 addons中,SYSRQ子產品實作一個簡單的認證機制,即password認證,為了防止有人攻破password,使用了序列号視窗機制。然而你要知 道,這種防護是很容易攻破的,第一,資料包使用UDP傳輸,第二,核心中實在不便于做強認證,特别是真的panic的情況下。是以,更好的做法是将這種認 證放在外部。
以上是我發現的兩個問題,但是解決它們的時候切記要點到為止,這是為什麼呢?
仔細想想,難道真的需要這種方式來擷取遠端的dmesg資訊嗎?如果系統還活着,用SSH的方式不是更好嗎?是以,正如xtables-addons文檔 中所述,這個功能隻是在系統死掉的情況下才會是一種選擇方式。知識點隻有一個,那就是系統挂起以及panic的時候,中斷或許還可以繼續被響應,而 Netfilter HOOK的執行作為中斷後的軟中斷也會被繼續響應。可能有些人會問,為何系統都panic了,還會響應中斷。答案在于你把panic想的過于嚴重 了,panic隻是系統處于恐慌而不知所措的狀态,并沒有死去。導緻系統不知所措的原因可能是系統級的操作bug導緻了記憶體混亂或者别的混亂,系統繼續運 行下去将會導緻不可知的後果,此時最好的辦法就是原地不動,這就是panic。由于中斷的響應邏輯是封閉的,是以中斷是依然可以響應的,然而如果中斷處理 相關的記憶體被破壞,就徹底沒有機會了。
接下來再看一下遠端SYSRQ的安全機制,其實作者原生的方式很安全,再次地,沒有必要将其向複雜化路線上擴充太多。系統已然挂起,此時的處理應該盡可能 地簡單,安全前提是需要保證的。首先網絡上沒有傳輸password的明文,而隻傳輸了其摘要值,這種政策在網絡認證領域已經爛大街了,其次,為了防止重 放攻擊以及防止碰撞密碼,采用了視窗機制,也不失為一個小巧的技巧,于是隻要加一個回報機制,就可以了,上面的問題2基本上不是問題。
正當一切都已經就緒,知道了該做什麼不做什麼的時候,當你用Sysrq-c故意把系統Crash之後,你會發現一切瞬間停止,網卡中斷完全不再響應...到底發生了什麼?
如果你想知道panic之後到底發生了什麼,也不是特别複雜,事實上,panic之後的序列如下:
1.禁止搶占(如果核心編譯時沒有打開搶占,相當于什麼都沒有做);
2.列印資訊和堆棧;
3.調用kexec邏輯;
4.通知其它的CPU停止所有工作;
5.調用panic通知鍊進行善後;
6.如果panic timeout被設定,則等待後重新開機機器;
7.如果panic timeout沒有被設定,則進入"閃燈"狀态,直到永遠。
其 中最關鍵的是第4步,這裡也是今後系統是否還會進行中斷和軟中斷的關鍵,在詳細解釋之前,先說一下為何要通知其它CPU停止工作。因為panic在本 CPU被觸發,而在多個CPU間共享的核心資料結構此時可能已經亂掉了,此時就要通知别的CPU在這一瞬間停止。那麼停止需要做哪些工作呢?主要集中在和 對應CPU相關的外部總線以及中斷控制器操作,典型的說就是關閉掉它們,等于說把這些CPU盡可能和外部事件隔離起來,這也是一種安全的做法,如果一下子 掉電,可能會在熱重新開機後遇到電平不一緻的問題,是以采用安全的關閉序列總是要好一些的。那麼有沒有直接一點的做法呢?當然有!
如果我們設定機器重新開機的方式為冷重新開機(闆子幾乎都支援),那麼就不存在電平不一緻的情況了,此時就可以将CPU安全地一下子掉電,是以也就沒有必要遵循安 全關閉序列來關閉CPU了,為了更高效,在明知系統馬上就會重新啟動的前提下(這很重要),根本沒有必要關閉中斷控制器,而此時的CPU便會繼續響應中 斷,是否會繼續處理軟中斷取決于panic是否在非中斷上下文中被觸發。由于已經禁止了搶占且沒有任何執行流會傳回使用者态,是以此時不可能發生task切 換,故而即便是軟中斷會被處理,也不會在softirqd上下文被處理,而是在irq_exit中的中斷上下文被處理。
一切就是這麼慘!
具體來講,要想在panic之後還能響應中斷,你需要設定一個核心啟動參數:reboot=f,c。含義是強制(force)冷(cold)重新開機,此時不 會執行停止CPU本地APIC中斷控制器的操作,要想繼續處理軟中斷,請别在中斷上下文panic,這就好像告訴某個人請别死一樣,這是無法控制的。即便 能保證中斷上下文不會panic,由于無法排程softirqd,在中斷上下文中來不及處理(隻有MAX_SOFTIRQ_RESTART次機會)的軟中 斷将會被徹底淹沒。另外,非要讓系統在panic繼續執行中斷響應和軟中斷處理是極其不對的想法,那樣會破壞更多的核心資料結構,萬一破壞了磁盤 cache/buffer,或者誤寫了某個位址/寄存器,後果将是不可預料的...一切就是這麼慘,隻因為核心panic!
正 确的做法不是設定一個iptables政策等待外部遠端觸發SYSRQ,這意味着中斷不能被關閉,且軟中斷必須被處理...正确的做法是在panic之後 盡可能馬上重新開機,而在重新開機之前需要做點善後操作,當然,如果你配置了kexec,那更好,但是如果你不準備調試它,那就完全沒必要。以下是正确的做法:
1.啟動時設定reboot=f,c參數,panic後不會禁止中斷控制器工作;
2.系統啟動後,設定sysctl -w kernel.panic=5,争取馬上重新開機系統;
3.注冊一個panic通知鍊,向外以廣播位址封裝以太頭,廣播核心環形緩沖區的内容(包括堆棧等資訊)。
為 何采用廣播我要說一下,因為我不希望它發送ARP請求然後再等ARP回應,因為那會平添一次互動(由于ARP回應的處理在軟中斷中進行,既然不能保證 panic不在中斷中被觸發,就不能保證軟中斷一定會被執行),再者說,發給誰呢?誠然可以配置一個接收IP位址,但多一個配置不說,這個IP畢竟儲存在 記憶體,隻要是使用了多一點的記憶體,panic之後取到錯誤資料的可能性就更大,panic之後你要想辦法使用最少的資訊,雖然“發送資料包”這種事場面已 經夠龐大了,但是也是沒有辦法。
本文轉自 dog250 51CTO部落格,原文連結:http://blog.51cto.com/dog250/1610428