Grāmata "BPF Linux uzraudzībai"

Grāmata "BPF Linux uzraudzÄ«bai"Sveiki, Khabro iedzÄ«votāji! BPF virtuālā maŔīna ir viena no svarÄ«gākajām Linux kodola sastāvdaļām. Pareiza tā izmantoÅ”ana ļaus sistēmu inženieriem atrast defektus un atrisināt pat vissarežģītākās problēmas. JÅ«s uzzināsit, kā rakstÄ«t programmas, kas pārrauga un modificē kodola darbÄ«bu, kā droÅ”i ieviest kodu, lai pārraudzÄ«tu notikumus kodolā, un daudz ko citu. Deivids Kalavera un Lorenco Fontana palÄ«dzēs jums atraisÄ«t BPF spēku. PaplaÅ”iniet savas zināŔanas par veiktspējas optimizāciju, tÄ«klu veidoÅ”anu un droŔību. - Izmantojiet BPF, lai uzraudzÄ«tu un mainÄ«tu Linux kodola darbÄ«bu. - Ievadiet kodu, lai droÅ”i pārraudzÄ«tu kodola notikumus, nepārkompilējot kodolu vai pārstartējot sistēmu. ā€” Izmantojiet ērtus kodu piemērus C, Go vai Python. - Pārņemiet kontroli, iegÅ«stot BPF programmas dzÄ«ves ciklu.

Linux kodola droŔība, tās funkcijas un Seccomp

BPF nodroÅ”ina jaudÄ«gu veidu, kā paplaÅ”ināt kodolu, nezaudējot stabilitāti, droŔību vai ātrumu. Å Ä« iemesla dēļ kodola izstrādātāji uzskatÄ«ja, ka bÅ«tu laba ideja izmantot tā daudzpusÄ«bu, lai uzlabotu procesa izolāciju Seccomp, ievieÅ”ot Seccomp filtrus, ko atbalsta BPF programmas, kas pazÄ«stami arÄ« kā Seccomp BPF. Å ajā nodaļā mēs paskaidrosim, kas ir Seccomp un kā tas tiek lietots. Pēc tam jÅ«s uzzināsit, kā rakstÄ«t Seccomp filtrus, izmantojot BPF programmas. Pēc tam mēs apskatÄ«sim iebÅ«vētos BPF āķus, kas ir iekļauti Linux droŔības moduļu kodolā.

Linux droŔības moduļi (LSM) ir ietvars, kas nodroÅ”ina funkciju kopumu, ko var izmantot dažādu droŔības modeļu ievieÅ”anai standartizētā veidā. LSM var izmantot tieÅ”i kodola avota kokā, piemēram, Apparmor, SELinux un Tomoyo.

Sāksim, pārrunājot Linux iespējas.

Spējas

Linux iespēju bÅ«tÄ«ba ir tāda, ka jums ir jāpieŔķir nepievilinātam procesam atļauja veikt noteiktu uzdevumu, bet neizmantojot Å”im nolÅ«kam suid, vai kā citādi padarÄ«t procesu priviliģētu, samazinot uzbrukuma iespēju un ļaujot procesam veikt noteiktus uzdevumus. Piemēram, ja jÅ«su lietojumprogrammai ir jāatver priviliģēts ports, piemēram, 80, tā vietā, lai palaistu procesu kā root, varat vienkārÅ”i pieŔķirt tai CAP_NET_BIND_SERVICE iespēju.

Apsveriet Go programmu ar nosaukumu main.go:

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

Å Ä« programma apkalpo HTTP serveri 80. portā (tas ir priviliģēts ports). Parasti mēs to palaižam uzreiz pēc kompilācijas:

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

Tomēr, tā kā mēs nepieŔķiram root tiesÄ«bas, Å”is kods radÄ«s kļūdu, saistot portu:

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

capsh (čaulas pārvaldnieks) ir rīks, kas palaiž čaulu ar noteiktu iespēju kopu.

Å ajā gadÄ«jumā, kā jau minēts, tā vietā, lai pieŔķirtu visas saknes tiesÄ«bas, varat iespējot priviliģēto portu saistÄ«Å”anu, nodroÅ”inot cap_net_bind_service iespēju kopā ar visu pārējo, kas jau ir programmā. Lai to izdarÄ«tu, mēs varam pievienot mÅ«su programmu 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"

