Cartea „BPF pentru monitorizarea Linux”

Cartea „BPF pentru monitorizarea Linux”Bună, locuitorii Khabro! Mașina virtuală BPF este una dintre cele mai importante componente ale nucleului Linux. Utilizarea corectă a acestuia va permite inginerilor de sistem să găsească defecțiuni și să rezolve chiar și cele mai complexe probleme. Veți învăța cum să scrieți programe care monitorizează și modifică comportamentul nucleului, cum să implementați codul în siguranță pentru a monitoriza evenimentele din nucleu și multe altele. David Calavera și Lorenzo Fontana vă vor ajuta să deblocați puterea BPF. Extindeți-vă cunoștințele despre optimizarea performanței, rețele, securitate. - Utilizați BPF pentru a monitoriza și modifica comportamentul nucleului Linux. - Injectați cod pentru a monitoriza în siguranță evenimentele nucleului fără a fi nevoie să recompilați nucleul sau să reporniți sistemul. — Folosiți exemple de cod convenabile în C, Go sau Python. - Preluați controlul deținând ciclul de viață al programului BPF.

Linux Kernel Security, caracteristicile sale și Seccomp

BPF oferă o modalitate puternică de a extinde nucleul fără a sacrifica stabilitatea, securitatea sau viteza. Din acest motiv, dezvoltatorii de kernel au considerat că ar fi o idee bună să-și folosească versatilitatea pentru a îmbunătăți izolarea procesului în Seccomp prin implementarea filtrelor Seccomp suportate de programele BPF, cunoscute și sub numele de Seccomp BPF. În acest capitol vom explica ce este Seccomp și cum este utilizat. Apoi veți învăța cum să scrieți filtre Seccomp folosind programe BPF. După aceea, ne vom uita la cârligele BPF încorporate care sunt incluse în nucleul modulelor de securitate Linux.

Modulele de securitate Linux (LSM) sunt un cadru care oferă un set de funcții care pot fi utilizate pentru a implementa diferite modele de securitate într-o manieră standardizată. LSM poate fi folosit direct în arborele sursă al nucleului, cum ar fi Apparmor, SELinux și Tomoyo.

Să începem prin a discuta despre capabilitățile Linux.

Capabilitățile

Esența capabilităților Linux este că trebuie să acordați unui proces neprivilegiat permisiunea de a efectua o anumită sarcină, dar fără a utiliza suid în acest scop, sau să faceți în alt mod privilegiul procesului, reducând posibilitatea de atac și permițând procesului să efectueze anumite sarcini. De exemplu, dacă aplicația dvs. trebuie să deschidă un port privilegiat, să zicem 80, în loc să ruleze procesul ca root, puteți pur și simplu să îi acordați capacitatea CAP_NET_BIND_SERVICE.

Luați în considerare un program Go numit main.go:

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

Acest program servește un server HTTP pe portul 80 (acesta este un port privilegiat). De obicei, îl rulăm imediat după compilare:

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

Cu toate acestea, deoarece nu acordăm privilegii root, acest cod va genera o eroare la legarea portului:

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

capsh (shell manager) este un instrument care rulează un shell cu un set specific de capabilități.

În acest caz, așa cum sa menționat deja, în loc să acordați drepturi root complete, puteți activa legarea de porturi privilegiate furnizând capacitatea cap_net_bind_service împreună cu tot ceea ce este deja în program. Pentru a face acest lucru, putem include programul nostru în 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"

Să înțelegem puțin această echipă.

  • capsh - folosește capsh ca coajă.
  • —caps='cap_net_bind_service+eip cap_setpcap,cap_setuid,cap_setgid+ep' - deoarece trebuie să schimbăm utilizatorul (nu vrem să rulăm ca root), vom specifica cap_net_bind_service și capacitatea de a schimba efectiv ID-ul utilizatorului de la root la nimeni, și anume cap_setuid și cap_setgid.
  • —keep=1 — dorim să păstrăm capabilitățile instalate atunci când trecem de la contul root.
  • —user=“nobody” — utilizatorul final care rulează programul va fi nimeni.
  • —addamb=cap_net_bind_service — setați ștergerea capabilităților aferente după trecerea din modul rădăcină.
  • - -c "./capabilities" - doar rulați programul.

Capacitățile legate sunt un tip special de capabilități care sunt moștenite de programele copil atunci când programul curent le execută folosind execve(). Numai capabilitățile cărora li se permite să fie asociate sau, cu alte cuvinte, ca capabilități de mediu, pot fi moștenite.

