<acronym id="s8ci2"><small id="s8ci2"></small></acronym>
<rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
<acronym id="s8ci2"></acronym>
<acronym id="s8ci2"><center id="s8ci2"></center></acronym>
0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

云原生運行時防護系統Tetragon介紹

xCb1_yikoulinux ? 來源:榫卯江湖 ? 作者:榫卯江湖 ? 2022-06-12 15:43 ? 次閱讀

TL;DR

文章較長,代碼很多,可直接拖到文末看講解視頻。

背景

在云原生領域中,Cilium是容器管理上最著名的網絡編排、可觀察性、網絡安全的開源軟件?;诟锩约夹geBPF實現,并用XDP、TC等功能實現了L3、L4層的防火墻、負載均衡軟件,具備優秀的網絡安全處理能力,但在運行時安全上,Cilium一直是缺失的。

2022年5月,在歐洲舉行的KubeCon技術峰會期間,Cilium的母公司Isovalent發布了云原生運行時防護系統Tetragon[1],填補這一空缺。

Tetragon的面世,意味著與falco、tracee、KubeArmor、datadog-agent等幾款產品正面競爭,eBPF運行時防護領域愈加內卷。

Tetragon介紹

摘自Tetragon官方倉庫[2]的產品介紹。

eBPF實時性

Tetragon 是一個運行時安全實時和可觀察性工具。它直接在內核中對事件做相應動作,比如執行過濾、阻止,無需再將事件發送到用戶空間處理。

3bf52fcc-e92d-11ec-ba43-dac502259ad0.png

對于可觀察性用例,直接在內核中應用過濾器會大大減少觀察開銷。避免昂貴的上下文切換,尤其是對于高頻事件,例如發送、讀取或寫入操作,減少了大量的內存、CPU等資源。

同時,Tetragon提供了豐富的過濾器(文件、套接字、二進制名稱、命名空間等),允許用戶篩選重要且相關的事件,并傳遞給用戶空間。

eBPF靈活性

Tetragon 可以掛載到Linux Kernel中的任何函數,并過濾其參數、返回值、進程元數據(例如,可執行文件名稱)、文件和其他屬性。

跟蹤策略通過編寫跟蹤策略,用戶可以解決各種安全性和可觀察性用例。Tetragon社區中,提供了一些常見的跟蹤策略,可以解決大部分的可觀察性和安全用例。

用戶也可以創建新的規則策略部署,自定義配置文件,以滿足業務需求。

eBPF 內核感知

Tetragon 通過eBPF鉤子感知Linux Kernel狀態,并將狀態與Kubernetes用戶策略相結合,以創建由內核實時執行的規則,來增強云原生場景的安全防護功能。例如,當容器內惡意程序更改其權限時,我們可以創建一個策略來觸發警報,甚至在進程有機會完成系統調用并可能運行其他系統調用之前終止該進程。

Tetragon阻斷實現原理

以上是Tetragon官方的介紹,提到具備阻斷能力,并在技術峰會上,展示了相關阻斷的截圖,有必要了解一下其實現原理。

tetragon的運行原理會在下篇詳細介紹,本篇主要講實時阻斷原理。本文分析的代碼版本為首次發布的tag v0.8.0[3] ,commit ID:75e49ab。

業界常見方式

LKM的內核模塊、LD_PRELOAD的動態鏈接庫修改、基于LSM的selinux、seccomp技術等都是常見做內核態/用戶態運行時阻斷的技術方案,而缺點就比較明顯,系統穩定性、規則靈活性、變更周期等問題比較突出。

3c383e52-e92d-11ec-ba43-dac502259ad0.png

當然,也有使用內核模塊方式。方法是把eBPF特性,封裝在內核模塊里,再安裝到老版本的內核上,這樣,就可以覆蓋更多內核版本了。但backport新特性的做法在社區里很不推薦,維護成本特別高,需要比較大的內核研發團隊與深厚的技術功底。。

云原生生態中,CNCF的項目Falco具備內核模塊與eBPF探針兩套驅動引擎,提供數據收集能力。同類產品Tracee也是,還基于LSM接口,實現了一定的防御阻斷能力,同時支持使用者自定義語法配置文件,進行檢測、判斷、阻斷規則的修改快速更新,以達到更好的防御能力。

3c634bec-e92d-11ec-ba43-dac502259ad0.png

作為云原生領域的容器管理軟件領頭羊,Cilium也會落后,但Linux Security Module[4]鉤子(以下簡稱LSM)需要Linux Kernel 5.7以上版本,而業界多數內核版本都不會這么新。Cilium有沒有使用LSM類HOOK進行阻斷呢?我們一起來看一下。

配置文件

前面提到,Tetragon靈活性更高,可以讀取配置文件規則,應用到內核態。以代碼倉庫的crds/examples/open_kill.yaml為例,語法規則分為如下幾部分

  1. kprobe函數名
  2. 函數原型參數
  3. 進程過濾配置
  4. 參數過濾配置
  5. 執行動作