Mazliet sapratīsim Ŕo komandu.

  • capsh - izmantojiet capsh kā apvalku.
  • ā€”caps='cap_net_bind_service+eip cap_setpcap,cap_setuid,cap_setgid+ep' ā€” tā kā mums ir jāmaina lietotājs (mēs nevēlamies palaist kā root), mēs norādÄ«sim cap_net_bind_service un iespēju faktiski mainÄ«t lietotāja ID no root nevienam, proti, cap_setuid un cap_setgid.
  • ā€”keep=1 ā€” mēs vēlamies saglabāt instalētās iespējas, pārejot no saknes konta.
  • ā€”user=ā€œneviensā€ ā€” galalietotājs, kas palaiž programmu, nebÅ«s neviens.
  • ā€”addamb=cap_net_bind_service ā€” iestatiet saistÄ«to iespēju notÄ«rÄ«Å”anu pēc pārslēgÅ”anās no saknes režīma.
  • - -c "./capabilities" - vienkārÅ”i palaidiet programmu.

SaistÄ«tās iespējas ir Ä«paÅ”a veida iespējas, kuras manto pakārtotās programmas, kad paÅ”reizējā programma tās izpilda, izmantojot execve(). Var mantot tikai tās iespējas, kuras ir atļauts saistÄ«t vai, citiem vārdiem sakot, kā vides iespējas.

JÅ«s droÅ”i vien domājat, ko nozÄ«mē +eip pēc iespējas norādÄ«Å”anas opcijā --caps. Å ie karodziņi tiek izmantoti, lai noteiktu, ka iespēja:

-jābūt aktivizētam (p);

- pieejams lietoŔanai (e);

-var mantot ar bērnu procesiem (i).

Tā kā mēs vēlamies izmantot cap_net_bind_service, mums tas jādara ar karogu e. Pēc tam mēs sāksim čaulu komandā. Tas palaidÄ«s iespējas bināri, un mums tas ir jāatzÄ«mē ar karogu i. Visbeidzot, mēs vēlamies, lai Ŕī funkcija tiktu iespējota (to darÄ«jām, nemainot UID), izmantojot p. Tas izskatās kā cap_net_bind_service+eip.

Jūs varat pārbaudīt rezultātu, izmantojot ss. Nedaudz saīsināsim izvadi, lai tā ietilptu lapā, taču tajā tiks parādīts saistītais ports un lietotāja ID, kas nav 0, Ŕajā gadījumā 65:

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

Šajā piemērā mēs izmantojām capsh, bet jūs varat rakstīt čaulu, izmantojot libcap. Papildinformāciju skatiet man 3 libcap.

Rakstot programmas, diezgan bieži izstrādātājs iepriekŔ nezina visas programmas darbības laikā nepiecieŔamās funkcijas; Turklāt Ŕīs funkcijas var mainīties jaunajās versijās.

Lai labāk izprastu mūsu programmas iespējas, mēs varam izmantot rīku BCC, kas iestata kprobe kodola funkcijai 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 1

Mēs varam sasniegt to paÅ”u, izmantojot bpftrace ar vienas lÄ«nijas kprobe kodola funkcijā cap_capable:

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

Ja mūsu programmas iespējas ir iespējotas pēc kprobe, tas izvadīs kaut ko līdzīgu:

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

Piektajā kolonnā ir norādÄ«tas procesam nepiecieÅ”amās iespējas, un, tā kā Å”ajā izvadē ir ietverti ar auditu nesaistÄ«ti notikumi, mēs redzam visas ar auditu nesaistÄ«tās pārbaudes un visbeidzot vajadzÄ«gās iespējas ar audita karogu (pēdējais izvadē), kas iestatÄ«ts uz 1. Iespēja. viens, kas mÅ«s interesē, ir CAP_NET_BIND_SERVICE, tas ir definēts kā konstante kodola avota kodā failā include/uapi/linux/ability.h ar identifikatoru 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">