Probabil vă întrebați ce înseamnă +eip după ce ați specificat capacitatea în opțiunea --caps. Aceste steaguri sunt folosite pentru a determina dacă capacitatea:

-trebuie activat (p);

- disponibil pentru utilizare (e);

-poate fi moștenit de procesele copil (i).

Deoarece vrem să folosim cap_net_bind_service, trebuie să facem acest lucru cu steag-ul e. Apoi vom porni shell-ul în comandă. Aceasta va rula binarul de capabilități și trebuie să-l marchem cu steag-ul i. În cele din urmă, dorim ca caracteristica să fie activată (am făcut acest lucru fără a schimba UID-ul) cu p. Arată ca cap_net_bind_service+eip.

Puteți verifica rezultatul folosind ss. Să scurtăm puțin rezultatul pentru a se potrivi pe pagină, dar va afișa portul asociat și ID-ul utilizatorului, altul decât 0, în acest caz 65:

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

În acest exemplu am folosit capsh, dar puteți scrie un shell folosind libcap. Pentru mai multe informații, consultați man 3 libcap.

Când scrieți programe, deseori dezvoltatorul nu cunoaște în avans toate caracteristicile de care are nevoie programul în timpul rulării; Mai mult, aceste caracteristici se pot schimba în versiunile noi.

Pentru a înțelege mai bine capacitățile programului nostru, putem lua instrumentul capabil BCC, care setează kprobe pentru funcția cap_capable kernel:

/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

Putem realiza același lucru utilizând bpftrace cu un kprobe cu o singură linie în funcția cap_capable kernel:

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

Acest lucru va scoate ceva de genul următor dacă capacitățile programului nostru sunt activate după 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

A cincea coloană reprezintă capabilitățile de care are nevoie procesul și, deoarece această ieșire include evenimente non-audit, vedem toate verificările non-audit și, în sfârșit, capacitatea necesară cu indicatorul de audit (ultimul în ieșire) setat la 1. Capacitatea unul care ne interesează este CAP_NET_BIND_SERVICE, este definit ca o constantă în codul sursă al kernelului în fișierul include/uapi/linux/ability.h cu identificatorul 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">

Capabilitățile sunt adesea activate în timpul rulării pentru containere precum runC sau Docker pentru a le permite să ruleze în mod neprivilegiat, dar li se permit numai capabilitățile necesare pentru a rula majoritatea aplicațiilor. Când o aplicație necesită anumite capacități, Docker le poate furniza folosind --cap-add:

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

Această comandă va oferi containerului capacitatea CAP_NET_ADMIN, permițându-i să configureze o legătură de rețea pentru a adăuga interfața dummy0.

Următoarea secțiune arată cum să folosim funcții precum filtrarea, dar folosind o tehnică diferită, care ne permite să implementăm în mod programatic propriile filtre.

Seccomp

Seccomp înseamnă Secure Computing și este un strat de securitate implementat în nucleul Linux care permite dezvoltatorilor să filtreze anumite apeluri de sistem. Deși Seccomp este comparabil ca capabilități cu Linux, capacitatea sa de a gestiona anumite apeluri de sistem îl face mult mai flexibil în comparație cu acestea.

Caracteristicile Seccomp și Linux nu se exclud reciproc și sunt adesea folosite împreună pentru a beneficia de ambele abordări. De exemplu, ați putea dori să acordați unui proces capacitatea CAP_NET_ADMIN, dar să nu îi permiteți să accepte conexiuni socket, blocând apelurile de sistem accept și accept4.

Metoda de filtrare Seccomp se bazează pe filtrele BPF care funcționează în modul SECCOMP_MODE_FILTER, iar filtrarea apelurilor de sistem se realizează în același mod ca și pentru pachete.

Filtrele Seccomp sunt încărcate folosind prctl prin operația PR_SET_SECCOMP. Aceste filtre iau forma unui program BPF care este executat pentru fiecare pachet Seccomp reprezentat de structura seccomp_data. Această structură conține arhitectura de referință, un pointer către instrucțiunile procesorului în momentul apelului de sistem și maximum șase argumente de apel de sistem, exprimate ca uint64.

Iată cum arată structura seccomp_data din codul sursă al nucleului din fișierul linux/seccomp.h:

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

După cum puteți vedea din această structură, putem filtra după apelul de sistem, argumentele acestuia sau o combinație a ambelor.

După primirea fiecărui pachet Seccop, filtrul trebuie să efectueze procesarea pentru a lua o decizie finală și a spune nucleului ce trebuie să facă în continuare. Decizia finală este exprimată prin una dintre valorile de returnare (coduri de stare).