3c94dfc2-e92d-11ec-ba43-dac502259ad0.jpg

其中,matchActions字段為匹配后的執行動作,比如這里的Sigkill

-call:"__x64_sys_write"
syscall:true
args:
-index:0
type:"fd"
-index:1
type:"char_buf"
sizeArgIndex:3
-index:2
type:"size_t"
selectors:
-matchPIDs:
-operator:NotIn
followForks:true
isNamespacePID:true
values:
-0
-1
matchArgs:
-index:0
operator:"Prefix"
values:
-"/etc/passwd"
matchActions:
-action:Sigkill

yaml配置文件的解析在pkg/k8s/apis/cilium.io/v1alpha1/types.go中的TracingPolicySpec結構體中,包含KProbeSpecTracepointSpec, 對應json:"kprobes"json:"tracepoints"兩個json的結構。

typeTracingPolicySpecstruct{
//+kubebuilderOptional
//Alistofkprobespecs.
KProbes[]KProbeSpec`json:"kprobes"`
//+kubebuilderOptional
//Alistoftracepointspecs.
Tracepoints[]TracepointSpec`json:"tracepoints"`
}

同時,Tetragon還支持遠程下發配置,配置結構與yaml結構是一樣的。這里相比傳統的內核模塊等技術方案,靈活性更高。

用戶空間

詳細執行流程會在下篇分享,直入主題。

數據結構

在項目中,抽象出一些概念:

  1. Tetragon由多個Sensors傳感器構成
  2. Sensor由多個Programs和Maps構成
  3. 每個Program對應eBPF代碼的HOOK函數
  4. 每個Map是相應Program的bpf的數據交互map
3cc19832-e92d-11ec-ba43-dac502259ad0.gif
//ProgramreprentsaBPFprogram.
typeProgramstruct{
//NameisthenameoftheBPFobjectfile.
Namestring
//Attachistheattachmentpoint,e.g.thekernelfunction.
Attachstring
//Labelistheprogramsectionnametoloadfromprogram.
Labelstring
//PinPathisthepinnedpathtothisprogram.Notethisisarelativepath
//basedontheBPFdirectoryFGSisrunningunder.
PinPathstring

//RetProbeindicateswhetherakprobeisakretprobe.
RetProbebool
//ErrorFatalindicateswhetheraprogrammustloadandfatalotherwise.
//Mostprogramwillsetthistotrue.Forexample,kernelfunctionshooks
//maychangeacrossverionssodifferentnamesareattempted,hence
//avoidingfatalingwhenthefirstattemptfails.
ErrorFatalbool

//Needsoverridebpfprogram
Overridebool

//TypeisthetypeofBPFprogram.Forexample,tc,skb,tracepoint,
//etc.
Typestring
LoadStateState

//TraceFDisneededbecausetracepointsareaddeddifferentthankprobes
//forexample.TheFDistokeepareferencetothetracepointprogramin
//ordertodeleteit.TODO:ThiscanbemovedintoloaderDatafor
//tracepoints.
TraceFDint

//LoaderDatarepresentsper-typespecificfields.
LoaderDatainterface{}

//unloaderfortheprogram.nilifnotloaded.
unloaderunloader.Unloader
}

ExecveV53=program.Builder(
"bpf_execve_event_v53.o",
"sched/sched_process_exec",
"tracepoint/sys_execve",
"event_execve",
"execve",
)
3d2390dc-e92d-11ec-ba43-dac502259ad0.jpg

Sensors傳感器加載

默認傳感器注冊

pkg/sensors/tracing包下由兩個文件對傳感器進行默認注冊,分別是generictracepoint.gogenerickprobe.go,寫入如下兩個Sensors到registeredTracingSensors中。

  1. observerKprobeSensor ,kprobe類型HOOK
  2. observerTracepointSensor, tracepoint類型HOOK

同時,還會注冊自定義eBPF probe加載器到registeredProbeLoad中。

//generickprobe.go#Line=43
funcinit(){
kprobe:=&observerKprobeSensor{
name:"kprobesensor",
}
sensors.RegisterProbeType("generic_kprobe",kprobe)
sensors.RegisterTracingSensorsAtInit(kprobe.name,kprobe)
observer.RegisterEventHandlerAtInit(ops.MSG_OP_GENERIC_KPROBE,handleGenericKprobe)
}

策略文件解析

GetSensorsFromParserPolicy方法遍歷所有Sensors,調用它的SpecHandler方法,解析yaml配置。解析配置后,生成新的傳感器對象,作為整個應用啟動、注入、監聽的所有傳感器。

//cmd/tetragon/main.go#line=209
startSensors,err=sensors.GetSensorsFromParserPolicy(&cnf.Spec)

