Knyga "BPF Linux stebėjimui"

Knyga "BPF Linux stebėjimui"Sveiki, Khabro gyventojai! BPF virtuali mašina yra vienas iš svarbiausių Linux branduolio komponentų. Tinkamas jo naudojimas leis sistemų inžinieriams rasti gedimus ir išspręsti net sudėtingiausias problemas. Sužinosite, kaip rašyti programas, kurios stebi ir modifikuoja branduolio elgseną, kaip saugiai įdiegti kodą, kad būtų galima stebėti branduolio įvykius, ir dar daugiau. David Calavera ir Lorenzo Fontana padės jums atskleisti BPF galią. Išplėskite savo žinias apie našumo optimizavimą, tinklų kūrimą, saugumą. - Naudokite BPF, kad galėtumėte stebėti ir keisti Linux branduolio elgseną. - Įveskite kodą, kad galėtumėte saugiai stebėti branduolio įvykius, nereikia iš naujo kompiliuoti branduolį arba iš naujo paleisti sistemą. — Naudokite patogius kodų pavyzdžius C, Go arba Python. - Perimkite kontrolę turėdami BPF programos gyvavimo ciklą.

„Linux“ branduolio sauga, jos funkcijos ir „Seccomp

BPF suteikia galingą būdą išplėsti branduolį neprarandant stabilumo, saugumo ar greičio. Dėl šios priežasties branduolio kūrėjai manė, kad būtų gera idėja panaudoti jo universalumą, siekiant pagerinti procesų izoliavimą Seccomp, įdiegiant Seccomp filtrus, palaikomus BPF programų, dar žinomų kaip Seccomp BPF. Šiame skyriuje paaiškinsime, kas yra Seccomp ir kaip jis naudojamas. Tada išmoksite rašyti Seccomp filtrus naudodami BPF programas. Po to pažvelgsime į įtaisytuosius BPF kabliukus, kurie yra įtraukti į Linux saugos modulių branduolį.

„Linux Security Modules“ (LSM) – tai sistema, suteikianti funkcijų rinkinį, kurį galima naudoti standartizuotai diegiant įvairius saugos modelius. LSM gali būti naudojamas tiesiogiai branduolio šaltinio medyje, pvz., Apparmor, SELinux ir Tomoyo.

Pradėkime aptardami Linux galimybes.

galimybės

Linux galimybių esmė ta, kad reikia suteikti neprivilegijuotam procesui leidimą atlikti tam tikrą užduotį, bet nenaudojant suid tam tikslui, arba kitaip padaryti procesą privilegijuotu, sumažinant atakos galimybę ir leidžiant procesui atlikti tam tikras užduotis. Pavyzdžiui, jei programai reikia atidaryti privilegijuotąjį prievadą, tarkime, 80, o ne vykdyti procesą kaip root, galite tiesiog suteikti jai CAP_NET_BIND_SERVICE galimybę.

Apsvarstykite Go programą, pavadintą main.go:

package main
import (
            "net/http"
            "log"
)
func main() {
     log.Fatalf("%v", http.ListenAndServe(":80", nil))
}

Ši programa aptarnauja HTTP serverį 80 prievade (tai yra privilegijuotas prievadas). Paprastai paleidžiame iškart po kompiliavimo:

$ go build -o capabilities main.go
$ ./capabilities

Tačiau kadangi mes nesuteikiame root teisių, šis kodas surišdamas prievadą parodys klaidą:

2019/04/25 23:17:06 listen tcp :80: bind: permission denied
exit status 1

capsh (apvalkalo tvarkyklė) yra įrankis, paleidžiantis apvalkalą su tam tikromis galimybėmis.

Šiuo atveju, kaip jau minėta, užuot suteikę visas pagrindines teises, galite įgalinti privilegijuotąjį prievado susiejimą suteikdami cap_net_bind_service galimybę kartu su viskuo, kas jau yra programoje. Norėdami tai padaryti, savo programą galime pridėti didžiosiomis raidėmis:

# 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"

Supraskime šią komandą šiek tiek.

  • capsh – naudokite capsh kaip apvalkalą.
  • —caps='cap_net_bind_service+eip cap_setpcap,cap_setuid,cap_setgid+ep' – kadangi turime pakeisti vartotoją (nenorime paleisti kaip root), nurodysime cap_net_bind_service ir galimybę iš tikrųjų pakeisti vartotojo ID iš root niekam, būtent cap_setuid ir cap_setgid.
  • —keep=1 — norime išlaikyti įdiegtas galimybes pereinant iš šakninės paskyros.
  • —user=“nobody” – galutinis vartotojas, vykdantis programą, bus niekas.
  • —addamb=cap_net_bind_service — nustatome, kad susijusios galimybės būtų išvalytos perėjus iš šakninio režimo.
  • -c "./capabilities" - tiesiog paleiskite programą.