- SECCOMP_RET_KILL_PROCESS - omoara întregul proces imediat după filtrarea unui apel de sistem care nu este executat din acest motiv.

- SECCOMP_RET_KILL_THREAD - termină firul curent imediat după filtrarea unui apel de sistem care nu este executat din această cauză.

— SECCOMP_RET_KILL — alias pentru SECCOMP_RET_KILL_THREAD, rămas pentru compatibilitate cu versiunea anterioară.

- SECCOMP_RET_TRAP - apelul de sistem este interzis, iar semnalul SIGSYS (Bad System Call) este trimis sarcinii care îl apelează.

- SECCOMP_RET_ERRNO - Apelul de sistem nu este executat și o parte din valoarea returnată a filtrului SECCOMP_RET_DATA este transmisă spațiului utilizator ca valoare errno. În funcție de cauza erorii, sunt returnate diferite valori de eroare. O listă cu numerele de eroare este furnizată în secțiunea următoare.

- SECCOMP_RET_TRACE - Folosit pentru a notifica urmăritorul ptrace folosind - PTRACE_O_TRACESECCOMP pentru a intercepta atunci când este executat un apel de sistem pentru a vedea și controla acel proces. Dacă un traser nu este conectat, este returnată o eroare, errno este setat la -ENOSYS și apelul de sistem nu este executat.

- SECCOMP_RET_LOG - apelul de sistem este rezolvat și înregistrat.

- SECCOMP_RET_ALLOW - apelul de sistem este pur și simplu permis.

ptrace este un apel de sistem pentru a implementa mecanisme de urmărire într-un proces numit tracee, cu capacitatea de a monitoriza și controla execuția procesului. Programul de urmărire poate influența eficient execuția și poate modifica registrele de memorie ale tracee. În contextul Seccomp, ptrace este utilizat atunci când este declanșat de codul de stare SECCOMP_RET_TRACE, astfel încât urmăritorul poate împiedica executarea apelului de sistem și să-și implementeze propria logică.

Erori seccomp

Din când în când, în timp ce lucrați cu Seccomp, veți întâlni diverse erori, care sunt identificate printr-o valoare returnată de tip SECCOMP_RET_ERRNO. Pentru a raporta o eroare, apelul de sistem seccomp va returna -1 în loc de 0.

Sunt posibile următoarele erori:

- EACCESS - Apelantul nu are voie să efectueze un apel de sistem. Acest lucru se întâmplă de obicei deoarece nu are privilegii CAP_SYS_ADMIN sau no_new_privs nu este setat folosind prctl (vom vorbi despre asta mai târziu);

— EFAULT — argumentele transmise (args în structura seccomp_data) nu au o adresă validă;

— EINVAL — pot exista patru motive aici:

-operația solicitată este necunoscută sau nu este suportată de kernel în configurația curentă;

-steagulele specificate nu sunt valabile pentru operațiunea solicitată;

-operarea include BPF_ABS, dar există probleme cu offset-ul specificat, care poate depăși dimensiunea structurii seccomp_data;

-numarul de instructiuni trecute la filtru depaseste maximul;

— ENOMEM — memorie insuficientă pentru a executa programul;

- EOPNOTSUPP - operația a indicat că cu SECCOMP_GET_ACTION_AVAIL acțiunea era disponibilă, dar nucleul nu acceptă returnări în argumente;

— ESRCH — a apărut o problemă la sincronizarea unui alt flux;

- ENOSYS - Nu există niciun trasor atașat acțiunii SECCOMP_RET_TRACE.

prctl este un apel de sistem care permite unui program din spațiul utilizatorului să manipuleze (seteze și obține) aspecte specifice ale unui proces, cum ar fi caracterul octet, numele firelor, modul de calcul securizat (Seccomp), privilegii, evenimente Perf etc.

Seccomp ți se poate părea o tehnologie sandbox, dar nu este. Seccomp este un utilitar care permite utilizatorilor să dezvolte un mecanism sandbox. Acum să ne uităm la modul în care programele de interacțiune cu utilizatorul sunt create folosind un filtru numit direct de apelul de sistem Seccomp.

Exemplu de filtru BPF Seccomp

Aici vom arăta cum să combinați cele două acțiuni discutate mai devreme, și anume:

— vom scrie un program Seccomp BPF, care va fi folosit ca filtru cu diferite coduri de returnare in functie de deciziile luate;

— încărcați filtrul folosind prctl.

Mai întâi aveți nevoie de anteturi din biblioteca standard și din nucleul 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>

