Kitabu "BPF kwa Ufuatiliaji wa Linux"

Kitabu "BPF kwa Ufuatiliaji wa Linux"Habari, wakazi wa Khabro! Mashine pepe ya BPF ni mojawapo ya vipengele muhimu vya kernel ya Linux. Matumizi yake sahihi yataruhusu wahandisi wa mfumo kupata makosa na kutatua hata shida ngumu zaidi. Utajifunza jinsi ya kuandika programu zinazofuatilia na kurekebisha tabia ya kernel, jinsi ya kutekeleza kwa usalama msimbo wa kufuatilia matukio kwenye kernel, na mengi zaidi. David Calavera na Lorenzo Fontana watakusaidia kufungua nguvu za BPF. Panua ujuzi wako wa uboreshaji wa utendakazi, mitandao, usalama. - Tumia BPF kufuatilia na kurekebisha tabia ya Linux kernel. - Ingiza msimbo ili kufuatilia matukio ya kernel kwa usalama bila kulazimika kukusanya tena kernel au kuwasha upya mfumo. - Tumia mifano rahisi ya nambari katika C, Go au Python. - Chukua udhibiti kwa kumiliki mzunguko wa maisha wa programu ya BPF.

Usalama wa Kernel ya Linux, Sifa Zake na Seccomp

BPF hutoa njia thabiti ya kupanua punje bila kuacha uthabiti, usalama au kasi. Kwa sababu hii, watengenezaji wa kernel walidhani lingekuwa wazo zuri kutumia utengamano wake ili kuboresha utengaji wa mchakato katika Seccomp kwa kutekeleza vichungi vya Seccomp vinavyoungwa mkono na programu za BPF, pia hujulikana kama Seccomp BPF. Katika sura hii tutaeleza Seccomp ni nini na inatumikaje. Kisha utajifunza jinsi ya kuandika vichungi vya Seccomp kwa kutumia programu za BPF. Baada ya hapo, tutaangalia ndoano za BPF zilizojengwa ndani ambazo zimejumuishwa kwenye kernel ya moduli za usalama za Linux.

Moduli za Usalama za Linux (LSM) ni mfumo ambao hutoa seti ya vitendakazi ambavyo vinaweza kutumika kutekeleza miundo mbalimbali ya usalama kwa njia iliyosanifiwa. LSM inaweza kutumika moja kwa moja kwenye mti chanzo cha kernel, kama vile Apparmor, SELinux na Tomoyo.

Wacha tuanze kwa kujadili uwezo wa Linux.

Uwezo

Kiini cha uwezo wa Linux ni kwamba unahitaji kutoa ruhusa ya mchakato usio na haki kufanya kazi fulani, lakini bila kutumia suid kwa madhumuni hayo, au vinginevyo kufanya mchakato kuwa wa upendeleo, kupunguza uwezekano wa mashambulizi na kuruhusu mchakato kufanya kazi fulani. Kwa mfano, ikiwa programu yako inahitaji kufungua mlango uliobahatika, sema 80, badala ya kuendesha mchakato kama mzizi, unaweza kuipa uwezo wa CAP_NET_BIND_SERVICE.

Fikiria mpango wa Go unaoitwa main.go:

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

Mpango huu hutumikia seva ya HTTP kwenye bandari 80 (hii ni bandari ya upendeleo). Kawaida tunaiendesha mara baada ya mkusanyiko:

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

Walakini, kwa kuwa hatutoi upendeleo wa mizizi, nambari hii itatupa makosa wakati wa kufunga bandari:

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

capsh (meneja wa ganda) ni chombo kinachoendesha ganda na seti maalum ya uwezo.

Katika kesi hii, kama ilivyotajwa tayari, badala ya kutoa haki kamili za mizizi, unaweza kuwezesha ufungaji mlango uliobahatika kwa kutoa cap_net_bind_service uwezo pamoja na kila kitu kingine ambacho tayari kiko kwenye programu. Ili kufanya hivyo, tunaweza kuambatanisha programu yetu kwa herufi ndogo:

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

