Boek "BPF vir Linux Monitoring"

Boek "BPF vir Linux Monitoring"Hallo Khabro inwoners! Die virtuele BPF-masjien is een van die belangrikste komponente van die Linux-kern. Die korrekte gebruik daarvan sal stelselingenieurs in staat stel om foute te vind en selfs die mees komplekse probleme op te los. Jy sal leer hoe om programme te skryf wat die gedrag van die kern monitor en verander, hoe om kode veilig te implementeer om gebeure in die kern te monitor, en nog baie meer. David Calavera en Lorenzo Fontana sal jou help om die krag van BPF te ontsluit. Brei jou kennis van werkverrigtingoptimalisering, netwerke, sekuriteit uit. - Gebruik BPF om die gedrag van die Linux-kern te monitor en te verander. - Spuit kode in om kerngebeure veilig te monitor sonder om die kern te hersaamstel of die stelsel te herlaai. — Gebruik gerieflike kodevoorbeelde in C, Go of Python. - Neem beheer deur die BPF-program lewensiklus te besit.

Linux Kernel Security, sy kenmerke en Secomp

BPF bied 'n kragtige manier om die kern uit te brei sonder om stabiliteit, sekuriteit of spoed in te boet. Om hierdie rede het die kernontwikkelaars gedink dat dit 'n goeie idee sou wees om die veelsydigheid daarvan te gebruik om prosesisolasie in Seccomp te verbeter deur Seccomp-filters te implementeer wat deur BPF-programme ondersteun word, ook bekend as Seccomp BPF. In hierdie hoofstuk sal ons verduidelik wat Secomp is en hoe dit gebruik word. Dan sal jy leer hoe om Secomp-filters te skryf met behulp van BPF-programme. Daarna sal ons kyk na die ingeboude BPF-hakies wat ingesluit is in die kern vir Linux-sekuriteitsmodules.

Linux Security Modules (LSM) is 'n raamwerk wat 'n stel funksies verskaf wat gebruik kan word om verskeie sekuriteitsmodelle op 'n gestandaardiseerde wyse te implementeer. LSM kan direk in die kernbronboom gebruik word, soos Apparmor, SELinux en Tomoyo.

Kom ons begin deur die vermoëns van Linux te bespreek.

Vermoëns

Die essensie van Linux se vermoëns is dat jy 'n onbevoorregte proses toestemming moet gee om 'n sekere taak uit te voer, maar sonder om suid vir daardie doel te gebruik, of andersins die proses bevoorreg maak, wat die moontlikheid van aanval verminder en die proses toelaat om sekere take uit te voer. Byvoorbeeld, as jou toepassing 'n bevoorregte poort moet oopmaak, sê 80, in plaas daarvan om die proses as wortel uit te voer, kan jy dit eenvoudig die CAP_NET_BIND_SERVICE-vermoë gee.

Oorweeg 'n Go-program genaamd main.go:

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

Hierdie program bedien 'n HTTP-bediener op poort 80 (dit is 'n bevoorregte poort). Gewoonlik voer ons dit onmiddellik na samestelling uit:

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

Aangesien ons egter nie wortelvoorregte toestaan ​​nie, sal hierdie kode 'n fout gooi wanneer die poort gebind word:

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

capsh (dopbestuurder) is 'n instrument wat 'n dop met 'n spesifieke stel vermoëns laat loop.

In hierdie geval, soos reeds genoem, in plaas daarvan om volle wortelregte toe te staan, kan jy bevoorregte poortbinding aktiveer deur die cap_net_bind_service-vermoë te voorsien saam met alles anders wat reeds in die program is. Om dit te doen, kan ons ons program in capsh insluit:

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

Kom ons verstaan ​​hierdie span 'n bietjie.

  • capsh - gebruik capsh as 'n dop.
  • —caps='cap_net_bind_service+eip cap_setpcap,cap_setuid,cap_setgid+ep' - aangesien ons die gebruiker moet verander (ons wil nie as wortel hardloop nie), sal ons cap_net_bind_service spesifiseer en die vermoë om die gebruiker ID te verander vanaf wortel na niemand, naamlik cap_setuid en cap_setgid.
  • —keep=1 — ons wil die geïnstalleerde vermoëns behou wanneer ons van die wortelrekening oorskakel.
  • —user=“niemand” — die eindgebruiker wat die program bestuur, sal niemand wees nie.
  • —addamb=cap_net_bind_service — stel die skoonmaak van verwante vermoëns in nadat oorgeskakel is van wortelmodus.
  • - -c "./capabilities" - hardloop net die program.