Konteineru, piemēram, runC vai Docker, iespējas bieži tiek iespējotas izpildes laikā, lai ļautu tiem darboties nepievilcÄ«gā režīmā, taču tām ir atļautas tikai tās iespējas, kas nepiecieÅ”amas, lai palaistu lielāko daļu lietojumprogrammu. Ja lietojumprogrammai ir nepiecieÅ”amas noteiktas iespējas, Docker var tās nodroÅ”ināt, izmantojot --cap-add:

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

Å Ä« komanda konteineram pieŔķirs CAP_NET_ADMIN iespēju, ļaujot tam konfigurēt tÄ«kla saiti, lai pievienotu dummy0 interfeisu.

Nākamajā sadaļā ir parādÄ«ts, kā izmantot tādas funkcijas kā filtrÄ“Å”ana, bet izmantojot citu paņēmienu, kas ļauj programmatiski ieviest savus filtrus.

Seccomp

Seccomp apzÄ«mē Secure Computing un ir droŔības slānis, kas ieviests Linux kodolā, kas ļauj izstrādātājiem filtrēt noteiktus sistēmas zvanus. Lai gan Seccomp pēc iespējām ir salÄ«dzināms ar Linux, tā spēja pārvaldÄ«t noteiktus sistēmas zvanus padara to daudz elastÄ«gāku salÄ«dzinājumā ar tiem.

Seccomp un Linux lÄ«dzekļi viens otru neizslēdz un bieži tiek izmantoti kopā, lai gÅ«tu labumu no abām pieejām. Piemēram, iespējams, vēlēsities procesam pieŔķirt CAP_NET_ADMIN iespēju, bet neļaut tam pieņemt ligzdas savienojumus, bloķējot akceptēt un pieņemt4 sistēmas zvanus.

Seccomp filtrÄ“Å”anas metode ir balstÄ«ta uz BPF filtriem, kas darbojas SECCOMP_MODE_FILTER režīmā, un sistēmas zvanu filtrÄ“Å”ana tiek veikta tāpat kā paketēm.

Seccomp filtri tiek ielādēti, izmantojot prctl, izmantojot darbÄ«bu PR_SET_SECCOMP. Å ie filtri ir BPF programmas formā, kas tiek izpildÄ«ta katrai Seccomp paketei, ko attēlo seccomp_data struktÅ«ra. Å Ä« struktÅ«ra satur atsauces arhitektÅ«ru, rādÄ«tāju uz procesora instrukcijām sistēmas izsaukuma laikā un ne vairāk kā seÅ”us sistēmas izsaukuma argumentus, kas izteikti kā uint64.

Lūk, kā seccomp_data struktūra izskatās no kodola pirmkoda failā linux/seccomp.h:

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

Kā redzams no Ŕīs struktÅ«ras, mēs varam filtrēt pēc sistēmas izsaukuma, tā argumentiem vai abu kombinācijas.

Pēc katras Seccomp paketes saņemÅ”anas filtram jāveic apstrāde, lai pieņemtu galÄ«go lēmumu un pateiktu kodolam, kā rÄ«koties tālāk. GalÄ«go lēmumu izsaka viena no atgrieÅ”anas vērtÄ«bām (statusa kodi).

- SECCOMP_RET_KILL_PROCESS - nogalina visu procesu uzreiz pēc sistēmas zvana filtrÄ“Å”anas, kas Ŕī iemesla dēļ netiek izpildÄ«ts.

- SECCOMP_RET_KILL_THREAD - pārtrauc paÅ”reizējo pavedienu tÅ«lÄ«t pēc sistēmas izsaukuma filtrÄ“Å”anas, kas Ŕī iemesla dēļ netiek izpildÄ«ts.

ā€” SECCOMP_RET_KILL ā€” aizstājvārds SECCOMP_RET_KILL_THREAD, atstāts atpakaļsaderÄ«bai.

- SECCOMP_RET_TRAP - sistēmas izsaukums ir aizliegts, un SIGSYS (Bad System Call) signāls tiek nosūtīts uzdevumam, kas to izsauc.

- SECCOMP_RET_ERRNO - sistēmas izsaukums netiek izpildÄ«ts, un daļa no SECCOMP_RET_DATA filtra atgrieÅ”anas vērtÄ«bas tiek nodota lietotāja telpai kā kļūdas vērtÄ«ba. AtkarÄ«bā no kļūdas iemesla tiek atgrieztas dažādas errno vērtÄ«bas. Kļūdu numuru saraksts ir sniegts nākamajā sadaļā.