Hebu ielewe timu hii kidogo.

  • capsh - tumia kofia kama ganda.
  • β€”caps='cap_net_bind_service+eip cap_setpcap,cap_setuid,cap_setgid+ep' - kwa kuwa tunahitaji kubadilisha mtumiaji (hatutaki kufanya kazi kama mzizi), tutabainisha cap_net_bind_service na uwezo wa kubadilisha kitambulisho cha mtumiaji kutoka. root to nobody, yaani cap_setuid na cap_setgid.
  • β€”weka=1 β€” tunataka kuweka uwezo uliosakinishwa wakati wa kubadili kutoka kwa akaunti ya msingi.
  • β€”mtumiaji=β€œhakuna mtu” β€” mtumiaji wa mwisho anayeendesha programu hatakuwa mtu.
  • -addamb=cap_net_bind_service - weka uondoaji wa uwezo unaohusiana baada ya kubadili kutoka kwa hali ya mizizi.
  • - -c "./uwezo" - endesha programu tu.

Uwezo uliounganishwa ni aina maalum ya uwezo ambao hurithiwa na programu za watoto wakati mpango wa sasa unazitekeleza kwa kutumia execve(). Uwezo tu ambao unaruhusiwa kuhusishwa, au kwa maneno mengine, kama uwezo wa mazingira, unaweza kurithi.

Labda unashangaa + eip inamaanisha nini baada ya kubainisha uwezo katika chaguo la --caps. Bendera hizi hutumiwa kuamua kuwa uwezo:

-lazima kuamilishwa (p);

-inapatikana kwa matumizi (e);

-inaweza kurithiwa na michakato ya mtoto (i).

Kwa kuwa tunataka kutumia cap_net_bind_service, tunahitaji kufanya hivi na e bendera. Kisha tutaanza shell katika amri. Hii itaendesha uwezo wa binary na tunahitaji kuiweka alama kwa i bendera. Mwishowe, tunataka kipengele hicho kuwezeshwa (tulifanya hivi bila kubadilisha UID) na p. Inaonekana kama cap_net_bind_service+eip.

Unaweza kuangalia matokeo kwa kutumia ss. Wacha tufupishe pato kidogo ili kutoshea kwenye ukurasa, lakini itaonyesha bandari inayohusika na kitambulisho cha mtumiaji isipokuwa 0, katika kesi hii 65:

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

Katika mfano huu tulitumia capsh, lakini unaweza kuandika ganda kwa kutumia libcap. Kwa habari zaidi, angalia man 3 libcap.

Wakati wa kuandika programu, mara nyingi msanidi programu hajui mapema huduma zote ambazo programu inahitaji wakati wa kukimbia; Aidha, vipengele hivi vinaweza kubadilika katika matoleo mapya.

Ili kuelewa vyema uwezo wa programu yetu, tunaweza kuchukua zana yenye uwezo wa BCC, ambayo huweka kprobe kwa kitendakazi cha 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

Tunaweza kufikia jambo lile lile kwa kutumia bpftrace na kprobe ya mjengo mmoja kwenye kitendakazi cha kernel 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

Hii itatoa kitu kama kifuatacho ikiwa uwezo wa programu yetu umewezeshwa baada ya 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

Safu ya tano ni uwezo ambao mchakato unahitaji, na kwa kuwa matokeo haya yanajumuisha matukio yasiyo ya ukaguzi, tunaona ukaguzi wote usio wa ukaguzi na hatimaye uwezo unaohitajika na alama ya ukaguzi (ya mwisho katika matokeo) umewekwa kwa 1. Uwezo. moja tunayopendezwa nayo ni CAP_NET_BIND_SERVICE, inafafanuliwa kama ya kudumu katika msimbo wa chanzo wa kernel kwenye faili ni pamoja na/uapi/linux/ability.h na kitambulisho 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">