//pkg/sensors/sensors.go#line=160
for_,s:=rangeregisteredTracingSensors{
sensor,err:=s.SpecHandler(spec)
iferr!=nil{
returnnil,err
}
ifsensor==nil{
continue
}
sensors=append(sensors,sensor)
}

新增傳感器

接上,yaml解析器讀取格式后,根據當前傳感器的類型(kprobe還是tracepoint),處理yaml配置文件對應的HOOK配置。

kprobe類型kprobe類型的hook配置為例,調用 addGenericKprobeSensors 詳細處理每一個配置內容。

//pkg/sensors/tracing/generickprobe.go#line=242-516
funcaddGenericKprobeSensors(kprobes[]v1alpha1.KProbeSpec,btfBaseFilestring)(*sensors.Sensor,error){
//遍歷所有kprobes的元素
fori:=rangekprobes{
f:=&kprobes[i]
funcName:=f.Call
//1.驗證配置文件中配置依賴,比如Sigkill需要內核大于5.3
// 2. 解析匹配參數(進程名、namespace、路徑、五元組等)寫入BTF對象
//3.解析ReturnArg返回值參數,寫入到BTF對象
//4.過濾保留參數,寫入BTF對象
//5.解析Filters邏輯,寫入BTF對象
//6.解析Binary名字到內核數據結構體
//7.將屬性寫入BTF指針,以便加載
//8.判斷action是否為SIGKILL,并寫入BTF對象
kernelSelectors,err:=selectors.InitKernelSelectors(f)
kprobeEntry:=genericKprobe{
loadArgs:kprobeLoadArgs{
filters:kernelSelectors,
btf:uintptr(btfobj),
retprobe:setRetprobe,
syscall:is_syscall,
},
argSigPrinters:argSigPrinters,
argReturnPrinters:argReturnPrinters,
userReturnFilters:userReturnFilters,
funcName:funcName,
pendingEvents:map[uint64]pendingEvent{},
tableId:idtable.UninitializedEntryID,
}
genericKprobeTable.AddEntry(&kprobeEntry)

//9.設定這個kprobe所需的ebpf字節碼文件信息

loadProgName:="bpf_generic_kprobe_v53.o"
// 10. 利用如上信息,填充到prog的結構體中。

//11.在prog結構體中,label字段的值都是**kprobe/generic_kprobe**
load:=program.Builder(
path.Join(option.Config.HubbleLib,loadProgName),
funcName,
"kprobe/generic_kprobe",
"kprobe"+"_"+funcName,
"generic_kprobe").
SetLoaderData(kprobeEntry.tableId)
}

//創建生成新的sensor,并返回
return&sensors.Sensor{
Name:"__generic_kprobe_sensors__",
Progs:progs,
Maps:[]*program.Map{},
},nil
}

解析過程如下:

  1. 驗證配置文件中配置依賴,比如Sigkill需要內核大于5.3
  2. 解析匹配參數(進程名、namespace、路徑、五元組等)寫入BTF對象
  3. 解析ReturnArg 返回值參數,寫入到BTF對象
  4. 過濾保留參數,寫入BTF對象
  5. 解析Filters邏輯,寫入BTF對象
  6. 解析Binary名字到內核數據結構體
  7. 將屬性寫入BTF指針,以便加載
  8. 判斷action是否為SIGKILL,并寫入BTF對象
  9. 設定這個kprobe所需的ebpf字節碼文件信息
  10. 利用如上信息,填充到prog的結構體中。
  11. 在prog結構體中,label字段的值都是kprobe/generic_kprobe

解析完成后,返回一個新的sensor,并添加到Sensors傳感器數組中。

至此,完成了配置文件的解析。在這里,阻斷指令的配置,是保存在genericKprobe.loadArgs.filters這個byte數組中的。

動態更新

在tetragon項目中,還具備從遠程下發新的規則,更新、添加Sensor傳感器功能,相應代碼在pkg/observer/observer.go中,本篇不做過多展開,會在下篇分享。

//InitSensorManagerstartsthesensorcontrollerandsttmanager.
func(k*Observer)InitSensorManager()error{
varerrerror
SensorManager,err=sensors.StartSensorManager(k.bpfDir,k.mapDir,k.ciliumDir)
returnerr
}

eBPF加載與掛載

Sensors啟動

傳感器啟動加載,執行流程為obs.Start(ctx, startSensors) -> config.LoadConfig -> load.Load 。

在Load方法里,對每一個Program元素,調用observerLoadInstance方法進行加載。

// load.Load

//Loadloadsthesensor,byloadingalltheBPFprogramsandmaps.
func(s*Sensor)Load(stopCtxcontext.Context,bpfDir,mapDir,ciliumDirstring)error{
for_,p:=ranges.Progs{
//...

//加載每個prog
iferr:=observerLoadInstance(stopCtx,bpfDir,mapDir,ciliumDir,p);err!=nil{
returnerr
}
//...
}
}