- SECCOMP_RET_TRACE - izmanto, lai paziņotu ptrace tracer, izmantojot - PTRACE_O_TRACESECCOMP, lai pārtvertu, kad tiek izpildÄ«ts sistēmas izsaukums, lai redzētu un kontrolētu Å”o procesu. Ja izsekotājs nav pievienots, tiek atgriezta kļūda, errno tiek iestatÄ«ts uz -ENOSYS un sistēmas izsaukums netiek izpildÄ«ts.

- SECCOMP_RET_LOG - sistēmas zvans ir atrisināts un reģistrēts.

- SECCOMP_RET_ALLOW ā€” sistēmas izsaukums ir vienkārÅ”i atļauts.

ptrace ir sistēmas izsaukums, lai ieviestu izsekoÅ”anas mehānismus procesā, ko sauc par tracee, ar iespēju uzraudzÄ«t un kontrolēt procesa izpildi. IzsekoÅ”anas programma var efektÄ«vi ietekmēt izpildi un modificēt tracee atmiņas reÄ£istrus. Seccomp kontekstā ptrace tiek izmantots, ja to aktivizē statusa kods SECCOMP_RET_TRACE, tāpēc izsekotājs var novērst sistēmas izsaukuma izpildi un ieviest savu loÄ£iku.

Seccomp kļūdas

Laiku pa laikam, strādājot ar Seccomp, jÅ«s saskaraties ar dažādām kļūdām, kuras identificē pēc SECCOMP_RET_ERRNO tipa atgrieÅ”anas vērtÄ«bas. Lai ziņotu par kļūdu, seccomp sistēmas izsaukums atgriezÄ«s -1, nevis 0.

Ir iespējamas Ŕādas kļūdas:

- EACCESS - zvanītājam nav atļauts veikt sistēmas zvanu. Tas parasti notiek tāpēc, ka tai nav CAP_SYS_ADMIN privilēģiju vai no_new_privs nav iestatīts, izmantojot prctl (par to mēs runāsim vēlāk);

ā€” EFAULT ā€” nodotajiem argumentiem (args seccomp_data struktÅ«rā) nav derÄ«gas adreses;

ā€” EINVAL ā€” Å”eit var bÅ«t četri iemesli:

-pieprasÄ«tā darbÄ«ba nav zināma vai kodols paÅ”reizējā konfigurācijā to neatbalsta;

-norādītie karodziņi nav derīgi pieprasītajai darbībai;

-operācija ietver BPF_ABS, taču ir problēmas ar norādīto nobīdi, kas var pārsniegt seccomp_data struktūras izmēru;

-filtram nodoto instrukciju skaits pārsniedz maksimālo;

ā€” ENOMEM ā€” nepietiek atmiņas programmas izpildei;

- EOPNOTSUPP - operācija norādīja, ka ar SECCOMP_GET_ACTION_AVAIL darbība bija pieejama, bet kodols neatbalsta atgrieŔanos argumentos;

ā€” ESRCH ā€” radās problēma, sinhronizējot citu straumi;

- ENOSYS - darbībai SECCOMP_RET_TRACE nav pievienots izsekotājs.

prctl ir sistēmas izsaukums, kas ļauj lietotāja telpas programmai manipulēt (iestatÄ«t un iegÅ«t) konkrētus procesa aspektus, piemēram, baitu endianitāti, pavedienu nosaukumus, droÅ”o skaitļoÅ”anas režīmu (Seccomp), privilēģijas, Perf notikumus utt.

Seccomp jums var Ŕķist smilÅ”kastes tehnoloÄ£ija, taču tā nav. Seccomp ir utilÄ«ta, kas lietotājiem ļauj izstrādāt smilÅ”kastes mehānismu. Tagad apskatÄ«sim, kā tiek izveidotas lietotāju mijiedarbÄ«bas programmas, izmantojot filtru, ko tieÅ”i izsauc Seccomp sistēmas izsaukums.

BPF Seccomp filtra piemērs

Å eit mēs parādÄ«sim, kā apvienot divas iepriekÅ” apspriestās darbÄ«bas, proti:

ā€” uzrakstÄ«sim Seccomp BPF programmu, kas tiks izmantota kā filtrs ar dažādiem atgrieÅ”anas kodiem atkarÄ«bā no pieņemtajiem lēmumiem;

ā€” ielādējiet filtru, izmantojot prctl.

Vispirms jums ir vajadzīgas galvenes no standarta bibliotēkas un Linux kodola:

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

Pirms mēģināt Å”o piemēru, mums ir jāpārliecinās, ka kodols ir kompilēts ar CONFIG_SECCOMP un CONFIG_SECCOMP_FILTER iestatÄ«tu uz y. Darba maŔīnā to var pārbaudÄ«t Ŕādi:

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

Pārējā koda daļa ir divdaļīga install_filter funkcija. Pirmajā daļā ir mÅ«su BPF filtrÄ“Å”anas instrukciju saraksts:

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

Norādījumi ir iestatīti, izmantojot makro BPF_STMT un BPF_JUMP, kas definēti failā linux/filter.h.
Iesim cauri instrukcijām.

- BPF_STMT(BPF_LD + BPF_W + BPF_ABS (offsetof(struct seccomp_data, arch))) - sistēma ielādē un uzkrāj no BPF_LD vārda BPF_W formā, pakeÅ”u dati atrodas fiksētā nobÄ«dē BPF_ABS.

- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arka, 0, 3) - pārbauda, ā€‹ā€‹izmantojot BPF_JEQ, vai arhitektÅ«ras vērtÄ«ba akumulatora konstantē BPF_K ir vienāda ar arku. Ja tā, pārlec pie nobÄ«des 0 uz nākamo instrukciju, pretējā gadÄ«jumā pārlec pie nobÄ«des 3 (Å”ajā gadÄ«jumā), lai radÄ«tu kļūdu, jo arka nesakrÄ«t.

- BPF_STMT(BPF_LD + BPF_W + BPF_ABS (offsetof(struct seccomp_data, nr))) - Ielādē un uzkrāj no BPF_LD vārda BPF_W formā, kas ir sistēmas izsaukuma numurs, kas ietverts BPF_ABS fiksētajā nobīdē.

ā€” BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, nr, 0, 1) ā€” salÄ«dzina sistēmas izsaukuma numuru ar mainÄ«gā nr vērtÄ«bu. Ja tie ir vienādi, pāriet uz nākamo instrukciju un atspējo sistēmas izsaukumu, pretējā gadÄ«jumā atļauj sistēmas izsaukumu ar SECCOMP_RET_ALLOW.

- BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ERRNO | (kļūda & SECCOMP_RET_DATA)) - pārtrauc programmu ar BPF_RET un rezultātā rada kļūdu SECCOMP_RET_ERRNO ar numuru no kļūdas mainīgā.

- BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW) - pārtrauc programmu ar BPF_RET un ļauj izpildīt sistēmas izsaukumu, izmantojot SECCOMP_RET_ALLOW.

SECCOMP IS CBPF
Jums var rasties jautājums, kāpēc kompilēta ELF objekta vai JIT kompilētas C programmas vietā tiek izmantots instrukciju saraksts.

Tam ir divi iemesli.

ā€¢ Pirmkārt, Seccomp izmanto cBPF (klasisko BPF), nevis eBPF, kas nozÄ«mē: tai nav reÄ£istru, bet tikai akumulators, lai saglabātu pēdējo aprēķina rezultātu, kā redzams piemērā.

ā€¢ Otrkārt, Seccomp pieņem rādÄ«tāju uz BPF instrukciju masÄ«vu tieÅ”i un neko citu. MÅ«su izmantotie makro vienkārÅ”i palÄ«dz norādÄ«t Å”os norādÄ«jumus programmētājam draudzÄ«gā veidā.

Ja jums nepiecieŔama papildu palīdzība, lai izprastu Ŕo komplektu, apsveriet pseidokodu, kas veic to paŔu:

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

Pēc filtra koda definÄ“Å”anas socket_filter struktÅ«rā ir jādefinē sock_fprog, kas satur kodu un aprēķināto filtra garumu. Å Ä« datu struktÅ«ra ir nepiecieÅ”ama kā arguments, lai paziņotu, ka process tiks palaists vēlāk:

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