Uwezo mara nyingi huwashwa wakati wa utekelezaji wa kontena kama vile runC au Docker ili kuziruhusu kufanya kazi katika hali isiyo ya upendeleo, lakini zinaruhusiwa tu uwezo unaohitajika kuendesha programu nyingi. Wakati programu inahitaji uwezo fulani, Docker inaweza kuwapa kwa kutumia --cap-add:

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

Amri hii itaipa kontena uwezo wa CAP_NET_ADMIN, ikiruhusu kusanidi kiunga cha mtandao ili kuongeza kiolesura cha dummy0.

Sehemu inayofuata inaonyesha jinsi ya kutumia vipengele kama vile kuchuja, lakini kwa kutumia mbinu tofauti ambayo huturuhusu kutekeleza kiprogramu vichujio vyetu.

Seccomp

Seccomp inawakilisha Secure Computing na ni safu ya usalama inayotekelezwa katika kernel ya Linux ambayo inaruhusu wasanidi programu kuchuja simu fulani za mfumo. Ingawa Seccomp inalinganishwa katika uwezo na Linux, uwezo wake wa kudhibiti simu fulani za mfumo huifanya iwe rahisi kunyumbulika zaidi ikilinganishwa nazo.

Vipengele vya Seccomp na Linux havitengani na mara nyingi hutumiwa pamoja ili kufaidika na mbinu zote mbili. Kwa mfano, unaweza kutaka kuupa mchakato uwezo wa CAP_NET_ADMIN lakini usiiruhusu kukubali miunganisho ya soketi, kuzuia kukubali na kukubali simu za mfumo 4.

Mbinu ya kuchuja ya Seccomp inategemea vichujio vya BPF vinavyofanya kazi katika hali ya SECCOMP_MODE_FILTER, na uchujaji wa simu za mfumo unafanywa kwa njia sawa na kwa pakiti.

Vichungi vya Seccomp hupakiwa kwa kutumia prctl kupitia operesheni ya PR_SET_SECCOMP. Vichujio hivi huchukua muundo wa programu ya BPF ambayo inatekelezwa kwa kila pakiti ya Seccomp inayowakilishwa na muundo wa seccomp_data. Muundo huu una usanifu wa marejeleo, kielekezi kwa maagizo ya kichakataji wakati wa simu ya mfumo, na upeo wa hoja sita za simu za mfumo, zilizoonyeshwa kama uint64.

Hivi ndivyo muundo wa seccomp_data unavyoonekana kutoka kwa msimbo wa chanzo wa kernel kwenye faili ya linux/seccomp.h:

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

Kama unavyoona kutoka kwa muundo huu, tunaweza kuchuja kwa simu ya mfumo, hoja zake, au mchanganyiko wa zote mbili.

Baada ya kupokea kila pakiti ya Seccomp, kichujio lazima kifanye usindikaji ili kufanya uamuzi wa mwisho na kuwaambia kernel nini cha kufanya baadaye. Uamuzi wa mwisho unaonyeshwa na moja ya maadili ya kurudi (misimbo ya hali).

- SECCOMP_RET_KILL_PROCESS - huua mchakato mzima mara tu baada ya kuchuja simu ya mfumo ambayo haijatekelezwa kwa sababu hii.

- SECCOMP_RET_KILL_THREAD - husitisha mazungumzo ya sasa mara tu baada ya kuchuja simu ya mfumo ambayo haijatekelezwa kwa sababu hii.

- SECCOMP_RET_KILL - lakabu la SECCOMP_RET_KILL_THREAD, limesalia kwa uoanifu wa nyuma.

- SECCOMP_RET_TRAP - simu ya mfumo ni marufuku, na ishara ya SIGSYS (Simu Mbaya ya Mfumo) inatumwa kwa kazi inayoiita.