Înainte de a încerca acest exemplu, trebuie să ne asigurăm că nucleul este compilat cu CONFIG_SECCOMP și CONFIG_SECCOMP_FILTER setate la y. Pe o mașină care funcționează, puteți verifica acest lucru astfel:

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

Restul codului este o funcție install_filter din două părți. Prima parte conține lista noastră de instrucțiuni de filtrare 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),
  };

Instrucțiunile sunt setate folosind macrocomenzile BPF_STMT și BPF_JUMP definite în fișierul linux/filter.h.
Să parcurgem instrucțiunile.

- BPF_STMT(BPF_LD + BPF_W + BPF_ABS (offsetof(struct seccomp_data, arch))) - sistemul se încarcă și se acumulează din BPF_LD sub forma cuvântului BPF_W, pachetele de date sunt situate la un offset fix BPF_ABS.

- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arch, 0, 3) - verifică folosind BPF_JEQ dacă valoarea arhitecturii din constanta acumulatorului BPF_K este egală cu arch. Dacă da, sare la offset 0 la următoarea instrucțiune, altfel sare la offset 3 (în acest caz) pentru a arunca o eroare deoarece arcul nu se potrivește.

- BPF_STMT(BPF_LD + BPF_W + BPF_ABS (offsetof(struct seccomp_data, nr))) - Se încarcă și se acumulează din BPF_LD sub forma cuvântului BPF_W, care este numărul de apel de sistem conținut în offset-ul fix al BPF_ABS.

— BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, nr, 0, 1) — compară numărul de apel de sistem cu valoarea variabilei nr. Dacă sunt egale, trece la următoarea instrucțiune și dezactivează apelul de sistem, în caz contrar permite apelul de sistem cu SECCOMP_RET_ALLOW.

- BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ERRNO | (error & SECCOMP_RET_DATA)) - termină programul cu BPF_RET și ca urmare produce o eroare SECCOMP_RET_ERRNO cu numărul din variabila err.

- BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW) - termină programul cu BPF_RET și permite executarea apelului de sistem folosind SECCOMP_RET_ALLOW.

SECCOMP ESTE CBPF
S-ar putea să vă întrebați de ce se folosește o listă de instrucțiuni în locul unui obiect ELF compilat sau al unui program C compilat JIT.

Există două motive pentru aceasta.

• În primul rând, Seccomp folosește cBPF (BPF clasic) și nu eBPF, ceea ce înseamnă: nu are registre, ci doar un acumulator pentru a stoca ultimul rezultat al calculului, așa cum se vede în exemplu.

• În al doilea rând, Seccomp acceptă direct un pointer către o serie de instrucțiuni BPF și nimic altceva. Macrocomenzile pe care le-am folosit pur și simplu ajută la specificarea acestor instrucțiuni într-un mod ușor de programat.

Dacă aveți nevoie de mai mult ajutor pentru înțelegerea acestui ansamblu, luați în considerare pseudocodul care face același lucru:

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

După definirea codului filtrului în structura socket_filter, trebuie să definiți un sock_fprog care să conțină codul și lungimea calculată a filtrului. Această structură de date este necesară ca argument pentru declararea ca procesul să ruleze mai târziu:

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

Mai rămâne un singur lucru de făcut în funcția install_filter - încărcați programul în sine! Pentru a face acest lucru, folosim prctl, luând PR_SET_SECCOMP ca opțiune pentru a intra în modul de calcul securizat. Apoi îi spunem modului să încarce filtrul folosind SECCOMP_MODE_FILTER, care este conținut în variabila prog de tip sock_fprog:

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

În cele din urmă, putem folosi funcția install_filter, dar înainte de asta trebuie să folosim prctl pentru a seta PR_SET_NO_NEW_PRIVS pentru execuția curentă și astfel evităm situația în care procesele copil primesc mai multe privilegii decât părinții lor. Cu aceasta, putem face următoarele apeluri prctl în funcția install_filter fără a avea drepturi de root.

Acum putem apela funcția install_filter. Să blocăm toate apelurile de sistem de scriere legate de arhitectura X86-64 și să dăm pur și simplu o permisiune care blochează toate încercările. După instalarea filtrului, continuăm execuția folosind primul 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]);
 }

Să începem. Pentru a compila programul nostru putem folosi fie clang, fie gcc, oricum este doar compilarea fișierului main.c fără opțiuni speciale:

clang main.c -o filter-write

După cum am menționat, am blocat toate intrările din program. Pentru a testa acest lucru, aveți nevoie de un program care scoate ceva - ls pare un candidat bun. Iată cum se comportă de obicei:

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

Minunat! Iată cum arată utilizarea programului nostru wrapper: Pur și simplu trecem programul pe care vrem să-l testăm ca prim argument:

./filter-write "ls -la"

Când este executat, acest program produce o ieșire complet goală. Cu toate acestea, putem folosi strace pentru a vedea ce se întâmplă:

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

Rezultatul lucrării este mult scurtat, dar partea corespunzătoare a acestuia arată că înregistrările sunt blocate cu eroarea EPERM - aceeași pe care am configurat-o. Aceasta înseamnă că programul nu scoate nimic, deoarece nu poate accesa apelul de sistem de scriere:

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

Acum înțelegeți cum funcționează Seccop BPF și aveți o idee bună despre ce puteți face cu el. Dar nu ți-ar plăcea să obții același lucru cu eBPF în loc de cBPF pentru a-și valorifica întreaga putere?

Când se gândesc la programele eBPF, majoritatea oamenilor cred că pur și simplu le scriu și le încarcă cu privilegii de administrator. În timp ce această afirmație este în general adevărată, nucleul implementează un set de mecanisme pentru a proteja obiectele eBPF la diferite niveluri. Aceste mecanisme sunt numite capcane BPF LSM.

Capcane BPF LSM

Pentru a oferi monitorizare independentă de arhitectură a evenimentelor din sistem, LSM implementează conceptul de capcane. Un apel hook este similar din punct de vedere tehnic cu un apel de sistem, dar este independent de sistem și integrat cu infrastructura. LSM oferă un nou concept în care un strat de abstractizare poate ajuta la evitarea problemelor întâlnite atunci când se confruntă cu apeluri de sistem pe diferite arhitecturi.

La momentul scrierii, nucleul are șapte cârlige asociate cu programele BPF, iar SELinux este singurul LSM încorporat care le implementează.

Codul sursă pentru capcane se află în arborele nucleului din fișierul 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);

Fiecare dintre ele va fi apelat în diferite etape de execuție:

— security_bpf — efectuează o verificare inițială a apelurilor de sistem BPF executate;

- security_bpf_map - verifică când nucleul returnează un descriptor de fișier pentru hartă;

- security_bpf_prog - verifică când nucleul returnează un descriptor de fișier pentru programul eBPF;

— security_bpf_map_alloc — verifică dacă câmpul de securitate din hărțile BPF este inițializat;

- security_bpf_map_free - verifică dacă câmpul de securitate este șters în hărțile BPF;

— security_bpf_prog_alloc — verifică dacă câmpul de securitate este inițializat în cadrul programelor BPF;

- security_bpf_prog_free - verifică dacă câmpul de securitate este șters în programele BPF.

Acum, văzând toate acestea, înțelegem: ideea din spatele interceptoarelor LSM BPF este că aceștia pot oferi protecție fiecărui obiect eBPF, asigurându-se că numai cei cu privilegiile corespunzătoare pot efectua operațiuni pe carduri și programe.

Rezumat

Securitatea nu este ceva pe care îl puteți implementa într-un mod unic pentru tot ceea ce doriți să protejați. Este important să poți proteja sistemele la diferite niveluri și în moduri diferite. Crezi sau nu, cel mai bun mod de a securiza un sistem este de a organiza diferite niveluri de protecție din diferite poziții, astfel încât reducerea securității unui nivel să nu permită accesul la întregul sistem. Dezvoltatorii de bază au făcut o treabă grozavă oferindu-ne un set de straturi și puncte de contact diferite. Sperăm că v-am dat o bună înțelegere a ce sunt straturile și cum să utilizați programele BPF pentru a lucra cu ele.

Despre autori

David Calavera este CTO la Netlify. A lucrat în suportul Docker și a contribuit la dezvoltarea instrumentelor Runc, Go și BCC, precum și a altor proiecte open source. Cunoscut pentru munca sa la proiectele Docker și dezvoltarea ecosistemului de plugin Docker. David este foarte pasionat de graficele de flacără și caută mereu să optimizeze performanța.

Lorenzo Fontana Lucrează în echipa cu sursă deschisă de la Sysdig, unde se concentrează în primul rând pe Falco, un proiect Cloud Native Computing Foundation care oferă securitate la rularea containerului și detectarea anomaliilor printr-un modul kernel și eBPF. Este pasionat de sistemele distribuite, rețele definite de software, nucleul Linux și analiza performanței.

» Mai multe detalii despre carte gasiti la site-ul editorului
» Cuprins
» Extras

Pentru Khabrozhiteley 25% reducere folosind cupon - Linux

La plata versiunii pe hârtie a cărții, o carte electronică va fi trimisă prin e-mail.

Sursa: www.habr.com

Adauga un comentariu