Funkcijā install_filter atlicis darÄ«t tikai vienu ā€“ ielādēt paÅ”u programmu! Lai to izdarÄ«tu, mēs izmantojam prctl, izmantojot PR_SET_SECCOMP kā opciju, lai pārietu droŔā skaitļoÅ”anas režīmā. Pēc tam mēs sakām režīmam, lai ielādētu filtru, izmantojot SECCOMP_MODE_FILTER, kas atrodas sock_fprog tipa prog mainÄ«gajā:

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

Visbeidzot, mēs varam izmantot funkciju install_filter, taču pirms tam mums ir jāizmanto prctl, lai iestatÄ«tu PR_SET_NO_NEW_PRIVS paÅ”reizējai izpildei un tādējādi izvairÄ«tos no situācijas, kad bērnu procesi saņem vairāk privilēģiju nekā viņu vecāki. Izmantojot to, mēs varam veikt Ŕādus prctl izsaukumus funkcijā install_filter bez root tiesÄ«bām.

Tagad mēs varam izsaukt funkciju install_filter. Bloķēsim visus rakstÄ«Å”anas sistēmas zvanus, kas saistÄ«ti ar X86-64 arhitektÅ«ru, un vienkārÅ”i pieŔķirsim atļauju, kas bloķē visus mēģinājumus. Pēc filtra instalÄ“Å”anas mēs turpinām izpildi, izmantojot pirmo 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]);
 }

Sāksim. Lai kompilētu mÅ«su programmu, mēs varam izmantot vai nu clang, vai gcc, jebkurā gadÄ«jumā tā ir tikai faila main.c kompilÄ“Å”ana bez Ä«paŔām opcijām:

clang main.c -o filter-write

Kā minēts, mēs esam bloķējuÅ”i visus programmas ierakstus. Lai to pārbaudÄ«tu, ir nepiecieÅ”ama programma, kas kaut ko izvada - ls Ŕķiet labs kandidāts. Viņa parasti uzvedas Ŕādi:

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

BrÄ«niŔķīgi! LÅ«k, kā izskatās mÅ«su iesaiņojuma programmas izmantoÅ”ana: Mēs vienkārÅ”i nododam programmu, kuru vēlamies pārbaudÄ«t, kā pirmo argumentu:

./filter-write "ls -la"

Kad Ŕī programma tiek izpildÄ«ta, tā rada pilnÄ«gi tukÅ”u izvadi. Tomēr mēs varam izmantot strace, lai redzētu, kas notiek:

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

Darba rezultāts ir ievērojami saÄ«sināts, bet tā atbilstoŔā daļa parāda, ka ieraksti ir bloķēti ar EPERM kļūdu - to paÅ”u, kuru mēs konfigurējām. Tas nozÄ«mē, ka programma neko neizvada, jo tā nevar piekļūt rakstÄ«Å”anas sistēmas izsaukumam:

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

Tagad jÅ«s saprotat, kā darbojas Seccomp BPF, un jums ir laba ideja par to, ko ar to varat darÄ«t. Bet vai jÅ«s nevēlaties panākt to paÅ”u ar eBPF, nevis cBPF, lai pilnÄ«bā izmantotu tā jaudu?

Domājot par eBPF programmām, lielākā daļa cilvēku domā, ka tās vienkārÅ”i raksta un ielādē ar administratora privilēģijām. Lai gan Å”is apgalvojums kopumā ir patiess, kodols ievieÅ” mehānismu kopumu, lai aizsargātu eBPF objektus dažādos lÄ«meņos. Å os mehānismus sauc par BPF LSM lamatām.

BPF LSM slazdi

Lai nodroÅ”inātu no arhitektÅ«ras neatkarÄ«gu sistēmas notikumu uzraudzÄ«bu, LSM ievieÅ” slazdu koncepciju. Āķa zvans ir tehniski lÄ«dzÄ«gs sistēmas zvanam, taču ir sistēmas neatkarÄ«gs un integrēts ar infrastruktÅ«ru. LSM nodroÅ”ina jaunu koncepciju, kurā abstrakcijas slānis var palÄ«dzēt izvairÄ«ties no problēmām, kas rodas, strādājot ar sistēmas izsaukumiem dažādās arhitektÅ«rās.