eBPF Program加載、掛載

在對每個eBPF program進行加載時,會判斷HOOK的類型,針對tracepoint特殊判斷處理。這里還是以Kprobe為例。代碼調用loadInstance函數,邏輯中判斷是否存在自定義的加載器

  1. 若有,則調用s.LoadProbe加載;
  2. 若沒有,則調用loader.LoadKprobeProgram加載;
//pkg/sensors/load.go#Line=297
ifs,ok:=registeredProbeLoad[load.Type];ok{
logger.GetLogger().WithField("Program",load.Name).WithField("Type",load.Type).Infof("Loadprobe")
returns.LoadProbe(LoadProbeArgs{
BPFDir:bpfDir,
MapDir:mapDir,
CiliumDir:ciliumDir,
Load:load,
Version:version,
Verbose:verbose,
})
}
returnloader.LoadKprobeProgram(
version,verbose,
btfObj,
load.Name,
load.Attach,
load.Label,
filepath.Join(bpfDir,load.PinPath),
mapDir,
load.RetProbe)

同樣,以前面提到的observerKprobeSensor類型傳感器,已經注冊自己的Probe加載器,那么會走s.LoadProbe()邏輯,之后,調用loadGenericKprobeSensor() -> loadGenericKprobe()進行加載。

這里的load.Name、load.Attach、load.Label的值,來自前面的yaml配置文件讀取部分,值分別為bpf_generic_kprobe_v53.o、 __x64_sys_write 、 kprobe/generic_kprobe,也就是說,不管是哪個kprobe函數,都會被掛載到kprobe/generic_kprobe上,都被generic_kprobe_event()這個eBPF 函數處理,起到統一管理的網關作用。

3d75e7b0-e92d-11ec-ba43-dac502259ad0.jpg

kprobe/generic_kprobe對應的eBPF代碼在bpf/process/bpf_generic_kprobe.c文件里,我們在后面內核空間代碼詳細分析。

__attribute__((section(("kprobe/generic_kprobe")),used))int
generic_kprobe_event(structpt_regs*ctx)
{
returngeneric_kprobe_start_process_filter(ctx);
}

filters過濾器

loadGenericKprobe()函數最后一個參數filters過濾器參數,類型是[4096]byte

funcloadGenericKprobe(bpfDir,mapDirstring,versionint,p*program.Program,btfuintptr,genmapDirstring,filters[4096]byte)error{
}

其內容為前面yaml格式解析中的第5步,

kernelSelectors,err:=selectors.InitKernelSelectors(f)

格式構成為

filter := [length][matchPIDs][matchBinaries][matchArgs][matchNamespaces][matchCapabilities][matchNamespaceChanges][matchCapabilityChanges]

這些數據,也是在內核空間eBPF邏輯中,實現參數匹配,動作響應的判斷依據。

Cgo函數調用

在調用BPF SYSCALL的實現上,Tetragon沒有使用母公司自己的Cilium/ebpf庫,而是使用CGo包裝了libbpf進行加載。使用的版本還是0.2.0,當前社區最新版為0.7.0,比較老。(下一篇再講原因)

loadGenericKprobe()函數調用bpf.LoadGenericKprobeProgram(),并把filters傳遞給下一個CGO的函數C.generic_kprobe_loader(),函數定義在pkg/bpf/loader.go的476行。

intgeneric_kprobe_loader(constintversion,
constintverbosity,
booloverride,
void*btf,
constchar*prog,
constchar*attach,
constchar*label,
constchar*__prog,
constchar*mapdir,
constchar*genmapdir,
void*filters){
structbpf_object*obj;
interr;
obj=generic_loader_args(version,verbosity,override,btf,prog,attach,
label,__prog,mapdir,filters,BPF_PROG_TYPE_KPROBE);
if(!obj){
return-1;
}
//...
}

之后,再調用CGO的C函數generic_loader_args()進行BPF SYSCALL調用,加載eBPF程序,掛載到對應kprobe函數上。之后,再寫入eBPF Maps。

3d9efe34-e92d-11ec-ba43-dac502259ad0.jpg

eBPF Maps創建寫入

Tetragon使用eBPF Maps進行用戶空間與內核空間的配置數據交互,比如yaml配置文件中,各種過濾條件,匹配后的處理動作等。

還是以open_kill.yaml為例,涉及了兩類eBPF Map

  1. 特征匹配規則,也就是配置的內容,比如需要保護的文件路徑、IP黑名單等,稱之為filters規則
  2. 路由分發規則,也就是tetragon程序內部,用于eBPF HOOK的函數網關處理各類參數的自用規則,用尾調用Tail Call類型的map實現。
3dc4c182-e92d-11ec-ba43-dac502259ad0.gif