- SECCOMP_RET_ERRNO - Simu ya mfumo haijatekelezwa, na sehemu ya thamani ya kurejesha kichujio cha SECCOMP_RET_DATA inapitishwa kwenye nafasi ya mtumiaji kama thamani ya makosa. Kulingana na sababu ya kosa, maadili tofauti ya makosa yanarejeshwa. Orodha ya nambari za makosa imetolewa katika sehemu inayofuata.

- SECCOMP_RET_TRACE - Hutumika kuarifu kifuatiliaji cha ptrace kwa kutumia - PTRACE_O_TRACESECCOMP ili kukata simu ya mfumo inapotekelezwa ili kuona na kudhibiti mchakato huo. Ikiwa kifuatiliaji hakijaunganishwa, hitilafu inarudishwa, errno imewekwa kuwa -ENOSYS, na simu ya mfumo haijatekelezwa.

- SECCOMP_RET_LOG - simu ya mfumo imetatuliwa na kurekodiwa.

- SECCOMP_RET_ALLOW - simu ya mfumo inaruhusiwa tu.

ptrace ni wito wa mfumo wa kutekeleza taratibu za ufuatiliaji katika mchakato unaoitwa tracee, wenye uwezo wa kufuatilia na kudhibiti utekelezaji wa mchakato. Programu ya kufuatilia inaweza kuathiri vyema utekelezaji na kurekebisha rejista za kumbukumbu za tracee. Katika muktadha wa Seccomp, ptrace hutumiwa inapoanzishwa na msimbo wa hali wa SECCOMP_RET_TRACE, kwa hivyo kifuatiliaji kinaweza kuzuia simu ya mfumo kutekeleza na kutekeleza mantiki yake yenyewe.

Makosa ya seccomp

Mara kwa mara, wakati wa kufanya kazi na Seccomp, utakutana na makosa mbalimbali, ambayo yanatambuliwa na thamani ya kurudi ya aina ya SECCOMP_RET_ERRNO. Ili kuripoti hitilafu, simu ya mfumo wa seccomp itarudi -1 badala ya 0.

Makosa yafuatayo yanawezekana:

- UPATIKANAJI - Mpiga simu haruhusiwi kupiga simu ya mfumo. Kwa kawaida hii hutokea kwa sababu haina mapendeleo ya CAP_SYS_ADMIN au no_new_privs haijawekwa kwa kutumia prctl (tutazungumza kuhusu hili baadaye);

- EFAULT - hoja zilizopitishwa (args katika muundo wa seccomp_data) hazina anwani halali;

- EINVAL - kunaweza kuwa na sababu nne hapa:

-operesheni iliyoombwa haijulikani au haihimiliwi na kernel katika usanidi wa sasa;

- bendera zilizoainishwa sio halali kwa operesheni iliyoombwa;

-operesheni inajumuisha BPF_ABS, lakini kuna matatizo na kukabiliana na maalum, ambayo inaweza kuzidi ukubwa wa muundo wa seccomp_data;

-idadi ya maagizo yaliyopitishwa kwenye kichungi huzidi kiwango cha juu;

- ENOMEM - kumbukumbu haitoshi kutekeleza programu;

- EOPNOTSUPP - operesheni ilionyesha kuwa kwa SECCOMP_GET_ACTION_AVAIL kitendo kilipatikana, lakini kernel haiauni marejesho katika hoja;

- ESRCH - shida ilitokea wakati wa kusawazisha mkondo mwingine;

- ENOSYS - Hakuna kifuatiliaji kilichoambatishwa kwa kitendo cha SECCOMP_RET_TRACE.

prctl ni simu ya mfumo ambayo huruhusu programu ya nafasi ya mtumiaji kudhibiti (kuweka na kupata) vipengele mahususi vya mchakato, kama vile kuishia kwa baiti, majina ya nyuzi, hali salama ya kukokotoa (Seccomp), hakimiliki, matukio ya Perf, n.k.

Seccomp inaweza kuonekana kama teknolojia ya sanduku la mchanga kwako, lakini sivyo. Seccomp ni matumizi ambayo inaruhusu watumiaji kuunda utaratibu wa sandbox. Sasa hebu tuangalie jinsi programu za mwingiliano wa watumiaji zinaundwa kwa kutumia kichungi kinachoitwa moja kwa moja na simu ya mfumo wa Seccomp.