RakstÄ«Å”anas laikā kodolam ir septiņi āķi, kas saistÄ«ti ar BPF programmām, un SELinux ir vienÄ«gais iebÅ«vētais LSM, kas tos ievieÅ”.

Slazdu pirmkods atrodas kodola kokā failā 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);

Katrs no tiem tiks izsaukts dažādos izpildes posmos:

ā€” security_bpf ā€” veic izpildÄ«to BPF sistēmas izsaukumu sākotnējo pārbaudi;

- security_bpf_map - pārbauda, ā€‹ā€‹kad kodols atgriež kartes faila deskriptoru;

- security_bpf_prog - pārbauda, ā€‹ā€‹kad kodols atgriež eBPF programmas faila deskriptoru;

ā€” security_bpf_map_alloc ā€” pārbauda, ā€‹ā€‹vai droŔības lauks BPF kartēs ir inicializēts;

- security_bpf_map_free - pārbauda, ā€‹ā€‹vai droŔības lauks ir notÄ«rÄ«ts BPF kartēs;

ā€” security_bpf_prog_alloc ā€” pārbauda, ā€‹ā€‹vai droŔības lauks ir inicializēts BPF programmās;

- security_bpf_prog_free - pārbauda, ā€‹ā€‹vai droŔības lauks ir notÄ«rÄ«ts BPF programmās.

Tagad, redzot to visu, mēs saprotam: LSM BPF pārtvērēju ideja ir tāda, ka tie var nodroÅ”ināt aizsardzÄ«bu katram eBPF objektam, nodroÅ”inot, ka tikai tie, kuriem ir atbilstoÅ”as ā€‹ā€‹privilēģijas, var veikt darbÄ«bas ar kartēm un programmām.

Kopsavilkums

DroŔība nav kaut kas tāds, ko jÅ«s varat ieviest vienādi visam, ko vēlaties aizsargāt. Ir svarÄ«gi spēt aizsargāt sistēmas dažādos lÄ«meņos un dažādos veidos. Ticiet vai nē, bet vislabākais veids, kā nodroÅ”ināt sistēmu, ir organizēt dažādus aizsardzÄ«bas lÄ«meņus no dažādām pozÄ«cijām, lai viena lÄ«meņa droŔības samazināŔana neļautu piekļūt visai sistēmai. Galvenie izstrādātāji ir paveikuÅ”i lielisku darbu, nodroÅ”inot mums dažādu slāņu un saskares punktu kopumu. Mēs ceram, ka esam devuÅ”i jums labu izpratni par to, kas ir slāņi un kā izmantot BPF programmas darbam ar tiem.

Par autoriem

Deivids Kalavera ir Netlify tehnoloÄ£iju vadÄ«tājs. ViņŔ strādāja Docker atbalsta jomā un piedalÄ«jās Runc, Go un BCC rÄ«ku, kā arÄ« citu atvērtā pirmkoda projektu izstrādē. PazÄ«stams ar savu darbu pie Docker projektiem un Docker spraudņu ekosistēmas izstrādi. Deivids ļoti aizraujas ar liesmu diagrammām un vienmēr cenÅ”as optimizēt veiktspēju.

Lorenco Fontana strādā Sysdig atvērtā pirmkoda komandā, kur galvenokārt koncentrējas uz Falco ā€” Cloud Native Computing Foundation projektu, kas nodroÅ”ina konteinera izpildlaika droŔību un anomāliju noteikÅ”anu, izmantojot kodola moduli un eBPF. ViņŔ aizraujas ar izplatÄ«tajām sistēmām, programmatÅ«ras definētu tÄ«klu izveidi, Linux kodolu un veiktspējas analÄ«zi.

Ā» SÄ«kāku informāciju par grāmatu var atrast vietnē izdevēja vietne
Ā» Satura
Ā» Izraksts

Par Khabrozhiteley 25% atlaide, izmantojot kuponu - Linux

Apmaksājot grāmatas papīra versiju, pa e-pastu tiks nosūtīta elektroniskā grāmata.

Avots: www.habr.com

Pievieno komentāru