Susietos galimybės yra ypatingos rūšies galimybės, kurias paveldi antrinės programos, kai dabartinė programa jas vykdo naudodama execve(). Gali būti paveldimos tik galimybės, kurias leidžiama susieti, arba, kitaip tariant, kaip aplinkos galimybes.

Tikriausiai susimąstote, ką reiškia +eip, nurodę galimybę --caps parinktyje. Šios vėliavėlės naudojamos norint nustatyti, kad galimybė:

-turi būti aktyvuotas (p);

-galima naudoti (e);

-gali būti paveldėtas antriniais procesais (i).

Kadangi norime naudoti cap_net_bind_service, tai turime padaryti naudodami e vėliavėlę. Tada komandoje paleisime apvalkalą. Taip bus paleistos dvejetainės galimybės ir turėsime ją pažymėti i vėliavėle. Galiausiai norime, kad funkcija būtų įjungta (tai padarėme nekeitę UID) su p. Panašu, kad cap_net_bind_service+eip.

Galite patikrinti rezultatą naudodami ss. Šiek tiek sutrumpinkime išvestį, kad tilptų puslapyje, bet bus rodomas susietas prievadas ir vartotojo ID, kuris nėra 0, šiuo atveju 65:

# ss -tulpn -e -H | cut -d' ' -f17-
128 *:80 *:*
users:(("capabilities",pid=30040,fd=3)) uid:65534 ino:11311579 sk:2c v6only:0

Šiame pavyzdyje mes naudojome capsh, bet galite parašyti apvalkalą naudodami libcap. Norėdami gauti daugiau informacijos, žr. man 3 libcap.

Rašydamas programas, gana dažnai kūrėjas iš anksto nežino visų funkcijų, kurių reikia programai vykdymo metu; Be to, šios funkcijos gali pasikeisti naujose versijose.

Norėdami geriau suprasti mūsų programos galimybes, galime naudoti įrankį BCC, kuris nustato cap_capable branduolio funkcijos kprobe:

/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 1

Tą patį galime pasiekti naudodami bpftrace su vienos linijos kprobe cap_capable branduolio funkcijoje:

bpftrace -e 
   'kprobe:cap_capable {
      time("%H:%M:%S ");
      printf("%-6d %-6d %-16s %-4d %dn", uid, pid, comm, arg2, arg3);
    }' 
    | grep -i capabilities

Tai išves kažką panašaus į šį, jei mūsų programos galimybės bus įjungtos 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 1

Penktasis stulpelis yra procesui reikalingos galimybės, o kadangi šis išvestis apima ne audito įvykius, matome visas ne audito patikras ir galiausiai reikiamą galimybę, kai audito vėliavėlė (paskutinė išvestyje) nustatyta į 1. Galimybė mus domina CAP_NET_BIND_SERVICE, ji apibrėžiama kaip konstanta branduolio šaltinio kode faile include/uapi/linux/ability.h su identifikatoriumi 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">

Konteinerių, pvz., „runC“ ar „Docker“, funkcijos dažnai įjungiamos vykdymo metu, kad jos galėtų veikti neprivilegijuotu režimu, tačiau joms leidžiamos tik tos galimybės, reikalingos daugumai programų paleisti. Kai programai reikalingos tam tikros galimybės, „Docker“ gali jas suteikti naudodami --cap-add:

docker run -it --rm --cap-add=NET_ADMIN ubuntu ip link add dummy0 type dummy

Ši komanda suteiks konteineriui CAP_NET_ADMIN galimybę, leidžiančią sukonfigūruoti tinklo nuorodą, kad būtų galima pridėti dummy0 sąsają.

Kitame skyriuje parodyta, kaip naudoti tokias funkcijas kaip filtravimas, tačiau naudojant kitą techniką, kuri leidžia programiškai įdiegti savo filtrus.

Seccomp

„Seccomp“ reiškia „Secure Computing“ ir yra „Linux“ branduolyje įdiegtas saugos sluoksnis, leidžiantis kūrėjams filtruoti tam tikrus sistemos skambučius. Nors „Seccomp“ savo galimybėmis yra palyginama su „Linux“, jos galimybė valdyti tam tikrus sistemos skambučius daro ją daug lankstesnę, palyginti su jais.