Gekoppelde vermoëns is 'n spesiale soort vermoëns wat deur kinderprogramme geërf word wanneer die huidige program dit uitvoer met behulp van execve(). Slegs vermoëns wat toegelaat word om geassosieer te word, of met ander woorde, as omgewingsvermoëns, kan geërf word.

Jy wonder waarskynlik wat +eip beteken nadat jy die vermoë in die --caps opsie gespesifiseer het. Hierdie vlae word gebruik om te bepaal dat die vermoë:

-moet geaktiveer word (p);

-beskikbaar vir gebruik (e);

-kan deur kinderprosesse geërf word (i).

Aangesien ons cap_net_bind_service wil gebruik, moet ons dit met die e-vlag doen. Dan sal ons die dop in die opdrag begin. Dit sal die vermoëns binêr laat loop en ons moet dit met die i-vlag merk. Laastens wil ons hê die kenmerk moet geaktiveer word (ons het dit gedoen sonder om die UID te verander) met p. Dit lyk soos cap_net_bind_service+eip.

U kan die resultaat nagaan met behulp van ss. Kom ons verkort die afvoer 'n bietjie om op die bladsy te pas, maar dit sal die gepaardgaande poort en gebruikers-ID anders as 0 wys, in hierdie geval 65:

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

In hierdie voorbeeld het ons capsh gebruik, maar jy kan 'n dop skryf met libcap. Vir meer inligting, sien man 3 libcap.

Wanneer programme geskryf word, weet die ontwikkelaar dikwels nie vooraf al die kenmerke wat die program benodig tydens looptyd nie; Boonop kan hierdie kenmerke in nuwe weergawes verander.

Om die vermoëns van ons program beter te verstaan, kan ons die BCC capable nutsding neem, wat die kprobe stel vir die cap_capable kernel funksie:

/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

Ons kan dieselfde ding bereik deur bpftrace te gebruik met 'n eenlyn-kprobe in die cap_capable kernelfunksie:

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

Dit sal iets soos die volgende uitvoer as ons program se vermoëns na kprobe geaktiveer is:

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

Die vyfde kolom is die vermoëns wat die proses benodig, en aangesien hierdie uitset nie-ouditgebeurtenisse insluit, sien ons alle nie-ouditkontroles en uiteindelik die vereiste vermoë met die ouditvlag (laaste in die uitset) gestel op 1. Vermoë. Die een waarin ons belangstel is CAP_NET_BIND_SERVICE, dit word gedefinieer as 'n konstante in die kernbronkode in die lêer include/uapi/linux/ability.h met identifiseerder 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">

Vermoë word dikwels tydens looptyd vir houers soos runC of Docker geaktiveer om hulle in onbevoorregte modus te laat loop, maar hulle word slegs toegelaat met die vermoëns wat nodig is om die meeste toepassings te laat loop. Wanneer 'n toepassing sekere vermoëns vereis, kan Docker dit verskaf deur --cap-add:

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

Hierdie opdrag sal die houer die CAP_NET_ADMIN-vermoë gee, wat dit toelaat om 'n netwerkskakel op te stel om die dummy0-koppelvlak by te voeg.

Die volgende afdeling wys hoe om kenmerke soos filtering te gebruik, maar deur 'n ander tegniek te gebruik wat ons toelaat om ons eie filters programmaties te implementeer.

Secomp

Secomp staan ​​vir Secure Computing en is 'n sekuriteitslaag wat in die Linux-kern geïmplementeer is wat ontwikkelaars toelaat om sekere stelseloproepe te filter. Alhoewel Secomp in vermoëns vergelykbaar is met Linux, maak sy vermoë om sekere stelseloproepe te bestuur dit baie meer buigsaam in vergelyking met hulle.

