一、eBPF安全可觀測性的前景展望
本次分享將從監(jiān)控和可觀測性,、eBPF安全可觀測性分析,、內(nèi)核安全可觀測性展望三個方面展開,。
1.監(jiān)控(Monitoring)vs可觀測性(Observability)
從上圖可以看到,,監(jiān)控只是可觀測性的冰山一角,而大部分都隱藏在水面之下的深層次問題無法簡單通過監(jiān)控解決,。
目前監(jiān)控也開始可視化,,但絕大部分都是事先預(yù)定義參數(shù),然后事后查看日志,,進行分析,。監(jiān)控的缺點包括:
可擴展性差,需要修改代碼和編譯,;驗證周期長,;數(shù)據(jù)來源窄等問題。
可觀測性是通過主動定制度量的搜集和內(nèi)核數(shù)據(jù)聚合,,包括以下三種:
Logging
實時或者事后特定事件信息
分布式服務(wù)器集群的海量數(shù)據(jù)溯源圖
離散信息整理各種異步信息
Tracing
數(shù)據(jù)源:提供數(shù)據(jù)來源
采集框架:往上對接數(shù)據(jù)源,,采集解析發(fā)送數(shù)據(jù),往下對用戶態(tài)提供接口
前端交互:對接Tracing內(nèi)核框架,,直接與用戶交互,,負責采集配置和數(shù)據(jù)分析
Metrics(度量)這也是可觀測性與監(jiān)控最主要的區(qū)別
系統(tǒng)中某一類信息的統(tǒng)計聚合,比如CPU,、內(nèi)存,、網(wǎng)絡(luò)吞吐、硬盤 I/O,、硬盤使用等情況,。當度量值觸發(fā)異常閾值時,系統(tǒng)可以發(fā)出告警信息或主動處理,,比如殺死或隔離進程,;
主要目的:監(jiān)控(Monitoring)和預(yù)警(Alert)
總結(jié)一下監(jiān)控和觀測的區(qū)別:
監(jiān)控:收集和分析系統(tǒng)數(shù)據(jù),查看系統(tǒng)當前的狀態(tài),,對可預(yù)見的問題進行分析處理,;
可觀測性:通過觀察系統(tǒng)并衡量系統(tǒng)的內(nèi)部狀態(tài),從其外部輸出的數(shù)據(jù)推斷出來系統(tǒng)此時處于某種程度的度量,,特別是我們所關(guān)心的場景和事件,;
2.eBPF安全可觀測性分析
先簡單定義下什么是安全:安全指的是某種對象或者對象屬性不受威脅的狀態(tài)。
所謂安全可觀測性:
通過觀測整個系統(tǒng),,從低級別的內(nèi)核可見性到跟蹤文件訪問,、網(wǎng)絡(luò)活動或能力(capability)變化,一直到應(yīng)用層,,涵蓋了諸如對易受攻擊的共享庫的函數(shù)調(diào)用,、跟蹤進程執(zhí)行或解析發(fā)出的 HTTP 請求。因此這里的安全是整體的概念,。
提供對各種內(nèi)核子系統(tǒng)的可觀測性,,涵蓋了命名空間逃逸、Capabilities 和特權(quán)升級、文件系統(tǒng)和數(shù)據(jù)訪問,、HTTP,、DNS、TLS 和 TCP 等協(xié)議的網(wǎng)絡(luò)活動,,以及系統(tǒng)調(diào)用層的事件,,以審計系統(tǒng)調(diào)用和跟蹤進程執(zhí)行。
從日志,、跟蹤及度量三個維度檢查相關(guān)輸出,,進而來衡量系統(tǒng)內(nèi)部安全狀態(tài)的能力。
eBPF的安全可觀測性表現(xiàn)為對內(nèi)核來說其存在感極低但觀測能力卻異常強大(藥效好,,副作用?。?
程序沙箱化:通過eBPF驗證器保護內(nèi)核穩(wěn)定運行。
侵入性低:無須修改內(nèi)核代碼,,且無須停止程序運行,。
透明化:從內(nèi)核中透明搜集數(shù)據(jù),保證企業(yè)最重要的數(shù)據(jù)資產(chǎn),。
可配置:Cilium等自定義乃至自動化配置策略,,更新靈活性高,過濾條件豐富,。
快速檢測:在內(nèi)核中直接處理各種事件,,不需要回傳用戶態(tài),使得異常檢測方便和快速,。
其中的程序沙箱話離不開更安全的eBPF Verifier(其中最重要的是邊界檢查):
擁有加載eBPF程序的流程所需的特權(quán)
無crash或其他異常導(dǎo)致系統(tǒng)崩潰的情況
程序可以正常結(jié)束,,無死循環(huán)
檢查內(nèi)存越界
檢查寄存器溢出
eBPF的可觀測性應(yīng)用場景主要有以下三類:
1.云原生容器的安全可觀測性、
這也是傳統(tǒng)BPF基于網(wǎng)絡(luò)應(yīng)用場景的進一步發(fā)展:
隨著云網(wǎng)邊端的急速發(fā)展,,人們的目光越發(fā)的聚焦在目前最火熱的云原生場景上,。Falco、Tracee,、Tetragon,、Datadog-agent、KubeArmor是現(xiàn)階段云原生場景下比較流行的幾款運行時防護方案,。
這些方案主要是基于eBPF掛載內(nèi)核函數(shù)并編寫過濾策略,,在內(nèi)核層出現(xiàn)異常攻擊時觸發(fā)預(yù)置的策略,無需再返回用戶層而直接發(fā)出告警甚至阻斷,。
以預(yù)防的方式在整個操作系統(tǒng)中執(zhí)行安全策略,,而不是對事件異步地做出反應(yīng)。除了能夠為多個層級的訪問控制指定允許列表外,,還能夠自動檢測特權(quán)和 Capabilities 升級或命名空間提權(quán)(容器逃逸),,并自動終止受影響的進程。
安全策略可以通過 Kubernetes(CRD)、JSON API 或 Open Policy Agent(OPA)等系統(tǒng)注入,。
2.應(yīng)用層安全可觀測方案
3.內(nèi)核層安全可觀測方案
3.內(nèi)核安全可觀測性展望
下面從傳統(tǒng)內(nèi)核安全,、Android內(nèi)核安全、KRSI等幾個方面展開討論,。
傳統(tǒng)內(nèi)核安全方案存在著諸多需要解決的問題:
正如Linus Torvalds曾經(jīng)說過的,大多數(shù)安全問題都是bug造成的,,而bug又是軟件開發(fā)過程的一部分,,是軟件就有bug。
至于是安全還是非安全漏洞bug,,內(nèi)核社區(qū)的做法就是盡可能多的測試,,找出更多潛在漏洞這樣近似于黑名單的做法。
內(nèi)核代碼提交走的流程比較繁瑣,,應(yīng)用到具體內(nèi)核版本上,,又存在周期長以及版本適配的問題,所以導(dǎo)致內(nèi)核在安全方面發(fā)展的速度明顯慢于其他模塊,。同時,,隨著智能化、數(shù)字化,、云化的飛速發(fā)展,,全球基于Linux系統(tǒng)的設(shè)備數(shù)以百億計,而這些設(shè)備的安全保障主要取決于主線內(nèi)核的安全性和健壯性,,當某一內(nèi)核LTS版本被發(fā)有漏洞,,這樣相關(guān)的機器都會面臨被攻破利用的局面,損失難以估量,。
嵌入式領(lǐng)域的Android內(nèi)核安全:
現(xiàn)如今,,世界上越來越多的智能終端包括手機、TV,、SmartBox和IoT,、汽車、多媒體設(shè)備等等,,均深度使用Android系統(tǒng),,而Android的底層正是Linux內(nèi)核,這也讓Linux內(nèi)核的安全性對Android產(chǎn)生重大影響,。
由于歷史原因,,Google在Android內(nèi)核開源的問題上,理念和Linux內(nèi)核社區(qū)不是十分的匹配,,這也導(dǎo)致了Android對內(nèi)核做了大量的針對性修改,,但是無法合入到Upstream上。這也導(dǎo)致了Android內(nèi)核在安全側(cè)有部分不同于Linux內(nèi)核,側(cè)重點也存在不同,。
在操作系統(tǒng)級別,,Android平臺不僅提供Linux內(nèi)核的安全功能,而且還提供安全的進程間通信 (IPC)機制,,以便在不同進程中運行的應(yīng)用之間安全通信,。操作系統(tǒng)級別的這些安全功能旨在確保即使是原生代碼也要受應(yīng)用沙盒的限制。無論相應(yīng)代碼是自帶應(yīng)用行為導(dǎo)致的結(jié)果,,還是利用應(yīng)用漏洞導(dǎo)致的結(jié)果,,系統(tǒng)都能防止違規(guī)應(yīng)用危害其他應(yīng)用、Android 系統(tǒng)或設(shè)備本身,。
Android內(nèi)核安全特性:
HWAddressSanitizer(硬件支持的內(nèi)存檢測工具)
KASAN
Top-byte Ignore
KCFI(流控完整性校驗)
ShadowCallStack(堆棧保護)
目前工作的關(guān)注重點是內(nèi)核安全可觀測性利器-KRSI:
KRSI (Kernel Runtime Security Instrumentation)的原型通過LSM (Linux security module)形式實現(xiàn),,可以將eBPF program掛載到kernel的security hook(安全掛鉤點)上。內(nèi)核的安全性主要包括兩個方面:Signals和Mitigations,,這兩者密不可分,。
Signals:意味著系統(tǒng)有一些異常活動的跡象,、事件
Mitigations:在檢測到異常行為之后所采取的告警或阻斷措施
KRSI基于LSM來實現(xiàn),,這也就使其能夠進行訪問控制策略的決策,但這不是KRSI的工作重心,,主要是為了全面監(jiān)視系統(tǒng)行為,,以便檢測攻擊(最重要的應(yīng)用場景,但目前主要還是只做檢測居多,,因為貿(mào)然做阻斷處理可能會比較危險),。從這種角度來看,KRSI可以說是內(nèi)核審計機制的擴展,,使用eBPF來提供比目前內(nèi)核審計子系統(tǒng)更高級別的可配置性,。
KRSI工具可以看作是eBPF和LSM的強強聯(lián)合:KRSI = eBPF + LSM
1.KRSI允許適當?shù)奶貦?quán)用戶將BPF程序掛載到LSM子系統(tǒng)提供的數(shù)百個鉤子中的任何一個上面。
2.為了簡化這個步驟,,KRSI在/sys/kernel/security/bpf下面導(dǎo)出了一個新的文件系統(tǒng)層次結(jié)構(gòu)——每個鉤子對應(yīng)一個文件,。
3.可以使用bpf()系統(tǒng)調(diào)用將BPF程序(新的BPF_PROG_TYPE_LSM 類型)掛載到這些鉤子上,并且可以有多個程序掛載到任何給定的鉤子,。
4.每當觸發(fā)一個安全鉤子時,,將依次調(diào)用所有掛載的BPF程序,只要任一BPF程序返回錯誤狀態(tài),,那么請求的操作將被拒絕,。
5.KRSI能夠從函數(shù)級別做阻斷操作,相比進程具有更細粒度,,危險程度也會小得多,。
后續(xù)計劃
內(nèi)核安全問題是個非常復(fù)雜的話題,,牽一發(fā)而動全身,防御機制,、加固配置,、漏洞利用等等挑戰(zhàn)性的技術(shù)。在進行加固防御的過程中,,又會產(chǎn)生性能或者系統(tǒng)穩(wěn)定性相關(guān)的影響,。
從eBPF + LSM的角度可以更加可視化、數(shù)據(jù)豐富的觀測內(nèi)核安全情況,,進而在內(nèi)核Livepatch,、漏洞檢測以及防御提權(quán)相關(guān)攻擊手段上,有著進一步的發(fā)展空間,。
二、Linux進程調(diào)度與性能優(yōu)化
本次分享將從進程調(diào)度概念,、進程調(diào)度框架,、進程調(diào)度算法和性能優(yōu)化四個方面展開。
1.進程調(diào)度概念
最初的Linux只有進程,,task_struct相當于進程控制塊(PCB),,后來出現(xiàn)了線程,task_struct就開始對應(yīng)線程,。Linux沒有從概念上直接區(qū)分兩者,,如果不同的task_struct間共享資源,它們屬于同一進程中的線程,,否則就屬于不同的進程,。
這里要注意,用戶態(tài)中的進程和線程與內(nèi)核態(tài)間的映射關(guān)系:用戶態(tài)函數(shù)getpid()得到的是內(nèi)核中的tgid,;用戶態(tài)函數(shù)gettid()得到的是內(nèi)核中的pid,。
Posix支持一級調(diào)度和二級調(diào)度兩種模式,二級調(diào)度會先調(diào)度進程,,然后才是進程中的線程,。而Linux選擇的是直接調(diào)度線程的一級調(diào)度,效率會比二級調(diào)度高,。
Linux進程調(diào)度體系如下圖:
CPU資源管理器(what):調(diào)度器(scheduler)就是CPU資源管理器,,因為操作系統(tǒng)的重要作用之一是管理各種系統(tǒng)資源,而CPU是其中最重要的資源,。常用直接共享型資源的管理方法有時分,、空分、獨占等,,IO一般是獨占資源,,內(nèi)存支持空分管理,,而CPU只支持時分,因此這種時分管理方法就是進程調(diào)度,。
為什么要及為什么能調(diào)度(why):
為什么要調(diào)度:宏觀并行,,微觀串行,支持多任務(wù)的協(xié)作與搶占,。最初是協(xié)作,,后來為了防止個別進程長期霸占CPU,引入了搶占機制,。
為什么能調(diào)度:包括主動調(diào)度和被動調(diào)度,,線程上下文可切換。
調(diào)度時機(when):
主動調(diào)度:通過主動調(diào)用sched_yield的自愿性主動調(diào)度,,以及進程由于等待資源而阻塞的非自愿性主動調(diào)度,。
被動調(diào)度:
觸發(fā)點:設(shè)置 TIF_NEED_RESCHED 標志,主要是時鐘中斷和喚醒搶占,。
執(zhí)行點:檢查 TIF_NEED_RESCHED 標志 和 滿足preempt_count == 0的條件,。
主要包括以下四種場景:
系統(tǒng)調(diào)用完成返回用戶空間
完成返回用戶空間
中斷完成返回內(nèi)核空間
出禁用搶占臨界區(qū)
如何進行調(diào)度(how):調(diào)用pick_next_task()選擇下一個調(diào)度進程和通過context_switch切換進程(上下文:內(nèi)存空間,寄存器和棧),。
下面按時間順序簡單介紹一下linux調(diào)度器的發(fā)展歷史:
傳統(tǒng)Unix調(diào)度器:區(qū)分IO密集型和CPU密集型,,但全局只有一個未排序的運行隊列,多CPU會有競爭,,而且調(diào)度選擇需要遍歷整個隊列,,復(fù)雜度為O(n)。
O(1)調(diào)度器:運行隊列從全局變成每個CPU一個,,鏈表分成活動和過期兩個數(shù)組,,引入位圖,復(fù)雜度降為為O(1),。
SD調(diào)度器:考慮到會因為個別進程的偽裝而造成的實際調(diào)度不公平,,不再區(qū)分IO和CPU密集型,未合入內(nèi)核,。
RSDL調(diào)度器:組時間配額,,有點像Cgroup,但未合入內(nèi)核,。
CFS(完全公平調(diào)度器):從SD/RSDL中吸取了完全公平的思想,,目前內(nèi)核主流調(diào)度器。
可以從以下幾個方面來評價調(diào)度器:
1.響應(yīng)性:存在大量人機交互的PC和手機要重視響應(yīng),。
2.吞吐量:服務(wù)器更關(guān)心吞吐量,,而提高響應(yīng)性會增加進程切換的評論,也就會降低吞吐量,。所以響應(yīng)和吞吐是一對矛盾,。
3.公平性:相對公平,,實際和理論的運行時間應(yīng)該相符。
4.適應(yīng)性:也就是可擴展性,。
5.節(jié)能性:智能手機的需求,,需要通過調(diào)度均衡來省電。
2.進程調(diào)度框架
如下圖所示,,Linux一共有5個調(diào)度類:(stop和idle每個CPU一個)
stop:無調(diào)度策略,,用戶空間不可用,內(nèi)核用來處理線程遷移等優(yōu)先級極高的事情,。
idle:無調(diào)度策略,,用戶空間不可用,無其他進程調(diào)用時才調(diào)用idle進程,。
deadline:硬實時,,要在確定時延內(nèi)完成調(diào)度。
realtime:軟實時,,盡力而為保證實時性,。
time-share:按優(yōu)先級分配時間片。
調(diào)度類之間的關(guān)系:除非資源限制,,優(yōu)先調(diào)度高優(yōu)先級進程。但要注意,,Linux考慮到在惡劣環(huán)境下,,普通進程應(yīng)該仍有被調(diào)度的機會,會默認預(yù)留有5%的帶寬給普通進程,。
如下圖所示,,從用戶空間到內(nèi)核空間,進程優(yōu)先級會發(fā)生轉(zhuǎn)換:
用戶空間優(yōu)先級:分為1~99優(yōu)先級從低到高的實時進程,,以及nice值-20~19動態(tài)優(yōu)先級從高到低的分時進程,。
映射到內(nèi)核空間優(yōu)先級分為三步:
1.實時優(yōu)先級:實時進程不變,分時進程從動態(tài)優(yōu)先級映射為100~139靜態(tài)優(yōu)先級由高到低,。
2.規(guī)范優(yōu)先級:實時進程映射為-1~98優(yōu)先級由高到低,,99為空,分時進程靜態(tài)優(yōu)先級不變,。
3.動態(tài)優(yōu)先級:可以看到動態(tài)優(yōu)先級幾乎等于規(guī)范優(yōu)先級,,只有在解決優(yōu)先級反轉(zhuǎn)問題時采用優(yōu)先級繼承策略時才會變化,此時會體現(xiàn)為動態(tài)優(yōu)先級,。
注意:-1和-2分別對應(yīng)deadline和stop的邏輯優(yōu)先級,。
那如何進行進程切換呢?
切換用戶空間:不同進程的用戶空間不同,,需要切換,。
切換內(nèi)核棧:不同進程共享內(nèi)核空間,,但需要切換內(nèi)核棧。進程切換就像火車切換軌道,,換道后,,車上的人感覺火車沒變,但是因為軌道變了,,行程也就跟著變了,。線程的執(zhí)行過程就是函數(shù)調(diào)用樹的深度優(yōu)先遍歷,進程的切換點是__schedule函數(shù),,該函數(shù)前半部分在進程A的調(diào)用棧上執(zhí)行,,后半部分就跑到進程B的調(diào)用棧上執(zhí)行了,返回則在新進程上執(zhí)行,。
3.進程調(diào)度算法
CFS調(diào)度器定義:通過引入虛擬運行時間vruntime(realtime / weight),,每次選擇vruntime最小的進程(紅黑樹來管理)來調(diào)度,在真實硬件上模擬理想多任務(wù)處理器,。
枯燥的定義不太容易理解的話,,下面我們通過一個與實際生活比較貼近的例子來解釋CFS調(diào)度模型。
請仔細觀察上圖,,圖中事物與進程調(diào)度的映射關(guān)系如下:
水杯:表示進程,,蓋子打開的水杯表示進程處于就緒和執(zhí)行狀態(tài),其中Ready Table上的水杯都是就緒態(tài)進程,,水龍頭下的水杯是運行態(tài)進程,。蓋子閉合的表示阻塞態(tài)的進程,它們都在不同的Wait Box中,。
水杯的粗細:表示不同的優(yōu)先級,。
watermark:表示虛擬運行時間vruntime,min watermark會被pick to run,。
Ready Table:表示可運行隊列rq,。
Wait Box:表示不同Event對應(yīng)的等待隊列,如果只能由對應(yīng)的Event喚醒的話,,就屬于不可中斷的深睡眠,,否則就是也可由signal喚醒的可中斷淺睡眠。
水龍頭:表示CPU,,水龍頭中放出來的水就是CPU時間,。
操作pick to run的手:表示調(diào)度算法,如果調(diào)度過于頻繁,,那杯子接的水就少,,水龍頭的水掉地上浪費了(響應(yīng)好但性能差);反之調(diào)度周期長的話,,杯子接的水太多會造成不平均(性能好但響應(yīng)差),。權(quán)衡下來既不能接太少,,最小值就是調(diào)度粒度;也不能接太多,,最大值就是時間片,。
這里需要特別注意的幾點:
1.進程的狀態(tài)
注意區(qū)別進程的5種狀態(tài)中的運行態(tài)和就緒態(tài),所有就緒態(tài)進程都位于cpu的運行隊列中,,而每個cpu上當前只有一個運行態(tài)進程,。可以看到下面的代碼分別通過on_rq和on_cpu來確定進程是runnable的就緒態(tài)還是running的運行態(tài),。對應(yīng)上面的例子,,桌子上的開口水杯表示就緒(on_rq),水龍頭下的開口水杯表示運行(on_cpu),。
2.優(yōu)先級與權(quán)重
CFS中的nice值會提前轉(zhuǎn)換為權(quán)重,,nice的范圍是 [-20 -- 19],為了使得兩個相鄰的權(quán)重的占比差為10%,,將以nice 0 為基準轉(zhuǎn)化為1024,,整個數(shù)列是以1.25為公比的等比數(shù)列。將原來的等差數(shù)列轉(zhuǎn)為等比數(shù)列,,因為調(diào)度策略的絕對值不重要,,相對值(比例)才重要。
3.調(diào)度周期
調(diào)度周期 = 調(diào)度粒度 * 就緒和運行進程的數(shù)量,,對應(yīng)上面的例子就是所有開蓋玻璃杯數(shù)量(桌子上和水龍頭下的)與調(diào)度粒度的乘積,,等于調(diào)度周期(所有杯子都接了一次水)。最小調(diào)度周期等于最小粒度乘以數(shù)量,,如果計算算出來的時間片小于最小調(diào)度周期,,內(nèi)核就會讓時間片直接等于最小調(diào)度周期,。
4.調(diào)度延遲
調(diào)度延遲是另一個概念,,對應(yīng)上面的例子就是從開口玻璃杯放到到桌子上開始計算,到送到水龍頭下接水的時間,。但要注意,,Linux中的調(diào)度延遲卻不這個意思,內(nèi)核代碼體現(xiàn)的調(diào)度延遲是最小調(diào)度周期,,也就是調(diào)度周期的最小值,。
4.性能優(yōu)化
單個進程的性能優(yōu)化:
對于單個進程來說,可以通過下面的系統(tǒng)調(diào)用來改變進程(線程)的調(diào)度策略和優(yōu)先級,,只有特權(quán)進程才能調(diào)高自己的調(diào)度策略或者優(yōu)先級,。一般不要輕易地把進程設(shè)置為實時進程或者提高其優(yōu)先級,除非有充足的理由,。
系統(tǒng)響應(yīng)性與吞吐量的優(yōu)化:
系統(tǒng)的響應(yīng)性和吞吐量是一對矛盾,,很多時候我們要根據(jù)具體的情況來對系統(tǒng)進行配置,。CONFIG_PREEMPT:是否開啟內(nèi)核搶占,開啟可增加響應(yīng)性,,不開啟可增加吞吐量,。一般服務(wù)器系統(tǒng)不會開啟,桌面和移動系統(tǒng)會開啟,。
定時器tick頻率HZ:
定時器tick的頻率HZ,,對系統(tǒng)的響應(yīng)性和吞吐量也有很大的影響,HZ取值較大,,系統(tǒng)響應(yīng)性好,,但是吞吐量會降低,HZ取值較小,,吞吐量會增大,,但是響應(yīng)性會降低。
避免實時進程過度占用CPU:
為此內(nèi)核提供了一對參數(shù),,可以設(shè)置一個運行周期內(nèi),,實時進程最多只能用多少CPU時間,這樣就可以給普通進程留下一些執(zhí)行時間,。這兩個參數(shù)對應(yīng)的文件是:
/proc/sys/kernel/sched_rt_period_us/proc/sys/kernel/sched_rt_runtime_us
可以通過cat和echo來查看和設(shè)置具體的值,,單位是微妙默認值分別是1000000,950000,,也就是說在每一秒內(nèi)實時進程最多運行0.95s,,會給普通進程保留0.5s。當然如果此時沒有普通進程,,實時進程還是可以使用100%的CPU時間的,。如果我們不希望實時進程使用過多的CPU時間的話,可以修改這個值,。
子進程是否優(yōu)先運行的問題:
默認情況下父進程與剛fork出來的子進程,,誰會先得到執(zhí)行是不確定的。如果在我們的環(huán)境中經(jīng)常會fork子進程,,并且我們總是希望子進程比父進程先得到執(zhí)行,,那么我們可以將/proc/sys/kernel/sched_child_runs_first值設(shè)為1。
更多信息可以來這里獲取==>>電子技術(shù)應(yīng)用-AET<<