„Seccomp“ ir „Linux“ funkcijos nėra tarpusavyje nesuderinamos ir dažnai naudojamos kartu, siekiant gauti naudos iš abiejų metodų. Pavyzdžiui, galbūt norėsite procesui suteikti CAP_NET_ADMIN galimybę, bet neleisti jam priimti lizdų jungčių, blokuodami akceptuoti ir priimti4 sistemos iškvietimus.

Seccomp filtravimo metodas yra pagrįstas BPF filtrais, veikiančiais SECCOMP_MODE_FILTER režimu, o sistemos skambučių filtravimas atliekamas taip pat, kaip ir paketams.

„Seccomp“ filtrai įkeliami naudojant prctl per operaciją PR_SET_SECCOMP. Šie filtrai yra BPF programos forma, kuri vykdoma kiekvienam Seccomp paketui, vaizduojamam seccomp_data struktūra. Šioje struktūroje yra atskaitos architektūra, žymeklis į procesoriaus instrukcijas sistemos iškvietimo metu ir daugiausia šešių sistemos iškvietimo argumentų, išreikštų kaip uint64.

Štai kaip seccomp_data struktūra atrodo iš branduolio šaltinio kodo linux/seccomp.h faile:

struct seccomp_data {
int nr;
      __u32 arch;
      __u64 instruction_pointer;
      __u64 args[6];
};

Kaip matote iš šios struktūros, galime filtruoti pagal sistemos iškvietimą, jo argumentus arba abiejų derinį.

Gavęs kiekvieną Seccomp paketą, filtras turi atlikti apdorojimą, kad priimtų galutinį sprendimą ir nurodytų branduoliui, ką daryti toliau. Galutinį sprendimą išreiškia viena iš grąžinamų reikšmių (būsenos kodai).

- SECCOMP_RET_KILL_PROCESS - užmuša visą procesą iškart po to, kai išfiltruojamas sistemos iškvietimas, kuris dėl to nevykdomas.

- SECCOMP_RET_KILL_THREAD - nutraukia dabartinę giją iškart po to, kai filtruojamas sistemos iškvietimas, kuris dėl to nevykdomas.

— SECCOMP_RET_KILL — SECCOMP_RET_KILL_THREAD pseudonimas, paliktas atgaliniam suderinamumui.

- SECCOMP_RET_TRAP – sistemos iškvietimas draudžiamas, o SIGSYS (Bad System Call) signalas siunčiamas jį iškviečiančiai užduočiai.

- SECCOMP_RET_ERRNO - Sistemos iškvietimas nevykdomas, o dalis SECCOMP_RET_DATA filtro grąžinimo vertės perduodama į vartotojo erdvę kaip errno reikšmė. Priklausomai nuo klaidos priežasties, grąžinamos skirtingos errno reikšmės. Klaidų numerių sąrašas pateikiamas kitame skyriuje.

- SECCOMP_RET_TRACE - Naudojamas pranešti ptrace tracer, naudojant - PTRACE_O_TRACESECCOMP, kad perimtų, kada vykdomas sistemos iškvietimas, siekiant pamatyti ir valdyti tą procesą. Jei sekimo priemonė neprijungta, grąžinama klaida, errno nustatoma į -ENOSYS ir sistemos iškvietimas nevykdomas.

– SECCOMP_RET_LOG – sistemos skambutis išspręstas ir registruojamas.

– SECCOMP_RET_ALLOW – sistemos skambutis tiesiog leidžiamas.

ptrace yra sistemos iškvietimas, skirtas įdiegti sekimo mechanizmus procese, vadinamame tracee, su galimybe stebėti ir kontroliuoti proceso vykdymą. Trace programa gali veiksmingai paveikti vykdymą ir modifikuoti tracee atminties registrus. Seccomp kontekste ptrace naudojamas, kai jį suaktyvina SECCOMP_RET_TRACE būsenos kodas, todėl sekimo priemonė gali neleisti sistemos iškvietimui vykdyti ir įgyvendinti savo logiką.

Seccomp klaidos

Kartkartėmis dirbdami su Seccomp susidursite su įvairiomis klaidomis, kurios identifikuojamos pagal SECCOMP_RET_ERRNO tipo grąžinimo reikšmę. Norėdami pranešti apie klaidą, seccomp sistemos iškvietimas grąžins -1, o ne 0.

Galimos šios klaidos:

- EACCESS - Skambintojui neleidžiama skambinti sisteminiu skambučiu. Paprastai taip nutinka, nes jis neturi CAP_SYS_ADMIN privilegijų arba no_new_privs nenustatytas naudojant prctl (apie tai kalbėsime vėliau);

— EFAULT — perduoti argumentai (seccomp_data struktūros argumentai) neturi tinkamo adreso;

— EINVAL — čia gali būti keturios priežastys:

-prašoma operacija nežinoma arba branduolio nepalaiko esamoje konfigūracijoje;

-nurodytos vėliavėlės negalioja prašomai operacijai;

-operacija apima BPF_ABS, tačiau yra problemų dėl nurodyto poslinkio, kuris gali viršyti seccomp_data struktūros dydį;

-filtrui perduodamų instrukcijų skaičius viršija maksimalų;

— ENOMEM — nepakanka atminties programai vykdyti;

- EOPNOTSUPP - operacija parodė, kad su SECCOMP_GET_ACTION_AVAIL veiksmas buvo galimas, tačiau branduolys nepalaiko argumentų grąžinimo;

— ESRCH — sinchronizuojant kitą srautą įvyko problema;

– ENOSYS – Prie veiksmo SECCOMP_RET_TRACE nėra pridėto žymeklio.

prctl yra sistemos iškvietimas, leidžiantis vartotojo erdvės programai manipuliuoti (nustatyti ir gauti) konkrečius proceso aspektus, tokius kaip baitų pabaiga, gijų pavadinimai, saugus skaičiavimo režimas (Seccomp), privilegijos, Perf įvykiai ir kt.

„Seccomp“ jums gali atrodyti kaip smėlio dėžės technologija, bet taip nėra. „Seccomp“ yra programa, leidžianti vartotojams sukurti smėlio dėžės mechanizmą. Dabar pažiūrėkime, kaip kuriamos vartotojo sąveikos programos, naudojant filtrą, kuris tiesiogiai iškviečiamas Seccomp sistemos iškvietimu.

BPF Seccomp filtro pavyzdys

Čia parodysime, kaip sujungti du anksčiau aptartus veiksmus, būtent:

— parašysime Seccomp BPF programą, kuri bus naudojama kaip filtras su skirtingais grąžinimo kodais, priklausomai nuo priimtų sprendimų;

— įkelkite filtrą naudodami prctl.

Pirmiausia jums reikia antraštės iš standartinės bibliotekos ir „Linux“ branduolio:

#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>

Prieš bandydami šį pavyzdį, turime įsitikinti, kad branduolys yra sudarytas su CONFIG_SECCOMP ir CONFIG_SECCOMP_FILTER nustatytais y. Veikiančioje mašinoje galite tai patikrinti taip:

cat /proc/config.gz| zcat | grep -i CONFIG_SECCOMP

Likusi kodo dalis yra dviejų dalių install_filter funkcija. Pirmoje dalyje yra mūsų BPF filtravimo instrukcijų sąrašas:

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),
  };

Instrukcijos nustatomos naudojant BPF_STMT ir BPF_JUMP makrokomandas, apibrėžtas faile linux/filter.h.
Peržiūrėkime instrukcijas.

- BPF_STMT(BPF_LD + BPF_W + BPF_ABS (offsetof(struct seccomp_data, arch))) - sistema įkelia ir kaupia iš BPF_LD žodžio BPF_W forma, paketiniai duomenys yra fiksuotame poslinkyje BPF_ABS.

- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arch, 0, 3) - patikrina naudojant BPF_JEQ, ar architektūros reikšmė BPF_K akumuliatoriaus konstantoje yra lygi arch. Jei taip, peršoka nuo 0 poslinkio į kitą komandą, kitu atveju peršoka ties 3 poslinkiu (šiuo atveju), kad būtų padaryta klaida, nes arch nesutampa.

- BPF_STMT(BPF_LD + BPF_W + BPF_ABS (offsetof(struct seccomp_data, nr))) - įkeliama ir kaupiama iš BPF_LD žodžio BPF_W forma, kuris yra sistemos skambučio numeris, esantis fiksuotame BPF_ABS poslinkyje.

— BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, nr, 0, 1) — lygina sistemos skambučio numerį su kintamojo nr reikšme. Jei jie yra vienodi, pereina prie kitos instrukcijos ir išjungia sistemos iškvietimą, kitu atveju leidžia sistemos iškvietimą su SECCOMP_RET_ALLOW.

- BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ERRNO | (klaida & SECCOMP_RET_DATA)) - baigia programą su BPF_RET ir dėl to sukuria klaidą SECCOMP_RET_ERRNO su numeriu iš err kintamojo.

- BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW) - baigia programą su BPF_RET ir leidžia sistemos iškvietimą vykdyti naudojant SECCOMP_RET_ALLOW.

SECCOMP yra CBPF
Jums gali kilti klausimas, kodėl vietoj sukompiliuoto ELF objekto arba JIT sukompiliuotos C programos naudojamas instrukcijų sąrašas.

Tam yra dvi priežastys.

• Pirma, Seccomp naudoja cBPF (klasikinį BPF), o ne eBPF, o tai reiškia: jis neturi registrų, o tik kaupiklį paskutiniam skaičiavimo rezultatui saugoti, kaip matyti pavyzdyje.

• Antra, Seccomp tiesiogiai priima žymeklį į BPF instrukcijų masyvą ir nieko daugiau. Mūsų naudojamos makrokomandos paprasčiausiai padeda nurodyti šias instrukcijas programuotojui patogiu būdu.

Jei jums reikia daugiau pagalbos suprasti šį rinkinį, apsvarstykite pseudokodą, kuris atlieka tą patį:

if (arch != AUDIT_ARCH_X86_64) {
    return SECCOMP_RET_ALLOW;
}
if (nr == __NR_write) {
    return SECCOMP_RET_ERRNO;
}
return SECCOMP_RET_ALLOW;

Apibrėžę filtro kodą socket_filter struktūroje, turite apibrėžti sock_fprog, kuriame yra kodas ir apskaičiuotas filtro ilgis. Ši duomenų struktūra reikalinga kaip argumentas, kad procesas būtų vykdomas vėliau:

struct sock_fprog prog = {
   .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])),
   .filter = filter,
};

Funkcijoje install_filter belieka padaryti vieną dalyką – įkelti pačią programą! Norėdami tai padaryti, naudojame prctl, kaip parinktį PR_SET_SECCOMP, kad įjungtumėte saugaus skaičiavimo režimą. Tada mes nurodome režimui įkelti filtrą naudojant SECCOMP_MODE_FILTER, kuris yra sock_fprog tipo prog kintamajame:

  if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
    perror("prctl(PR_SET_SECCOMP)");
    return 1;
  }
  return 0;
}

Galiausiai galime naudoti funkciją install_filter, tačiau prieš tai turime naudoti prctl, kad nustatytume PR_SET_NO_NEW_PRIVS dabartiniam vykdymui ir taip išvengtume situacijos, kai antriniai procesai gauna daugiau privilegijų nei jų tėvai. Taip galime atlikti šiuos prctl iškvietimus funkcijoje install_filter neturėdami root teisių.

Dabar galime iškviesti funkciją install_filter. Užblokuokime visus rašymo sistemos skambučius, susijusius su X86-64 architektūra, ir tiesiog suteikime leidimą, kuris blokuoja visus bandymus. Įdiegę filtrą, tęsiame vykdymą naudodami pirmąjį argumentą:

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]);
 }

Pradėkime. Norėdami kompiliuoti savo programą, galime naudoti arba clang, arba gcc, bet kuriuo atveju tai yra tik main.c failo kompiliavimas be specialių parinkčių:

clang main.c -o filter-write

Kaip minėta, užblokavome visus programos įrašus. Norėdami tai išbandyti, jums reikia programos, kuri kažką išvestų – ls atrodo geras kandidatas. Ji dažniausiai elgiasi taip:

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

Nuostabu! Štai kaip atrodo mūsų įvyniojimo programos naudojimas: Tiesiog perduodame programą, kurią norime išbandyti kaip pirmąjį argumentą:

./filter-write "ls -la"

Kai vykdoma, ši programa sukuria visiškai tuščią išvestį. Tačiau galime naudoti strace, kad pamatytume, kas vyksta:

strace -f ./filter-write "ls -la"

Darbo rezultatas labai sutrumpėja, tačiau atitinkama jo dalis rodo, kad įrašai blokuojami su EPERM klaida – ta pačia, kurią sukonfigūravome. Tai reiškia, kad programa nieko neišveda, nes negali pasiekti rašymo sistemos iškvietimo:

[pid 25099] write(2, "ls: ", 4) = -1 EPERM (Operation not permitted)
[pid 25099] write(2, "write error", 11) = -1 EPERM (Operation not permitted)
[pid 25099] write(2, "n", 1) = -1 EPERM (Operation not permitted)