Secomp- en Linux-kenmerke sluit nie wedersyds uit nie en word dikwels saam gebruik om voordeel te trek uit beide benaderings. Byvoorbeeld, jy wil dalk 'n proses die CAP_NET_ADMIN-vermoë gee, maar nie toelaat dat dit sokverbindings aanvaar nie, wat die aanvaar en aanvaar4-stelseloproepe blokkeer.

Die Secomp-filtreermetode is gebaseer op BPF-filters wat in die SECCOMP_MODE_FILTER-modus werk, en stelseloproepfiltrering word op dieselfde manier as vir pakkies uitgevoer.

Seccomp-filters word gelaai met behulp van prctl via die PR_SET_SECCOMP-bewerking. Hierdie filters neem die vorm aan van 'n BPF-program wat uitgevoer word vir elke Seccomp-pakkie wat deur die seccomp_data-struktuur verteenwoordig word. Hierdie struktuur bevat die verwysingsargitektuur, 'n wyser na verwerkerinstruksies ten tyde van die stelseloproep, en 'n maksimum van ses stelseloproepargumente, uitgedruk as uint64.

Dit is hoe die secomp_data-struktuur lyk vanaf die kernbronkode in die linux/secomp.h-lêer:

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

Soos u uit hierdie struktuur kan sien, kan ons filter volgens die stelseloproep, sy argumente of 'n kombinasie van albei.

Nadat elke Seccomp-pakkie ontvang is, moet die filter verwerking uitvoer om 'n finale besluit te neem en die kern te vertel wat om volgende te doen. Die finale besluit word uitgedruk deur een van die terugkeerwaardes (statuskodes).

- SECCOMP_RET_KILL_PROCESS - maak die hele proses dood onmiddellik nadat 'n stelseloproep gefiltreer is wat nie as gevolg hiervan uitgevoer word nie.

- SECCOMP_RET_KILL_THREAD - beëindig die huidige draad onmiddellik nadat 'n stelseloproep gefiltreer is wat nie as gevolg hiervan uitgevoer word nie.

— SECCOMP_RET_KILL — alias vir SECCOMP_RET_KILL_THREAD, links vir terugwaartse versoenbaarheid.

- SECCOMP_RET_TRAP - die stelseloproep is verbode, en die SIGSYS (Bad System Call) sein word gestuur na die taak wat dit oproep.

- SECCOMP_RET_ERRNO - Die stelseloproep word nie uitgevoer nie, en 'n deel van die SECCOMP_RET_DATA-filter-terugsendingwaarde word as die foutwaarde na gebruikersspasie deurgegee. Afhangende van die oorsaak van die fout, word verskillende foutwaardes teruggestuur. 'n Lys van foutnommers word in die volgende afdeling verskaf.

- SECCOMP_RET_TRACE - Word gebruik om die ptrace-spoorder in kennis te stel deur - PTRACE_O_TRACESECCOMP te onderskep wanneer 'n stelseloproep uitgevoer word om daardie proses te sien en te beheer. As 'n tracer nie gekoppel is nie, word 'n fout teruggestuur, errno is op -ENOSYS gestel, en die stelseloproep word nie uitgevoer nie.

- SECCOMP_RET_LOG - die stelseloproep word opgelos en aangeteken.

- SECCOMP_RET_ALLOW - die stelseloproep word eenvoudig toegelaat.

ptrace is 'n stelseloproep om opsporingsmeganismes te implementeer in 'n proses genaamd tracee, met die vermoë om die uitvoering van die proses te monitor en te beheer. Die spoorprogram kan die uitvoering effektief beïnvloed en die spoorhouer se geheueregisters wysig. In die Secomp-konteks word ptrace gebruik wanneer dit deur die SECCOMP_RET_TRACE-statuskode geaktiveer word, sodat die naspoorder kan verhoed dat die stelseloproep uitgevoer word en sy eie logika implementeer.

Secomp foute

Van tyd tot tyd, terwyl jy met Secomp werk, sal jy verskeie foute teëkom, wat geïdentifiseer word deur 'n terugkeerwaarde van tipe SECCOMP_RET_ERRNO. Om 'n fout aan te meld, sal die seccomp-stelseloproep -1 in plaas van 0 terugstuur.

Die volgende foute is moontlik:

- TOEGANG - Die oproeper word nie toegelaat om 'n stelseloproep te maak nie. Dit gebeur gewoonlik omdat dit nie CAP_SYS_ADMIN-voorregte het nie of no_new_privs nie gestel is met behulp van prctl nie (ons sal later hieroor praat);

— EFAULT — die geslaagde argumente (args in die seccomp_data-struktuur) het nie 'n geldige adres nie;

— EINVAL — hier kan vier redes wees:

-die gevraagde operasie is onbekend of word nie deur die kern in die huidige konfigurasie ondersteun nie;

-die gespesifiseerde vlae is nie geldig vir die gevraagde operasie nie;

-operasie sluit BPF_ABS in, maar daar is probleme met die gespesifiseerde offset, wat die grootte van die secomp_data-struktuur kan oorskry;

-die aantal instruksies wat na die filter gestuur word, oorskry die maksimum;

— ENOMEM — nie genoeg geheue om die program uit te voer nie;

- EOPNOTSUPP - die operasie het aangedui dat met SECCOMP_GET_ACTION_AVAIL die aksie beskikbaar was, maar die kern ondersteun nie opbrengste in argumente nie;

— ESRCH — 'n probleem het voorgekom tydens die sinchronisering van 'n ander stroom;

- ENOSYS - Daar is geen naspoor aan die SECCOMP_RET_TRACE-aksie gekoppel nie.

prctl is 'n stelseloproep wat 'n gebruikerspasieprogram toelaat om spesifieke aspekte van 'n proses te manipuleer (stel en kry) soos byte endianness, draadname, veilige berekeningsmodus (Seccomp), voorregte, Perf-gebeurtenisse, ens.

Secomp lyk dalk vir jou soos 'n sandbox-tegnologie, maar dit is nie. Secomp is 'n program waarmee gebruikers 'n sandbox-meganisme kan ontwikkel. Kom ons kyk nou hoe gebruikersinteraksieprogramme geskep word met behulp van 'n filter wat direk deur die Secommp-stelseloproep genoem word.

BPF Secomp Filter Voorbeeld

Hier sal ons wys hoe om die twee aksies wat vroeër bespreek is, te kombineer, naamlik:

— ons sal 'n Secomp BPF-program skryf, wat as 'n filter met verskillende terugkeerkodes gebruik sal word, afhangende van die besluite wat geneem is;

- laai die filter met prctl.

Eerstens benodig u kopskrifte van die standaardbiblioteek en die Linux-kern:

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

Voordat ons hierdie voorbeeld probeer, moet ons verseker dat die kern saamgestel is met CONFIG_SECCOMP en CONFIG_SECCOMP_FILTER op y gestel. Op 'n werkende masjien kan jy dit so nagaan:

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

Die res van die kode is 'n tweedelige install_filter-funksie. Die eerste deel bevat ons lys van BPF-filterinstruksies:

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

Die instruksies word gestel deur die BPF_STMT- en BPF_JUMP-makro's te gebruik wat in die linux/filter.h-lêer gedefinieer is.
Kom ons gaan deur die instruksies.

- BPF_STMT(BPF_LD + BPF_W + BPF_ABS (offsetof(struct seccomp_data, arch))) - die stelsel laai en akkumuleer vanaf BPF_LD in die vorm van die woord BPF_W, pakkiedata is geleë op 'n vaste offset BPF_ABS.

- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, boog, 0, 3) - kontroleer met behulp van BPF_JEQ of die argitektuurwaarde in die akkumulatorkonstante BPF_K gelyk is aan boog. Indien wel, spring by offset 0 na die volgende instruksie, anders spring by offset 3 (in hierdie geval) om 'n fout te gooi omdat boog nie ooreenstem nie.

- BPF_STMT(BPF_LD + BPF_W + BPF_ABS (offsetof(struct seccomp_data, nr))) - Laai en akkumuleer vanaf BPF_LD in die vorm van die woord BPF_W, wat die stelseloproepnommer in die vaste offset van BPF_ABS is.

— BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, nr, 0, 1) — vergelyk die stelseloproepnommer met die waarde van die nr-veranderlike. As hulle gelyk is, gaan voort na die volgende instruksie en deaktiveer die stelseloproep, anders laat die stelseloproep met SECCOMP_RET_ALLOW toe.

- BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ERRNO | (fout & SECCOMP_RET_DATA)) - beëindig die program met BPF_RET en produseer as gevolg daarvan 'n fout SECCOMP_RET_ERRNO met die nommer van die foutveranderlike.

- BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW) - beëindig die program met BPF_RET en laat toe dat die stelseloproep uitgevoer word deur SECCOMP_RET_ALLOW te gebruik.

SECCOMP IS CBPF
Jy mag dalk wonder hoekom 'n lys instruksies gebruik word in plaas van 'n saamgestelde ELF-objek of 'n JIT saamgestelde C-program.

Daar is twee redes hiervoor.

• Eerstens gebruik Seccomp cBPF (klassieke BPF) en nie eBPF nie, wat beteken: dit het geen registers nie, maar slegs 'n akkumulator om die laaste resultaat van die berekening te stoor, soos in die voorbeeld gesien kan word.

• Tweedens aanvaar Secomp 'n wyser na 'n verskeidenheid BPF-instruksies direk en niks anders nie. Die makro's wat ons gebruik het, help eenvoudig om hierdie instruksies op 'n programmeervriendelike manier te spesifiseer.

As jy meer hulp nodig het om hierdie samestelling te verstaan, oorweeg die pseudokode wat dieselfde ding doen:

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

Nadat u die filterkode in die socket_filter-struktuur gedefinieer het, moet u 'n sock_fprog definieer wat die kode en die berekende lengte van die filter bevat. Hierdie datastruktuur is nodig as 'n argument om te verklaar dat die proses later loop:

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

Daar is net een ding oor om te doen in die install_filter funksie - laai die program self! Om dit te doen, gebruik ons ​​prctl, met PR_SET_SECCOMP as 'n opsie om veilige rekenaarmodus te betree. Dan vertel ons die modus om die filter te laai met SECCOMP_MODE_FILTER, wat vervat is in die prog-veranderlike van tipe sock_fprog:

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

Ten slotte kan ons ons install_filter-funksie gebruik, maar voor dit moet ons prctl gebruik om PR_SET_NO_NEW_PRIVS vir die huidige uitvoering te stel en sodoende die situasie te vermy waar kinderprosesse meer voorregte as hul ouers ontvang. Hiermee kan ons die volgende prctl-oproepe in die install_filter-funksie maak sonder om wortelregte te hê.

Nou kan ons die install_filter-funksie noem. Kom ons blokkeer alle skryfstelseloproepe wat met die X86-64-argitektuur verband hou en gee eenvoudig 'n toestemming wat alle pogings blokkeer. Nadat ons die filter geïnstalleer het, gaan ons voort met die uitvoering met die eerste 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]);
 }

Laat ons begin. Om ons program saam te stel kan ons óf clang óf gcc gebruik, in elk geval is dit net die samestelling van die main.c lêer sonder spesiale opsies:

clang main.c -o filter-write

Soos opgemerk, het ons alle inskrywings in die program geblokkeer. Om dit te toets het jy 'n program nodig wat iets uitvoer - dit lyk na 'n goeie kandidaat. Dit is hoe sy gewoonlik optree:

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

Wonderlik! Hier is hoe die gebruik van ons omhulprogram lyk: Ons slaag eenvoudig die program wat ons wil toets as die eerste argument:

./filter-write "ls -la"

Wanneer dit uitgevoer word, produseer hierdie program heeltemal leë uitset. Ons kan egter strace gebruik om te sien wat aangaan:

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

Die resultaat van die werk is aansienlik verkort, maar die ooreenstemmende deel daarvan wys dat rekords geblokkeer word met die EPERM-fout - dieselfde een wat ons gekonfigureer het. Dit beteken dat die program niks uitstuur nie, want dit kan nie toegang tot die skryfstelseloproep kry nie:

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

Nou verstaan ​​jy hoe Secomp BPF werk en het 'n goeie idee van wat jy daarmee kan doen. Maar wil jy nie dieselfde ding met eBPF in plaas van cBPF bereik om sy volle krag te benut nie?