filters規則Map

generic_loader_args()里,創建了"filter_map" eBPF Map,并將判斷規則、阻斷規則filter bytes數組寫入到這個map里,格式依舊是[4096]byte的字節流。

//...
char*filter_map="filter_map";
//...
map_fd=bpf_object__find_map_fd_by_name(obj,filter_map);
if(map_fd>=0){
err=bpf_map_update_elem(map_fd,&zero,filter,BPF_ANY);
if(err){
printf("WARNING:mapupdateelem%serror%d
",filter_map,err);
}
}

Tail Call尾調用Map

在eBPF Program加載部分提到,eBPF Kprobe函數kprobe/generic_kprobe(即generic_kprobe_event())作為統一的過濾處理網關,針對不同的kprobe,其參數個數、參數類型一定是不一樣的,比如文件讀寫函數__x64_sys_write有三個參數,分別是

args:
-index:0
type:"fd"
-index:1
type:"char_buf"
sizeArgIndex:3
-index:2
type:"size_t"

而SOCKET連接函數__x64_connect只有兩個參數,分別是

args:
-index:0
type:"sockfd"
-index:1
type:"sockaddr"

并且,他們的參數類型也不一樣,作為統一網關,且面對參數個數、參數類型都不一樣的問題,Tetragon在eBPF的解決方案是使用BPF_MAP_TYPE_PROG_ARRAY類型的eBPF Map實現,用于尾調用Tail Call處理。,

kprobe_calls尾調用Map

structbpf_map_def__attribute__((section("maps"),used))kprobe_calls={
.type=BPF_MAP_TYPE_PROG_ARRAY,
.key_size=sizeof(__u32),
.value_size=sizeof(__u32),
.max_entries=11,
};

kprobe_calls Map寫入

map_bpf=bpf_object__find_map_by_name(obj,"kprobe_calls");
//...
err=bpf_map__pin(map_bpf,map_name);
//...
map_fd=bpf_map__fd(map_bpf);

for(i=0;i11;i++){
structbpf_program*prog;
charprog_name[20];
charpin_name[200];
intfd;

snprintf(prog_name,sizeof(prog_name),"kprobe/%i",i);
prog=bpf_object__find_program_by_title(obj,prog_name);
if(!prog)
gotoout;
fd=bpf_program__fd(prog);
if(fd0){
err=errno;
gotoerr;
}
snprintf(pin_name,sizeof(pin_name),"%s_%i",__prog,i);
bpf_program__unpin(prog,pin_name);
err=bpf_program__pin(prog,pin_name);
if(err){
printf("programpin%stailcallerr%d
",pin_name,err);
gotoerr;
}
err=bpf_map_update_elem(map_fd,&i,&fd,BPF_ANY);
if(err){
printf("mapupdateelemi%i%stailcallerr%d%d
",i,prog_name,err,errno);
gotoerr;
}
}

用戶空間程序,讀取eBPF二進制文件,讀取kprobe開頭的的eBPF函數,以ID作為Key,寫入到kprobe_calls尾調用表中。

一共11個函數,都在bpf/process/bpf_generic_kprobe.c文件里,分別是:

  1. kprobe/0 對應 generic_kprobe_process_event0
  2. kprobe/1 對應 generic_kprobe_process_event1
  3. kprobe/2 對應 generic_kprobe_process_event2
  4. kprobe/3 對應 generic_kprobe_process_event3
  5. kprobe/4 對應 generic_kprobe_process_event4
  6. kprobe/5 對應 generic_kprobe_process_filter
  7. kprobe/6 對應 generic_kprobe_filter_arg1
  8. kprobe/7 對應 generic_kprobe_filter_arg2
  9. kprobe/8 對應 generic_kprobe_filter_arg3
  10. kprobe/9 對應 generic_kprobe_filter_arg4
  11. kprobe/10 對應 generic_kprobe_filter_arg5

至此,涉及eBPF阻斷功能的用戶空間邏輯全部完成。

內核空間

在內核空間,入口函數為用戶空間HOOK的kprobe點 "kprobe/generic_kprobe" ,對應 generic_kprobe_event() 函數。這函數內部只有一個 **generic_kprobe_start_process_filter()**的調用。

3e03f398-e92d-11ec-ba43-dac502259ad0.jpg

統一網關觸發

前面提到,tetragon是把open_kill.yaml中的所有kprobe syscall都交給這個eBPF函數處理,所以,當__x64_sys_write這些HOOK點觸發后,邏輯都交給generic_kprobe_start_process_filter()處理。

//bpf/process/bpf_generic_kprobe.c#line=48
staticinline__attribute__((always_inline))int
generic_kprobe_start_process_filter(void*ctx)
{
//...

/*Tailcallintofilters.*/
tail_call(ctx,&kprobe_calls,5);
return0;
}

程序內部獲取當前進程信息(struct task_struct、namespace、caps等)后,使用Tail Call尾調用轉交kprobe_calls Maps的第5個函數處理,即generic_kprobe_process_filter()。

提醒

因eBPF虛擬機寄存器限制,只能獲取前5個參數。

進程過濾流程

獲取當前進程信息后,進入下一個流程,獲取當前kprobe的進程參數。即進入generic_kprobe_process_filter()函數內部

//bpf/process/bpf_generic_kprobe.c#Line=146
//...
ret=generic_process_filter(msg,&filter_map,&process_call_heap);
if(ret==PFILTER_CONTINUE)
tail_call(ctx,&kprobe_calls,5);
elseif(ret==PFILTER_ACCEPT)
tail_call(ctx,&kprobe_calls,0);
/*Iffilterdoesnotacceptdropit.Ideallywewould
*logerrorcodesforlaterreview,TBD.
*/
returnPFILTER_REJECT;

這里對kprobe所在進程信息,以及配置文件中信息匹配,判斷是否要走過濾、阻斷流程。流程邏輯如下

PFILTER_ACCEPT 逐個進入5類進程event事件判斷

  1. generic_process_event0()
  2. generic_process_event1()
  3. generic_process_event2()
  4. generic_process_event3()
  5. generic_process_event4()
  6. generic_kprobe_filter_arg1()

PFILTER_CONTINUE直接進入參數判斷

  1. generic_kprobe_filter_arg1()

OTHER 其他情況

直接返回PFILTER_REJECTeBPF HOOK流程。

參數判斷流程

當前kprobe函數是否需要過濾判斷完成后,流程轉入真正的判斷邏輯中,即tailcals尾調用的第6個函數處理,即generic_kprobe_filter_arg1()

__attribute__((section(("kprobe/6")),used))int
generic_kprobe_filter_arg1(void*ctx)
{
returnfilter_read_arg(ctx,0,&process_call_heap,&filter_map,
&kprobe_calls,&override_tasks);
}

其中,核心處理函數為filter_read_arg(),第四個參數&filter_map就是來自用戶空間解析yaml的filters bytes數組。

filter_read_arg()函數判斷觸發當前kprobe的進程filter配置,若沒找到,則調用kprobe/7kprobe/9的尾調用函數,逐個查找filter配置。

阻斷動作執行

當找到filter配置后,則讀取配置中相應的action參數類型,開始進行相應動作分類判斷,執行相關流程邏輯,這塊都是在__do_action()函數中完成的。

3e37835c-e92d-11ec-ba43-dac502259ad0.jpg
actions=(structselector_action*)&f[actoff];

postit=do_actions(e,actions,override_tasks);

action動作的類型有下面幾種

  1. ACTION_POST = 0,
  2. ACTION_FOLLOWFD = 1,
  3. ACTION_SIGKILL = 2,
  4. ACTION_UNFOLLOWFD = 3,
  5. ACTION_OVERRIDE = 4,

每個action動作類型都有相應的處理邏輯,本文重點是阻斷的實現,那么只需要關注ACTION_SIGKILL類型。

staticinline__attribute__((always_inline))long
__do_action(longi,structmsg_generic_kprobe*e,
structselector_action*actions,structbpf_map_def*override_tasks)
{
intaction=actions->act[i];

switch(action){
caseACTION_UNFOLLOWFD:
caseACTION_FOLLOWFD:
//...
break;
caseACTION_SIGKILL:
if(bpf_core_enum_value(tetragon_args,sigkill))
send_signal(FGS_SIGKILL);
break;
caseACTION_OVERRIDE:

default:
break;
}
if(!err){
e->action=action;
return++i;
}
return-1;
}

可以看到,針對類型,是調用了send_signal()函數進行下發FGS_SIGKILL指令給當前進程,完整阻斷動作。send_signal()函數是ebpf的內置函數,在Kernel 5.3版本[5]里增加。

阻斷演示視頻可以到CNCF (Cloud Native Computing Foundation)的油管觀看:Real Time Security - eBPF for Preventing attacks - Liz Rice, Isovalent[6]

LSM HOOK比較LSM類HOOK是在Kernel 5.7以后才添加。阻斷功能的實現,Tetragon選擇send_signal()的方式,有著兼容更多內核版本的優勢。并且其kprobe的HOOK點上,可以實現網關式通用處理,通過配置方式,更靈活地變更HOOK點,避免更新eBPF字節碼的方式。

  1. 更靈活
  2. 網關式
  3. 內核版本覆蓋多

總結

Tetragon是一個實時識別阻斷的運行時防護系統。具備網關式統一處理抓手,可以覆蓋更多內核版本,通過配置文件方式靈活變更HOOK點。在eBPF技術支持下,還具備熱掛載,系統穩定性高,程序可靠性高等特點。是主機運行時防護系統HIDS的最佳學習項目。

3e4e1004-e92d-11ec-ba43-dac502259ad0.jpg

筆者水平有限,若有錯誤,歡迎指出,謝謝。

Tetragon阻斷爭論

2022年5月,云原生安全公司Isovalent的CTO宣布開源了其內部開發了多年的基于eBPF安全監控和阻斷的方案:Tetragon。稱可以防御容器逃逸的Linux內核漏洞。

安全研究人員Felix Wilhelm的質疑,在Tetragone: A Lesson in Security Fundamentals[7]認為可以輕易繞過,并用CVE-2021-22555[8]漏洞修改版演示。

這篇文章從標題上都充滿了各種嘲諷,Tetragon單詞加了e,大概是gone的諧音吧。grsecurity是linux 內核安全經驗非常深厚的大廠,對這個領域比較精通。但Tetragon的優勢并不是內核底層安全能力。

賽博堡壘(HardenedVault)也撰寫一篇文章,云原生安全Tetragon案例之安全產品自防護[9] 認為該產品必定失敗。

隨著云原生的流行,Linux內核安全成為了一個無法繞開的問題,某個容器被攻陷后可以向Linux內核發起攻擊,一旦成功則整個主機都會被攻擊者控制,如果你不想你的產品耗資上百萬美金后攻擊者兩個小時就攻陷的話,那應該認真的考慮是否應該從一開始就建立正確的威脅模型。另外,eBPF機制更適合實現審計監控類的安全方案而非防護阻斷類,VED的eBPF版本也僅僅是為審計而設計,剩下的事情你應該讓SIEM和SOC團隊去做,在安全流程上我們也應該遵循KISS(Keep it simple, stupid?。┰瓌t,不是嗎?

我的看法

針對漏洞利用的方法(注:不是漏洞)的防御機制通常會針對三個階段:

  1. Pre-exploitation(前漏洞利用階段)
  2. Exploitation(漏洞利用階段)
  3. Post-exploitation(后漏洞利用階段)

Tetragon的阻斷功能是在Exploitation漏洞利用階段生效的,因為是可以直接阻斷,讓此次漏洞攻擊失敗。其次,認可幾位安全人員的關于Tetragon適用場景說法,更適合阻斷用戶空間的內核逃逸漏洞。

否定的聲音來自底層防御的傳統廠商,其實他們沒明白,Tetragon的優勢是可以動態、輕量、無感知的提升防御能力,并不是完全防御所有攻擊方式。

業務優先于安全

在云原生領域,業務類型多數是web服務,安全級別不需要那么高,硬件宿主機重啟成本較高,性能要求大,業務優先,安全其次。過于嚴格的安全檢測,占用過多資源,影響業務運行速度,性價比低,成本高。這是云原生場景不能接受的。但偶爾的入侵行為是能容忍的。所以業務優先級大于安全是第一守則。

安全優先于業務

傳統安全廠商的產品對系統穩定性、可用性、性能都有較大影響,且存在熱更新的難題,哪怕解決了,依舊是特別重的方案。在輕量、動態、熱更新的需求下,顯得特別笨重。

機密數據庫等保密程度較高的服務器,數據安全大于業務功能,愿意犧牲性能換取安全性,那么這種場景適合傳統安全廠商的產品。這種場景的規則是安全優先級大于業務,更適合傳統安全廠商發揮。

爭議總結

當今互聯網的服務器市場中,云原生業務占比越來越高,這將會是Tetragon、Falco、Tracee、Datadog等運行時安全產品愈加內卷的動力。

對于用戶來說,根據自己的業務特性,選擇相應的防御檢測產品,滿足自己業務需求。

原文標題:Tetragon阻斷爭論

文章出處:【微信公眾號:一口Linux】歡迎添加關注!文章轉載請注明出處。

審核編輯:湯梓紅
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 內核
    +關注

    關注

    3

    文章

    1312

    瀏覽量

    39888
  • Linux
    +關注

    關注

    87

    文章

    11004

    瀏覽量

    206858
  • 云原生
    +關注

    關注

    0

    文章

    222

    瀏覽量

    7857

原文標題:Tetragon阻斷爭論

文章出處:【微信號:yikoulinux,微信公眾號:一口Linux】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    如何縮短Vivado的運行時

    在Vivado Implementation階段,有時是有必要分析一下什么原因導致運行時間(runtime)過長,從而找到一些方法來縮短運行時間。
    的頭像 發表于 05-29 14:37 ?1.4w次閱讀
    如何縮短Vivado的<b class='flag-5'>運行時</b>間

    云原生技術概述 云原生火爆成為升職加薪核心必備

    一、部署指南 二、集群方案 k8s 針對不同環境和場景可以使用不同的方案,涵蓋網絡、存儲、運行時、 Ingress、Metrics 等。 三、最佳實踐
    的頭像 發表于 07-27 10:23 ?1033次閱讀

    進擊的 Java ,云原生時代的蛻變

    。我們先拋開這些觀點,首先思考一下云原生對應用運行時的不同需求。體積更小 - 對于微服務分布式架構而言,更小的體積意味著更少的下載帶寬,更快的分發下載速度。啟動速度更快 - 對于傳統單體應用,啟動速度
    發表于 09-17 15:54

    只需 6 步,你就可以搭建一個云原生操作系統原型

    。第二步在操作系統之上,我們需要一個能夠運行容器的運行時。 傳統上,云原生和容器有非常強的關系,也就是說云原生是通過容器催生出來的。Linu
    發表于 09-15 14:01

    紫金橋組態軟件新的功能_運行時組態

    運行時組態是組態軟件新近提出的新的概念。運行時組態是在運行環境下對已有工程進行修改,添加新的功能。它不同于在線組態,在線組態是在工程運行的同時,進入組態環境,在組態環境中對工程進行修改
    發表于 10-13 16:17 ?2次下載
    紫金橋組態軟件新的功能_<b class='flag-5'>運行時</b>組態

    引領云原生2.0時代,賦能新云原生企業

    十年云計算浪潮下,DevOps、容器、微服務等技術飛速發展,云原生成為潮流。Forrester首席分析師戴鯤表示,云原生是企業數字化轉型的基礎,企業需要建立云原生優先的戰略,構建一體化全棧云原
    的頭像 發表于 12-11 16:04 ?1643次閱讀

    解讀騰訊云原生 鵝廠云原生的“新路”與“歷承”

    在云計算產業中,云原生是一個長期討論的“老話題”。而在今年新基建、產業數字化的宏觀背景下,云原生的應用主體開始擴張,關于這條技術路徑的討論也重新火熱了起來。 云原生突然“翻紅”的原因,或許在于更多
    的頭像 發表于 12-28 18:10 ?3299次閱讀

    什么是分布式云原生

    什么是分布式云原生 華為云分布式云原生服務(Ubiquitous Cloud Native Service, UCS)是業界首個分布式云原生產品,為企業提供云原生業務部署、管理、應用生
    發表于 07-27 15:52 ?1363次閱讀

    Go運行時:4年之后

    自 2018 年以來,Go GC,以及更廣泛的 Go 運行時,一直在穩步改進。近日,Go 社區總結了 4 年來 Go 運行時的一些重要變化。
    的頭像 發表于 11-30 16:21 ?551次閱讀

    如何在AUTOSAR OS系統運行時使用事件Event呢?

    在AUTOSAR OS系統中,事件用于向任務發送信號信息。本節解釋事件是什么,如何配置它們以及如何在運行時使用它們。
    發表于 05-22 10:04 ?1525次閱讀
    如何在AUTOSAR OS<b class='flag-5'>系統</b><b class='flag-5'>運行時</b>使用事件Event呢?

    JVM運行時數據區之堆內存

    說一下 JVM 運行時數據區吧,都有哪些區?分別是干什么的?
    的頭像 發表于 08-19 14:35 ?449次閱讀
    JVM<b class='flag-5'>運行時</b>數據區之堆內存

    ch32v307記錄程序運行時

    ,不僅會降低用戶的體驗,甚至可能會導致系統的崩潰。 因此,在程序設計和調試中,我們常常需要記錄程序的運行時間,并通過不斷的優化來提升程序的性能。本文將介紹如何在各種編程語言中記錄程序運行時
    的頭像 發表于 08-22 15:53 ?503次閱讀

    Xilinx運行時(XRT)發行說明

    電子發燒友網站提供《Xilinx運行時(XRT)發行說明.pdf》資料免費下載
    發表于 09-14 10:01 ?0次下載
    Xilinx<b class='flag-5'>運行時</b>(XRT)發行說明

    如何保證它們容器運行時的安全?

    緊密耦合的容器運行時繼承了主機操作系統的安全態勢和攻擊面。運行時或主機內核中的任何漏洞及其利用都會成為攻擊者的潛在切入點。
    的頭像 發表于 11-03 15:24 ?334次閱讀

    jvm運行時內存區域劃分

    的內存區域劃分對于了解Java程序的內存使用非常重要,本文將詳細介紹JVM運行時的內存區域劃分。 JVM運行時內存區域主要劃分為以下幾個部分: 程序計數器(Program Counter
    的頭像 發表于 12-05 14:08 ?274次閱讀
    亚洲欧美日韩精品久久_久久精品AⅤ无码中文_日本中文字幕有码在线播放_亚洲视频高清不卡在线观看
    <acronym id="s8ci2"><small id="s8ci2"></small></acronym>
    <rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
    <acronym id="s8ci2"></acronym>
    <acronym id="s8ci2"><center id="s8ci2"></center></acronym>