Dabar jūs suprantate, kaip veikia Seccomp BPF, ir gerai žinote, ką galite su juo padaryti. Bet ar nenorėtumėte to paties pasiekti naudodami eBPF, o ne cBPF, kad išnaudotumėte visą jo galią?

Galvodami apie eBPF programas, dauguma žmonių mano, kad tiesiog jas parašo ir įkelia administratoriaus teises. Nors šis teiginys paprastai yra teisingas, branduolys įgyvendina mechanizmų rinkinį, skirtą apsaugoti eBPF objektus įvairiais lygiais. Šie mechanizmai vadinami BPF LSM spąstais.

BPF LSM spąstai

Siekdama užtikrinti nuo architektūros nepriklausomą sistemos įvykių stebėjimą, LSM įgyvendina spąstų koncepciją. Kablys skambutis techniškai panašus į sistemos skambutį, tačiau yra nepriklausomas nuo sistemos ir integruotas su infrastruktūra. LSM pateikia naują koncepciją, kurioje abstrakcijos sluoksnis gali padėti išvengti problemų, kylančių sprendžiant sistemos iškvietimus skirtingose ​​architektūrose.

Rašymo metu branduolys turi septynis kabliukus, susietus su BPF programomis, o SELinux yra vienintelis integruotas LSM, kuris juos įgyvendina.

Spąstų šaltinio kodas yra failo include/linux/security.h branduolio medyje:

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

Kiekvienas iš jų bus vadinamas skirtingais vykdymo etapais:

— security_bpf — atlieka pradinį vykdytų BPF sistemos iškvietimų patikrinimą;

- security_bpf_map – tikrina, kada branduolys grąžina žemėlapio failo aprašą;

- security_bpf_prog – tikrina, kada branduolys grąžina eBPF programos failo aprašą;

— security_bpf_map_alloc — patikrina, ar saugos laukas BPF žemėlapiuose yra inicijuotas;

- security_bpf_map_free – patikrina, ar saugos laukas išvalytas BPF žemėlapiuose;

— security_bpf_prog_alloc — patikrina, ar saugos laukas inicijuotas BPF programose;

- security_bpf_prog_free – patikrina, ar saugos laukas išvalytas BPF programose.

Dabar, matydami visa tai, suprantame: LSM BPF gaudyklių idėja yra ta, kad jie gali apsaugoti kiekvieną eBPF objektą, užtikrindami, kad operacijas su kortelėmis ir programomis galėtų atlikti tik tie, kurie turi atitinkamas teises.

Santrauka

Saugumas nėra tai, ką galite įgyvendinti vienareikšmiškai viskam, ką norite apsaugoti. Svarbu turėti galimybę apsaugoti sistemas įvairiais lygiais ir skirtingais būdais. Tikėkite ar ne, bet geriausias būdas apsaugoti sistemą yra organizuoti skirtingus apsaugos lygius iš skirtingų pozicijų, kad sumažinus vieno lygio saugumą nebūtų galima pasiekti visos sistemos. Pagrindiniai kūrėjai atliko puikų darbą suteikdami mums įvairių sluoksnių ir kontaktinių taškų rinkinį. Tikimės, kad suteikėme jums gerą supratimą apie tai, kas yra sluoksniai ir kaip naudoti BPF programas dirbant su jais.

Apie autorius

Davidas Calavera yra „Netlify“ technologijų vadovas. Jis dirbo Docker palaikymo srityje ir prisidėjo prie Runc, Go ir BCC įrankių kūrimo bei kitų atvirojo kodo projektų. Žinomas dėl savo darbo su „Docker“ projektais ir „Docker“ papildinių ekosistemos kūrimu. Davidas labai aistringai žiūri į liepsnos grafikus ir visada siekia optimizuoti našumą.

Lorenzo Fontana dirba Sysdig atvirojo kodo komandoje, kur daugiausia dėmesio skiria Falco – Cloud Native Computing Foundation projektui, užtikrinančiam konteinerio vykdymo saugą ir anomalijų aptikimą per branduolio modulį ir eBPF. Jis aistringai domisi paskirstytomis sistemomis, programine įranga apibrėžtu tinklu, Linux branduoliu ir našumo analize.

» Daugiau informacijos apie knygą rasite adresu leidėjo svetainė
» Turinys
» Ištrauka

Khabrozhiteley 25% nuolaida naudojant kuponą - Linux

Apmokėjus popierinę knygos versiją, elektroninė knyga bus atsiųsta el.

Šaltinis: www.habr.com

Добавить комментарий