Mfano wa Kichujio cha BPF Seccomp

Hapa tutaonyesha jinsi ya kuchanganya vitendo viwili vilivyojadiliwa hapo awali, yaani:

- tutaandika programu ya Seccomp BPF, ambayo itatumika kama kichungi na nambari tofauti za kurejesha kulingana na maamuzi yaliyofanywa;

β€” pakia kichungi kwa kutumia prctl.

Kwanza unahitaji vichwa kutoka kwa maktaba ya kawaida na kernel ya 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>

Kabla ya kujaribu mfano huu, ni lazima tuhakikishe kuwa kerneli imejumuishwa na CONFIG_SECCOMP na CONFIG_SECCOP_FILTER imewekwa kuwa y. Kwenye mashine ya kufanya kazi unaweza kuangalia hii kama hii:

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

Msimbo uliosalia ni chaguo la kukokotoa la install_filter lenye sehemu mbili. Sehemu ya kwanza ina orodha yetu ya maagizo ya kuchuja ya 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),
  };

Maagizo yamewekwa kwa kutumia makro ya BPF_STMT na BPF_JUMP iliyofafanuliwa katika faili ya linux/filter.h.
Wacha tupitie maagizo.

- BPF_STMT(BPF_LD + BPF_W + BPF_ABS (offsetof(muundo seccomp_data, arch)))) - mfumo hupakia na kujilimbikiza kutoka BPF_LD kwa namna ya neno BPF_W, data ya pakiti iko katika kukabiliana na fasta BPF_ABS.

- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arch, 0, 3) - hukagua kwa kutumia BPF_JEQ ikiwa thamani ya usanifu katika mkusanyiko wa BPF_K isiyobadilika ni sawa na upinde. Ikiwa ni hivyo, ruka katika kukabiliana na 0 hadi maagizo yanayofuata, vinginevyo ruka kwenye kukabiliana na 3 (katika kesi hii) kutupa hitilafu kwa sababu upinde haulingani.

- BPF_STMT(BPF_LD + BPF_W + BPF_ABS (offsetof(muundo seccomp_data, nr))) - Hupakia na kukusanyika kutoka BPF_LD katika umbo la neno BPF_W, ambayo ni nambari ya simu ya mfumo iliyo katika upatanisho usiobadilika wa BPF_ABS.

β€” BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, nr, 0, 1) - inalinganisha nambari ya simu ya mfumo na thamani ya kigezo cha nr. Ikiwa ni sawa, inakwenda kwenye maagizo yanayofuata na kuzima simu ya mfumo, vinginevyo itaruhusu simu ya mfumo na SECCOMP_RET_ALLOW.

- BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ERRNO | (hitilafu & SECCOMP_RET_DATA)) - husitisha programu kwa BPF_RET na matokeo yake huleta hitilafu SECCOMP_RET_ERRNO na nambari kutoka kwa tofauti ya hitilafu.

- BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW) - husitisha programu kwa BPF_RET na kuruhusu simu ya mfumo kutekelezwa kwa kutumia SECCOMP_RET_ALLOW.

SECCMP NI CBPF
Unaweza kuwa unashangaa kwa nini orodha ya maagizo inatumiwa badala ya kitu kilichokusanywa cha ELF au programu ya C iliyojumuishwa na JIT.

Kuna sababu mbili za hii.

β€’ Kwanza, Seccomp hutumia cBPF (BPF ya kawaida) na si eBPF, ambayo ina maana: haina rejista, lakini ni kikusanyaji tu cha kuhifadhi matokeo ya mwisho ya hesabu, kama inavyoonekana katika mfano.

β€’ Pili, Seccomp inakubali kielekezi kwa safu ya maagizo ya BPF moja kwa moja na si chochote kingine. Macro ambazo tumetumia husaidia tu kubainisha maagizo haya kwa njia ya kiprogramu-programu.

