Ahojte, Habržitelia! Virtuálny stroj BPF je jednou z najdôležitejších súčastí jadra Linux. Jeho správne využitie umožní systémovým inžinierom odhaľovať chyby a riešiť aj tie najzložitejšie problémy. Naučíte sa vytvárať programy, ktoré sledujú a modifikujú správanie jadra, budete môcť bezpečne implementovať kód na sledovanie udalostí v jadre a ešte oveľa viac. David Calavera a Lorenzo Fontana vám pomôžu odhaliť možnosti BPF. Rozšírte svoje vedomosti o optimalizácii výkonu, sieťach a bezpečnosti. — Používajte BPF na sledovanie a modifikáciu správania jadra Linux. — Implementujte kód na bezpečné monitorovanie udalostí v jadre — bez nutnosti prekompilovať jadro alebo reštartovať systém. — Využívajte praktické ukážky kódu v C, Go alebo Pythone. — Ovládajte situáciu, majúc na pamäti životný cyklus programu BPF.
Bezpečnosť jadra Linux, jeho možnosti a Seccomp
BPF poskytuje mocný spôsob, ako rozšíriť jadro bez ohrozenia stability, bezpečnosti a rýchlosti. Z tohto dôvodu si vývojári jadra povedali, že by bolo dobré využiť jeho univerzálnosť na zlepšenie izolácie procesov v Seccomp implementovaním filtrov Seccomp podporovaných programami BPF, známymi aj ako Seccomp BPF. V tejto kapitole si povieme, čo je Seccomp a ako sa používa. Potom sa naučíte, ako písať filtre Seccomp pomocou programov BPF. Následne si predstavíme vstavané zachytávače BPF, ktoré sú v jadre pre moduly bezpečnosti Linux.
Moduly bezpečnosti Linux (LSM) sú platforma, ktorá poskytuje súbor funkcií, ktoré je možné aplikovať na štandardizované implementovanie rôznych bezpečnostných modelov. LSM môže byť použitá priamo v strome zdrojového kódu jadra, napríklad Apparmor, SELinux a Tomoyo.
Začneme diskusiou o možnostiach Linuxu.
Možnosti
Podstata možností Linux spočíva v tom, že musíte poskytnúť neprivilegovanému procesu povolenie na vykonanie určitej úlohy, ale bez toho, aby ste na tento účel používali suid, alebo inak spravili proces privilegovaným, čím sa znižuje možnosť útokov a zabezpečuje procesu možnosť vykonávať určité úlohy. Napríklad, ak vaše aplikácie potrebujú otvoriť privilegovaný port, povedzme 80, namiesto spustenia procesu pod menom root mu môžete jednoducho poskytnúť povolenie CAP_NET_BIND_SERVICE.
Pozrime sa na program Go s názvom main.go:
package main
import (
"net/http"
"log"
)
func main() {
log.Fatalf("%v", http.ListenAndServe(":80", nil))
}Tento program obsluhuje HTTP server na porte 80 (toto je privilegovaný port). Zvyčajne ho spúšťame hneď po kompilácii:
$ go build -o capabilities main.go
$ .\/capabilitiesAvšak, keďže neposkytujeme privilegium root, tento kód vygeneruje chybu pri viazaní portu:
2019\/04\/25 23:17:06 listen tcp :80: bind: permission denied
exit status 1capsh (nástroj na správu shellu) je nástroj, ktorý spúšťa shell s určitým súborom možností.
V tomto prípade, ako už bolo povedané, namiesto poskytovania plných práv root môžeme povoliť viazanie privilegovaných portov, poskytnutím možnosti cap_net_bind_service spolu so všetkými ostatnými, ktoré už existujú v programe. Na to môžeme uzavrieť náš program do capsh:
# capsh --caps='cap_net_bind_service+eip cap_setpcap,cap_setuid,cap_setgid+ep'
--keep=1 --user="nobody"
--addamb=cap_net_bind_service -- -c "./capabilities"Trochu sa pozrime na tento príkaz.
- capsh — používame capsh ako shell.
- —caps='cap_net_bind_service+eip cap_setpcap,cap_setuid,cap_setgid+ep' — keďže potrebujeme zmeniť používateľa (nechceme bežať s právami root), uvedieme cap_net_bind_service a možnosť fakticky zmeniť identifikátor používateľa z root na nobody, a to konkrétne cap_setuid a cap_setgid.
- —keep=1 — chceme si zachovať nastavené možnosti, keď je vykonané prepínanie z účtu root.
- —user="nobody" — konečným používateľom, ktorý spúšťa program, bude nobody.
- —addamb=cap_net_bind_service — nastavujeme vyčistenie súvisiacich možností po prechode z režimu root.
- — -c ".\/capabilities" — jednoducho spúšťame program.
Súvisiace možnosti sú osobitný druh možností, ktoré sú zdedené potomkami programov, keď aktuálny program ich vykonáva pomocou execve(). Dediť sa môžu iba možnosti, ktoré sú povolené ako súvisiace, alebo inými slovami, ako možnosti prostredia.
Pravdepodobne vás zaujíma, čo znamená +eip po uvedení možnosti v voľbe —caps. Tieto praporky sa používajú na určenie toho, čo znamená možnosť:
-musí byť aktivovaná (p);
-je dostupná na použitie (e);
-môže byť zdedená dcérskymi procesmi (i).
Keďže chceme použiť cap_net_bind_service, musíme tak urobiť s praporkom e. Potom spustíme shell v príkaze. V dôsledku toho sa spustí binárny súbor capabilities a musíme ho označiť praporkom i. Nakoniec chceme, aby bola možnosť aktivovaná (to sme urobili bez zmeny UID) pomocou p. Vyzerá to ako cap_net_bind_service+eip.
Môžete skontrolovať výsledok pomocou ss. Trochu skrátime výstup, aby sa vošiel na stránku, ale ukáže súvisiaci port a identifikátor používateľa, ktorý nie je 0, v tomto prípade 65 534:
# ss -tulpn -e -H | cut -d' ' -f17-
128 *:80 *:*
users:(("capabilities",pid=30040,fd=3)) uid:65534 ino:11311579 sk:2c v6only:0V tomto príklade sme použili capsh, ale môžete napísať shell pomocou libcap. Pre ďalšie informácie sa obráťte na man 3 libcap.
Pri písaní programov vývojár dosť často nevie vopred všetky možnosti potrebné pre program počas behu; navyše, v nových verziách sa tieto možnosti môžu zmeniť.
Aby sme lepšie pochopili možnosti nášho programu, môžeme vziať nástroj BCC capable, ktorý nastavuje kprobe pre funkciu jadra cap_capable:
/usr/share/bcc/tools/capable
TIME UID PID TID COMM CAP NAME AUDIT
10:12:53 0 424 424 systemd-udevd 12 CAP_NET_ADMIN 1
10:12:57 0 1103 1101 timesync 25 CAP_SYS_TIME 1
10:12:57 0 19545 19545 capabilities 10 CAP_NET_BIND_SERVICE 1To isté môžeme dosiahnuť pomocou bpftrace s jednoradkovým kprobe vo funkcii jadra cap_capable:
bpftrace -e
'kprobe:cap_capable {
time("%H:%M:%S ");
printf("%-6d %-6d %-16s %-4d %dn", uid, pid, comm, arg2, arg3);
}'
| grep -i capabilitiesTo vyprodukuje niečo podobné nasledujúcemu, ak sú možnosti nášho programu aktivované po kprobe:
12:01:56 1000 13524 capabilities 21 0
12:01:56 1000 13524 capabilities 21 0
12:01:56 1000 13524 capabilities 21 0
12:01:56 1000 13524 capabilities 12 0
12:01:56 1000 13524 capabilities 12 0
12:01:56 1000 13524 capabilities 12 0
12:01:56 1000 13524 capabilities 12 0
12:01:56 1000 13524 capabilities 10 1Piata stĺpec — to sú možnosti, ktoré proces potrebuje, a pretože tieto výstupy obsahujú aj neauditované udalosti, vidíme všetky neauditované overovania a nakoniec požadovanú možnosť s praporkom auditu (posledný vo výstupe), nastaveným na 1. Možnosť, ktorá nás zaujíma, — CAP_NET_BIND_SERVICE, je definovaná ako konštanta v zdrojovom kóde jadra v súbore include/uapi/linux/ability.h s identifikátorom 10:
/* Allows binding to TCP/UDP sockets below 1024 */
/* Allows binding to ATM VCIs below 32 */
#define CAP_NET_BIND_SERVICE 10<source lang="go">Možnosti sa často využívajú pri spúšťaní kontajnerov, ako sú runC alebo Docker, aby mohli fungovať v neprivilegovanom režime, ale s povolením len tých možností, ktoré sú potrebné na spustenie väčšiny aplikácií. Keď aplikáciám chýbajú určité možnosti, je možné ich v Dockeri poskytnúť pomocou —cap-add:
docker run -it --rm --cap-add=NET_ADMIN ubuntu ip link add dummy0 type dummyTento príkaz poskytne kontajneru možnosť CAP_NET_ADMIN, čo mu umožní konfigurovať sieťové odkazy na pridanie rozhrania dummy0.
V nasledujúcej časti je uvedené využitie takýchto možností, ako je filtrovanie, ale pomocou inej metódy, ktorá nám umožní programovo implementovať vlastné filtre.
Seccomp
Seccomp znamená Secure Computing, čo je úroveň bezpečnosti implementovaná v jadre Linuxu, ktorá umožňuje vývojárom filtrovať určité systémové volania. Hoci je Seccomp porovnateľný s možnosťami Linuxu, jeho schopnosť spravovať určité systémové volania ho robí oveľa flexibilnejším v porovnaní s nimi.
Seccomp a možnosti Linuxu sa navzájom nevylučujú, často sa používajú spolu, aby sa využili výhody oboch prístupov. Napríklad môžete chcieť poskytnúť procesu možnosť CAP_NET_ADMIN, ale zabrániť mu v akceptovaní pripojení cez socket, blokovaním systémových volaní accept a accept4.
Spôsob filtrovania Seccomp je založený na filtroch BPF, ktoré fungujú v režime SECCOMP_MODE_FILTER a filtrovanie systémových volaní prebieha podobne ako pri paketoch.
Filtrovanie Seccomp sa nahráva pomocou prctl cez operáciu PR_SET_SECCOMP. Tieto filtre majú formu programu BPF, ktorý sa vykonáva pre každý paket Seccomp, ktorý je predložený pomocou štruktúry seccomp_data. Táto štruktúra obsahuje referenčnú architektúru, ukazovateľ na inštrukcie procesora počas systémového volania a maximálne šesť argumentov systémového volania, vyjadrených ako uint64.
Tu je ukážka štruktúry seccomp_data zo zdrojového kódu jadra v súbore linux/seccomp.h:
struct seccomp_data {
int nr;
__u32 arch;
__u64 instruction_pointer;
__u64 args[6];
};Ako je vidieť z tejto štruktúry, môžeme filtrovať podľa systémového volania, jeho argumentov alebo ich kombinácie.
Po prijatí každého balíka musí filtr Seccomp vykonať spracovanie, aby prijal konečné rozhodnutie a informoval jadro, čo má robiť ďalej. Konečné rozhodnutie je vyjadrené jednou z návratových hodnôt (stavových kódov).
— SECCOMP_RET_KILL_PROCESS — okamžité ukončenie celého procesu po filtrovaní systémového volania, ktoré sa z tohto dôvodu nevykonáva.
— SECCOMP_RET_KILL_THREAD — okamžité ukončenie aktuálnej vlákna po filtrovaní systémového volania, ktoré sa z tohto dôvodu nevykonáva.
— SECCOMP_RET_KILL — alias pre SECCOMP_RET_KILL_THREAD, ponechaný pre spätnú kompatibilitu.
— SECCOMP_RET_TRAP — systémové volanie je zakázané a signál SIGSYS (Bad System Call) je odoslaný volajúcej úlohe.
— SECCOMP_RET_ERRNO — systémové volanie sa nevykonáva a časť návratovej hodnoty filtra SECCOMP_RET_DATA je prenášaná do používateľského priestoru ako hodnota errno. V závislosti od dôvodu chyby sa vracajú rôzne hodnoty errno. Zoznam chybových čísel je uvedený v nasledujúcej sekcii.
— SECCOMP_RET_TRACE — používa sa na upozornenie ladčiaceho nástroja ptrace pomocou — PTRACE_O_TRACESECCOMP na zachytenie, keď je vykonané systémové volanie, aby sa tento proces mohol monitorovať a kontrolovať. Ak nie je pripojený žiadny ladčiaci nástroj, vráti sa chyba, errno sa nastaví na -ENOSYS a systémové volanie sa nevykonáva.
— SECCOMP_RET_LOG — systémové volanie je povolené a zaznamenané do protokolu.
— SECCOMP_RET_ALLOW — systémové volanie je jednoducho povolené.
ptrace je systémové volanie na implementáciu mechanizmov sledovania v procese nazývanom tracee, s možnosťou pozorovania a kontroly vykonávania procesu. Program sledovania môže účinne ovplyvniť vykonávanie a meniť registre pamäte tracee. V kontexte Seccomp sa ptrace používa, keď sa spúšťa kódom stavu SECCOMP_RET_TRACE, čo umožňuje ladčiacemu nástroju zabrániť vykonaniu systémového volania a implementovať svoju vlastnú logiku.
Chyby Seccomp
Občas, pri práci so Seccomp, sa stretnete s rôznymi chybami, ktoré sú identifikované návratovou hodnotou typu SECCOMP_RET_ERRNO. Ak chcete oznámiť chybu, systémové volanie seccomp vráti -1 namiesto 0.
Nasledujúce chyby sú možné:
— EACCESS — volajúcej strane nie je povolené vykonávať systémový volanie. Zvyčajne sa to deje preto, lebo nemá privilégiá CAP_SYS_ADMIN alebo nebola nastavená no_new_privs pomocou prctl (o tom si povieme neskôr);
— EFAULT — odovzdané argumenty (args v štruktúre seccomp_data) nemajú platnú adresu;
— EINVAL — tu môžu byť štyri dôvody:
- požadovaná operácia je neznáma alebo nie je podporovaná jadrom v aktuálnej konfigurácii;
- zadané flagy sú neplatné pre požadovanú operáciu;
- operácia zahŕňa BPF_ABS, ale sú problémy s uvedeným posunom, ktorý môže prekračovať veľkosť štruktúry seccomp_data;
- počet inštrukcií odovzdaných vo filtri presahuje maximum;
— ENOMEM — nedostatok pamäte na vykonanie programu;
— EOPNOTSUPP — operácia uviedla, že s SECCOMP_GET_ACTION_AVAIL bola akcia dostupná, avšak jadro nepodporuje návrat v argumentoch;
— ESRCH — nastal problém pri synchronizácii iného vlákna;
— ENOSYS — neexistuje trasovač pripojený k akcii SECCOMP_RET_TRACE.
prctl je systémové volanie, ktoré umožňuje programu užívateľského priestoru spravovať (nastavovať a získavať) konkrétne aspekty procesu, ako sú poradové číslo byte, názvy vlákien, režim zabezpečených výpočtov (Seccomp), privilégiá, udalosti Perf a pod.
Seccomp sa môže zdať ako technológia pieskoviska, ale nie je to tak. Seccomp je nástroj, ktorý umožňuje používateľom vyvíjať mechanizmus pieskoviska. Teraz sa pozrime, ako sa vytvárajú programy užívateľských interakcií pomocou filtra, ktorý sa volá priamo systémovým volaním Seccomp.
Príklad filtra BPF Seccomp
Tu ukážeme, ako spojiť dve predtým posudzované akcie, a to:
— napíšeme program Seccomp BPF, ktorý sa bude používať ako filter s rôznymi kódmi návratu v závislosti od prijímaných rozhodnutí;
— načítame filter pomocou prctl.
Na začiatok potrebujeme hlavičky z štandardnej knižnice a jadra Linux:
#include <errno.h>
#include <linux/audit.h>
#include <linux/bpf.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <linux/unistd.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/prctl.h>
#include <unistd.h>Predtým, ako sa pokúsime vykonať tento príklad, musíme sa uistiť, že jadro je skompilované s CONFIG_SECCOMP a CONFIG_SECCOMP_FILTER nastavenými na y. Na pracovnej stanici sa to dá skontrolovať takto:
cat /proc/config.gz | zcat | grep -i CONFIG_SECCOMP
Zvyšok kódu predstavuje funkciu install_filter, ktorá sa skladá z dvoch častí. Prvá časť obsahuje náš zoznam inštrukcií na filtrovanie BPF:
static int install_filter(int nr, int arch, int error) {
struct sock_filter filter[] = {
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, (offsetof(struct seccomp_data, arch))),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arch, 0, 3),
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, (offsetof(struct seccomp_data, nr))),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, nr, 0, 1),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ERRNO | (error & SECCOMP_RET_DATA)),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),
}; Inštrukcie sa nastavujú pomocou makier BPF_STMT a BPF_JUMP definovaných vo fieldu linux/filter.h.
Prejdeme si inštrukcie.
— BPF_STMT(BPF_LD + BPF_W + BPF_ABS (offsetof(struct seccomp_data, arch))) — systém načíta a akumuluje s BPF_LD vo forme slova BPF_W, paketové dáta sa nachádzajú s pevným posunom BPF_ABS.
— BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arch, 0, 3) — overuje pomocou BPF_JEQ, či je hodnota architektúry v konštante akumulátora BPF_K rovná arch. Ak áno, prejde s posunom 0 k ďalšej inštrukcii, inak, aby vydal chybu, preskočí s posunom 3 (v tomto prípade), pretože arch sa nezhoduje.
— BPF_STMT(BPF_LD + BPF_W + BPF_ABS (offsetof(struct seccomp_data, nr))) — načíta a akumuluje s BPF_LD vo forme slova BPF_W, ktoré je číslom systémového volania nachádzajúceho sa s pevným posunom BPF_ABS.
— BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, nr, 0, 1) — porovnáva číslo systémového volania s hodnotou premennej nr. Ak sa rovnajú, prejde k ďalšej inštrukcii a zamietne systémové volanie, inak povoľuje systémové volanie s SECCOMP_RET_ALLOW.
— BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ERRNO | (error & SECCOMP_RET_DATA)) — ukončuje program s BPF_RET a výsledkom vydáva chybu SECCOMP_RET_ERRNO s číslom z premennej err.
— BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW) — ukončuje program s BPF_RET a povoľuje vykonanie systémového volania pomocou SECCOMP_RET_ALLOW.
SECCOMP — JE TO CBPF
Možno sa pýtate, prečo sa namiesto skompilovaného objektu ELF alebo programu v C, skompilovaného s JIT, používa zoznam inštrukcií.Na to sú dve príčiny.
• Po prvé, Seccomp aplikuje cBPF (klasický BPF), a nie eBPF, čo znamená: nemá registry, iba akumulátor na uchovávanie posledného výsledku výpočtov, čo je vidieť v príklade.
• Po druhé, Seccomp prijíma ukazovateľ na pole inštrukcií BPF priamo a nič viac. Makrá, ktoré sme použili, iba pomáhajú určiť tieto inštrukcie v prehľadnej forme pre programátorov.
Ak potrebujete ďalšiu pomoc pri porozumení tejto zostave, zvážte pseudokód, ktorý robí to isté:
if (arch != AUDIT_ARCH_X86_64) {
return SECCOMP_RET_ALLOW;
}
if (nr == __NR_write) {
return SECCOMP_RET_ERRNO;
}
return SECCOMP_RET_ALLOW;Po určení kódu filtra v štruktúre socket_filter je potrebné určiť sock_fprog, ktorá obsahuje kód a vypočítanú dĺžku filtra. Táto dátová štruktúra je potrebná ako argument na vyhlásenie fungovania procesu v budúcnosti:
struct sock_fprog prog = {
.len = (unsigned short)(sizeof(filter) / sizeof(filter[0])),
.filter = filter,
};Ostáva len jedno, čo treba urobiť vo funkcii install_filter - načítať samotný program! Na tento účel použijeme prctl, pričom PR_SET_SECCOMP uvedieme ako voľbu na prechod do režimu chránených výpočtov. Potom nastavíme režim tak, aby načítaval filter pomocou SECCOMP_MODE_FILTER, ktorý je obsiahnutý v premennej prog typu sock_fprog:
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
perror("prctl(PR_SET_SECCOMP)");
return 1;
}
return 0;
}Nakoniec môžeme využiť našu funkciu install_filter, avšak predtým musíme aktivovať prctl, aby sme nastavili PR_SET_NO_NEW_PRIVS pre aktuálne vykonávanie a tým sa vyhli situácii, keď dcérske procesy získavajú širšie privilégiá ako rodičovské. V tomto prípade môžeme robiť nasledujúce volania prctl vo funkcii install_filter bez práv root.
Teraz môžeme zavolať funkciu install_filter. Zablokujeme všetky systémové volania write, týkajúce sa architektúry X86-64, a jednoducho umožníme blokovanie všetkých pokusov. Po nastavení filtra pokračujeme vo vykonávaní s použitím prvého argumentu:
int main(int argc, char const *argv[]) {
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
perror("prctl(NO_NEW_PRIVS)");
return 1;
}
install_filter(__NR_write, AUDIT_ARCH_X86_64, EPERM);
return system(argv[1]);
}Poďme na to. Na kompiláciu našej aplikácie môžeme použiť buď clang, alebo gcc, inak je to len kompilácia súboru main.c bez špeciálnych možností:
clang main.c -o filter-writeAko bolo uvedené, zablokovali sme všetky zápisy v programe. Ak to chceme overiť, potrebujeme program, ktorý niečo vypisuje — ls sa javí ako dobrý kandidát. Takto sa obvykle správa:
ls -la
total 36
drwxr-xr-x 2 fntlnz users 4096 Apr 28 21:09 .
drwxr-xr-x 4 fntlnz users 4096 Apr 26 13:01 ..
-rwxr-xr-x 1 fntlnz users 16800 Apr 28 21:09 filter-write
-rw-r--r-- 1 fntlnz users 19 Apr 28 21:09 .gitignore
-rw-r--r-- 1 fntlnz users 1282 Apr 28 21:08 main.c
Skvele! Takto vyzerá použitie našej príkazovej aplikácie: jednoducho prenášame program, ktorý chceme otestovať, ako prvý argument:
.\/filter-write "ls -la"Po vykonaní tento program vráti úplne prázdny výstup. Avšak môžeme použiť strace, aby sme videli, čo sa deje:
strace -f .\/filter-write "ls -la"Výsledok práce je silne skrátený, ale príslušná časť ukazuje, že zápisy sú blokované s chybou EPERM — tou, ktorú sme nastavili. To znamená, že program nič nevracia, pretože nemôže získať prístup k systémovému volaniu write:
[pid 25099] write(2, "ls: ", 4) = -1 EPERM (Operácia nie je povolená)
[pid 25099] write(2, "chyba pri zápise", 15) = -1 EPERM (Operácia nie je povolená)
[pid 25099] write(2, "n", 1) = -1 EPERM (Operácia nie je povolená)Teraz rozumiete, ako funguje Seccomp BPF, a dobre si predstavujete, čo sa s ním dá dosiahnuť. Ale či nechcete dosiahnuť to isté s použitím eBPF namiesto cBPF, aby ste využili jeho plnú silu?
Pri zmýšľaní o programoch eBPF si väčšina ľudí myslí, že ich jednoducho píšu a nahrávajú s oprávneniami administrátora. Hoci to platí vo všeobecnosti, jadro implementuje súbor mechanizmov na ochranu objektov eBPF na rôznych úrovniach. Tieto mechanizmy sa nazývajú BPF LSM háčiky.
BPF LSM háčiky
Aby sa zabezpečila nezávislá kontrola systémových udalostí bez ohľadu na architektúru, LSM implementuje koncept háčikov. Technicky je volanie háčika podobné systémovému volaniu, avšak je nezávislé od systému a integrované do infraštruktúry. LSM poskytuje nový koncept, v ktorom úroveň abstrakcie dokáže pomôcť vyhnúť sa problémom pri práci so systémovými volaniami na rôznych architektúrach.
V čase písania knihy malo jadro sedem háčikov súvisiacich s BPF programami, pričom SELinux je jediný vstavaný LSM, ktorý ich implementuje.
Zdrojový kód háčikov je umiestnený v strome jadra v súbore include/linux/security.h:
extern int security_bpf(int cmd, union bpf_attr *attr, unsigned int size);
extern int security_bpf_map(struct bpf_map *map, fmode_t fmode);
extern int security_bpf_prog(struct bpf_prog *prog);
extern int security_bpf_map_alloc(struct bpf_map *map);
extern void security_bpf_map_free(struct bpf_map *map);
extern int security_bpf_prog_alloc(struct bpf_prog_aux *aux);
extern void security_bpf_prog_free(struct bpf_prog_aux *aux);Každá z nich bude vyvolaná v rôznych fázach vykonávania:
— security_bpf — vykonáva prvotnú kontrolu vykonaných systémových volaní BPF;
— security_bpf_map — kontroluje, keď jadro vracia súborový deskriptor pre mapu;
— security_bpf_prog — kontroluje, keď jadro vracia súborový deskriptor pre program eBPF;
— security_bpf_map_alloc — kontroluje, či je pole zabezpečenia inicializované vo vnútri máp BPF;
— security_bpf_map_free — kontroluje, či sa vykonáva čistenie poľa zabezpečenia vo vnútri máp BPF;
— security_bpf_prog_alloc — kontroluje, či je pole zabezpečenia inicializované vo vnútri programov BPF;
— security_bpf_prog_free — kontroluje, či sa čistí pole zabezpečenia vo vnútri programov BPF.
Teraz, keď vidíme všetko toto, chápeme: nápad LSM BPF interceptors spočíva v tom, že môžu zabezpečiť ochranu každého objektu eBPF, čím zaručujú, že iba tie, ktoré majú príslušné privilégia, môžu vykonávať operácie s mapami a programami.
Zhrnutie
Bezpečnosť nie je niečo, čo môžete implementovať univerzálne na všetko, čo chcete chrániť. Je dôležité mať možnosť chrániť systémy na rôznych úrovniach a rôznymi spôsobmi. Chcete veríte, chcete nie, najlepší spôsob zabezpečenia systému je organizovať rôzne úrovne ochrany z rôznych pozícií, aby zníženie bezpečnosti jednej úrovne neumožnilo prístup k celému systému. Vývojári jadra odviedli skvelú prácu, keď nám poskytli sadu rôznych vrstiev a interakčných bodov. Dúfame, že sme vám dali dobrý prehľad o tom, čo sú vrstvy a ako používať programy BPF na ich spracovanie.
O autoroch
David Calavera je technický riaditeľ v Netlify. Pracoval v podpore Dockeru a zúčastnil sa na vývoji nástrojov Runc, Go a BCC, ako aj na iných projektoch s otvoreným zdrojovým kódom. Je známy svojou prácou na projektoch Docker a rozvoji ekosystému pluginov Docker. David veľmi miluje flame grafy a vždy sa snaží o optimalizáciu výkonu.
Lorenzo Fontana pracuje v tíme vývojárov softvéru s otvoreným zdrojovým kódom v Sysdig, kde sa prevažne zaoberá projektom Falco — projektom Cloud Native Computing Foundation, ktorý zabezpečuje bezpečnosť prostredia kontajnerov a detekciu anomálií prostredníctvom jadrového modulu a eBPF. Je nadšený pre distribuované systémy, softvérovo definované siete, Linuxové jadro a analýzu výkonu.
» Podrobnejšie informácie o knihe nájdete na
»
»
Pre Habr čitateľov zľava 25% s kupónom — Linux
Po zaplatení tlačenej verzie knihy vám na e-mail bude zaslaná elektronická kniha.
Zdroj: habr.com