Wanneer hulle aan eBPF-programme dink, dink die meeste mense dat hulle dit eenvoudig skryf en met administrateur-regte laai. Alhoewel hierdie stelling oor die algemeen waar is, implementeer die kern 'n stel meganismes om eBPF-voorwerpe op verskillende vlakke te beskerm. Hierdie meganismes word BPF LSM lokvalle genoem.

BPF LSM lokvalle

Om argitektuur-onafhanklike monitering van stelselgebeure te verskaf, implementeer LSM die konsep van lokvalle. 'n Hook call is tegnies soortgelyk aan 'n stelsel oproep, maar is stelsel onafhanklik en geïntegreer met die infrastruktuur. LSM verskaf 'n nuwe konsep waarin 'n abstraksielaag kan help om probleme wat ondervind word wanneer stelseloproepe op verskillende argitekture hanteer word, te vermy.

Ten tyde van die skryf daarvan het die kern sewe hake wat met BPF-programme geassosieer word, en SELinux is die enigste ingeboude LSM wat dit implementeer.

Die bronkode vir die lokvalle is geleë in die kernboom in die lêer 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);

Elkeen van hulle sal op verskillende stadiums van teregstelling geroep word:

— security_bpf — voer 'n aanvanklike kontrole uit van uitgevoerde BPF-stelseloproepe;

- security_bpf_map - kontroleer wanneer die kern 'n lêerbeskrywer vir die kaart terugstuur;

- security_bpf_prog - kontroleer wanneer die kern 'n lêerbeskrywer vir die eBPF-program terugstuur;

— security_bpf_map_alloc — kontroleer of die sekuriteitsveld binne BPF-kaarte geïnisialiseer is;

- security_bpf_map_free - kontroleer of die sekuriteitsveld binne BPF-kaarte skoongemaak is;

— security_bpf_prog_alloc — kontroleer of die sekuriteitsveld binne BPF-programme geïnisialiseer is;

- security_bpf_prog_free - kontroleer of die sekuriteitsveld binne BPF-programme skoongemaak is.

Nou, as ons dit alles sien, verstaan ​​ons: die idee agter LSM BPF-onderskeppers is dat hulle beskerming aan elke eBPF-voorwerp kan bied, om te verseker dat slegs diegene met die toepaslike voorregte bewerkings op kaarte en programme kan uitvoer.

Opsomming

Sekuriteit is nie iets wat jy op 'n een-grootte-pas-almal manier kan implementeer vir alles wat jy wil beskerm nie. Dit is belangrik om stelsels op verskillende vlakke en op verskillende maniere te kan beskerm. Glo dit of nie, die beste manier om 'n stelsel te beveilig, is om verskillende vlakke van beskerming vanuit verskillende posisies te organiseer, sodat die vermindering van die sekuriteit van een vlak nie toegang tot die hele stelsel toelaat nie. Die kernontwikkelaars het 'n goeie werk gedoen om ons 'n stel verskillende lae en raakpunte te gee. Ons hoop ons het jou 'n goeie begrip gegee van wat lae is en hoe om BPF-programme te gebruik om daarmee te werk.

Oor die skrywers

David Calavera is die CTO by Netlify. Hy het in Docker-ondersteuning gewerk en bygedra tot die ontwikkeling van Runc-, Go- en BCC-nutsgoed, sowel as ander oopbronprojekte. Bekend vir sy werk aan Docker-projekte en ontwikkeling van die Docker-inprop-ekosisteem. David is baie passievol oor vlamgrafieke en is altyd op soek om prestasie te optimaliseer.

Lorenzo Fontana werk op die oopbronspan by Sysdig, waar hy hoofsaaklik gefokus is op Falco, 'n Cloud Native Computing Foundation-projek wat houerlooptydsekuriteit en anomalie-opsporing deur 'n kernmodule en eBPF bied. Hy is passievol oor verspreide stelsels, sagteware-gedefinieerde netwerke, die Linux-kern en prestasie-analise.

» Vir meer inligting oor die boek, besoek asseblief uitgewer se webwerf
» Inhoudsopgawe
» Uittreksel

Vir Khabrozhiteli 25% afslag op die koepon - Linux

By betaling van die papierweergawe van die boek word 'n e-boek na die e-pos gestuur.

Bron: will.com

Voeg 'n opmerking