Ikiwa unahitaji usaidizi zaidi kuelewa mkusanyiko huu, fikiria pseudocode ambayo hufanya kitu kimoja:

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

Baada ya kufafanua msimbo wa chujio katika muundo wa socket_filter, unahitaji kufafanua sock_fprog iliyo na msimbo na urefu uliohesabiwa wa chujio. Muundo huu wa data unahitajika kama hoja ya kutangaza mchakato utakaotekelezwa baadaye:

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

Kuna jambo moja tu lililosalia kufanya katika kitendakazi cha install_filter - pakia programu yenyewe! Ili kufanya hivyo, tunatumia prctl, kuchukua PR_SET_SECCMP kama chaguo la kuingiza hali salama ya kompyuta. Kisha tunaambia modi hiyo kupakia kichungi kwa kutumia SECCOMP_MODE_FILTER, ambayo iko katika utofauti wa prog ya aina sock_fprog:

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

Hatimaye, tunaweza kutumia kitendakazi chetu cha install_filter, lakini kabla ya hapo tunahitaji kutumia prctl kuweka PR_SET_NO_NEW_PRIVS kwa utekelezaji wa sasa na hivyo kuepuka hali ambapo michakato ya watoto hupokea mapendeleo zaidi kuliko wazazi wao. Kwa hili, tunaweza kupiga simu za prctl zifuatazo kwenye kitendakazi cha kichungi cha install_filter bila kuwa na haki za mizizi.

Sasa tunaweza kuita install_filter kazi. Wacha tuzuie simu zote za mfumo wa uandishi zinazohusiana na usanifu wa X86-64 na tupe ruhusa ambayo inazuia majaribio yote. Baada ya kusanikisha kichungi, tunaendelea kutekeleza kwa kutumia hoja ya kwanza:

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

Tuanze. Kukusanya programu yetu tunaweza kutumia aidha clang au gcc, kwa vyovyote vile ni kuandaa tu faili kuu bila chaguo maalum:

clang main.c -o filter-write

Kama ilivyobainishwa, tumezuia maingizo yote kwenye programu. Ili kujaribu hii unahitaji programu ambayo hutoa kitu - ls inaonekana kama mgombea mzuri. Hivi ndivyo anavyofanya kawaida:

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

Ajabu! Hivi ndivyo kutumia programu yetu ya kanga inaonekana kama: Tunapitisha tu programu tunayotaka kujaribu kama hoja ya kwanza:

./filter-write "ls -la"

Inapotekelezwa, programu hii hutoa pato tupu kabisa. Walakini, tunaweza kutumia strace kuona kinachoendelea:

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

Matokeo ya kazi yamefupishwa sana, lakini sehemu yake inayolingana inaonyesha kuwa rekodi zimezuiwa na hitilafu ya EPERM - ile ile tuliyosanidi. Hii inamaanisha kuwa programu haitoi chochote kwa sababu haiwezi kufikia simu ya mfumo wa uandishi:

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

Sasa unaelewa jinsi Seccomp BPF inavyofanya kazi na una wazo nzuri la nini unaweza kufanya nayo. Lakini si ungependa kufikia jambo lile lile na eBPF badala ya cBPF kutumia uwezo wake kamili?

Wakati wa kufikiria kuhusu programu za eBPF, watu wengi hufikiri kwamba wanaziandika tu na kuzipakia na marupurupu ya msimamizi. Ingawa taarifa hii kwa ujumla ni kweli, kernel hutekeleza seti ya mbinu za kulinda vitu vya eBPF katika viwango mbalimbali. Taratibu hizi huitwa mitego ya BPF LSM.

Mitego ya BPF LSM

Ili kutoa ufuatiliaji unaojitegemea wa usanifu wa matukio ya mfumo, LSM hutekeleza dhana ya mitego. Simu ya ndoano inafanana kitaalam na simu ya mfumo, lakini inajitegemea na imeunganishwa na miundombinu. LSM hutoa dhana mpya ambayo safu ya uondoaji inaweza kusaidia kuzuia shida zinazopatikana wakati wa kushughulikia simu za mfumo kwenye usanifu tofauti.

Wakati wa kuandika, kernel ina ndoano saba zinazohusiana na programu za BPF, na SELinux ndiyo LSM pekee iliyojengwa ambayo inazitekeleza.

Nambari ya chanzo cha mitego iko kwenye mti wa kernel kwenye faili ni pamoja na/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);

Kila mmoja wao ataitwa katika hatua tofauti za utekelezaji:

- security_bpf - hufanya ukaguzi wa awali wa simu za mfumo wa BPF zilizotekelezwa;

- security_bpf_map - huangalia wakati kernel inarudi maelezo ya faili kwa ramani;

- security_bpf_prog - huangalia wakati kernel inarudi maelezo ya faili kwa programu ya eBPF;

β€” security_bpf_map_alloc β€” hukagua kama sehemu ya usalama ndani ya ramani za BPF imeanzishwa;

- security_bpf_map_free - hukagua kama sehemu ya usalama imefutwa ndani ya ramani za BPF;

β€” security_bpf_prog_alloc β€” hukagua kama sehemu ya usalama imeanzishwa ndani ya programu za BPF;

- security_bpf_prog_free - hukagua kama sehemu ya usalama imefutwa ndani ya programu za BPF.

Sasa, kwa kuona haya yote, tunaelewa: wazo la viingiliaji vya LSM BPF ni kwamba wanaweza kutoa ulinzi kwa kila kitu cha eBPF, kuhakikisha kwamba ni wale tu walio na haki zinazofaa wanaweza kufanya shughuli kwenye kadi na programu.

Muhtasari

Usalama si kitu unachoweza kutekeleza kwa njia moja kwa kila kitu unachotaka kulinda. Ni muhimu kuwa na uwezo wa kulinda mifumo katika viwango tofauti na kwa njia tofauti. Amini usiamini, njia bora ya kupata mfumo ni kuandaa viwango tofauti vya ulinzi kutoka kwa nafasi tofauti, ili kupunguza usalama wa ngazi moja hairuhusu ufikiaji wa mfumo mzima. Wasanidi wa msingi wamefanya kazi nzuri ya kutupa seti ya tabaka tofauti na sehemu za kugusa. Tunatumai tumekupa ufahamu mzuri wa tabaka ni nini na jinsi ya kutumia programu za BPF kufanya kazi nazo.

Kuhusu waandishi

David Calavera ni CTO katika Netlify. Alifanya kazi katika usaidizi wa Docker na akachangia katika ukuzaji wa zana za Runc, Go na BCC, pamoja na miradi mingine ya chanzo huria. Anajulikana kwa kazi yake kwenye miradi ya Docker na ukuzaji wa mfumo wa ikolojia wa Docker. David anapenda sana grafu za mwali na daima anatafuta kuboresha utendaji.

Lorenzo Fontana anafanya kazi kwenye timu ya programu huria huko Sysdig, ambapo anaangazia sana Falco, mradi wa Cloud Native Computing Foundation ambao hutoa usalama wa wakati wa kukimbia wa kontena na utambuzi wa hitilafu kupitia moduli ya kernel na eBPF. Anapenda sana mifumo iliyosambazwa, mtandao uliofafanuliwa wa programu, kinu cha Linux, na uchanganuzi wa utendaji.

Β» Maelezo zaidi kuhusu kitabu yanaweza kupatikana tovuti ya mchapishaji
Β» Meza ya yaliyomo
Β» Dondoo

Kwa Khabrozhiteley punguzo la 25% kwa kutumia kuponi - Linux

Baada ya malipo ya toleo la karatasi la kitabu, kitabu cha elektroniki kitatumwa kwa barua pepe.

Chanzo: mapenzi.com

Kuongeza maoni