"Linux monitoringi uchun BPF" kitobi

"Linux monitoringi uchun BPF" kitobiSalom, Khabro aholisi! BPF virtual mashinasi Linux yadrosining eng muhim komponentlaridan biridir. Undan to'g'ri foydalanish tizim muhandislariga nosozliklarni topish va hatto eng murakkab muammolarni hal qilish imkonini beradi. Siz yadro harakatini kuzatuvchi va o'zgartiruvchi dasturlarni yozishni, yadrodagi hodisalarni kuzatish uchun kodni xavfsiz amalga oshirishni va boshqa ko'p narsalarni o'rganasiz. David Kalavera va Lorenzo Fontana sizga BPF kuchini ochishga yordam beradi. Ishlashni optimallashtirish, tarmoq, xavfsizlik bo'yicha bilimlaringizni kengaytiring. - Linux yadrosining harakatini kuzatish va o'zgartirish uchun BPF dan foydalaning. - Yadroni qayta kompilyatsiya qilmasdan yoki tizimni qayta ishga tushirmasdan, yadro hodisalarini xavfsiz kuzatish uchun kodni kiriting. — C, Go yoki Python-da qulay kod misollaridan foydalaning. - BPF dasturining hayot aylanishiga egalik qilib, nazoratni o'z qo'lingizga oling.

Linux yadro xavfsizligi, uning xususiyatlari va Seccomp

BPF barqarorlik, xavfsizlik yoki tezlikni yo'qotmasdan yadroni kengaytirishning kuchli usulini taqdim etadi. Shu sababli, yadro ishlab chiquvchilari Seccomp BPF sifatida ham tanilgan BPF dasturlari tomonidan qo'llab-quvvatlanadigan Seccomp filtrlarini amalga oshirish orqali Seccomp-da jarayon izolyatsiyasini yaxshilash uchun uning ko'p qirraliligidan foydalanish yaxshi fikr deb o'ylashdi. Ushbu bobda Seccomp nima ekanligini va u qanday ishlatilishini tushuntiramiz. Keyin BPF dasturlari yordamida Seccomp filtrlarini yozishni o'rganasiz. Shundan so'ng, biz Linux xavfsizlik modullari uchun yadroga kiritilgan o'rnatilgan BPF ilgaklarini ko'rib chiqamiz.

Linux xavfsizlik modullari (LSM) - bu turli xil xavfsizlik modellarini standartlashtirilgan tarzda amalga oshirish uchun ishlatilishi mumkin bo'lgan funktsiyalar to'plamini ta'minlovchi ramka. LSM to'g'ridan-to'g'ri yadro manba daraxtida ishlatilishi mumkin, masalan, Apparmor, SELinux va Tomoyo.

Keling, Linux imkoniyatlarini muhokama qilishdan boshlaylik.

Xususiyatlar

Linux imkoniyatlarining mohiyati shundaki, siz ma'lum bir vazifani bajarish uchun imtiyozsiz jarayonga ruxsat berishingiz kerak, lekin bu maqsadda suid ishlatmasdan yoki boshqa usulda jarayonni imtiyozli qilib, hujum ehtimolini kamaytiradi va jarayonga muayyan vazifalarni bajarishga imkon beradi. Misol uchun, agar ilovangiz imtiyozli portni ochishi kerak bo'lsa, masalan, 80, jarayonni ildiz sifatida ishga tushirish o'rniga, unga CAP_NET_BIND_SERVICE qobiliyatini berishingiz mumkin.

main.go nomli Go dasturini ko'rib chiqing:

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

Bu dastur 80-portda HTTP serveriga xizmat qiladi (bu imtiyozli port). Odatda biz uni kompilyatsiyadan so'ng darhol ishga tushiramiz:

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

Biroq, biz ildiz huquqlarini bermaganimiz sababli, ushbu kod portni ulashda xatoga yo'l qo'yadi:

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

capsh (qobiq menejeri) - bu ma'lum imkoniyatlar to'plamiga ega qobiqni boshqaradigan vosita.

Bunday holda, yuqorida aytib o'tilganidek, to'liq ildiz huquqlarini berish o'rniga, dasturda mavjud bo'lgan barcha narsalar bilan birga cap_net_bind_service qobiliyatini taqdim etish orqali imtiyozli port ulanishini yoqishingiz mumkin. Buning uchun dasturimizni capsh bilan qo'shishimiz mumkin:

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

Keling, bu jamoani biroz tushunaylik.

  • capsh - qobiq sifatida capshdan foydalaning.
  • —caps='cap_net_bind_service+eip cap_setpcap,cap_setuid,cap_setgid+ep' - foydalanuvchini o'zgartirishimiz kerakligi sababli (biz root sifatida ishga tushirishni istamaymiz), biz cap_net_bind_service va foydalanuvchi identifikatorini aslida o'zgartirish imkoniyatini belgilaymiz. root hech kimga, ya'ni cap_setuid va cap_setgid.
  • —keep=1 — biz ildiz hisobidan oʻtishda oʻrnatilgan imkoniyatlarni saqlab qolmoqchimiz.
  • —user=“hech kim” — dasturni boshqarayotgan oxirgi foydalanuvchi hech kim boʻlmaydi.
  • —addamb=cap_net_bind_service — ildiz rejimidan oʻtgandan soʻng tegishli imkoniyatlarni tozalashni oʻrnating.
  • - -c "./capabilities" - faqat dasturni ishga tushiring.

Bog'langan imkoniyatlar - bu joriy dastur ularni execve() yordamida bajarganida, bolalar dasturlari tomonidan meros qilib olingan qobiliyatlarning maxsus turi. Faqatgina bog'lanishga ruxsat berilgan qobiliyatlar yoki boshqacha qilib aytganda, atrof-muhit imkoniyatlari sifatida meros qilib olinishi mumkin.

--caps variantida imkoniyatni belgilaganingizdan keyin +eip nimani anglatishini qiziqtirgandirsiz. Ushbu bayroqlar qobiliyatni aniqlash uchun ishlatiladi:

-faollashtirilgan bo'lishi kerak (p);

-foydalanish uchun mavjud (e);

-bola jarayonlar tomonidan meros bo'lishi mumkin (i).

Biz cap_net_bind_service dan foydalanmoqchi bo'lganimiz uchun buni e bayrog'i bilan qilishimiz kerak. Keyin buyruqda qobiqni ishga tushiramiz. Bu ikkilik qobiliyatlarni ishga tushiradi va biz uni i bayrog'i bilan belgilashimiz kerak. Nihoyat, biz ushbu xususiyatni yoqishni xohlaymiz (biz buni UIDni o'zgartirmasdan qildik) p. Bu cap_net_bind_service+eipga o'xshaydi.

Natijani ss yordamida tekshirishingiz mumkin. Keling, sahifaga sig'ishi uchun chiqishni biroz qisqartiraylik, lekin u 0 dan boshqa tegishli port va foydalanuvchi identifikatorini ko'rsatadi, bu holda 65:

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

Ushbu misolda biz capsh dan foydalandik, lekin siz libcap yordamida qobiq yozishingiz mumkin. Qo'shimcha ma'lumot olish uchun man 3 libcap-ga qarang.

Dasturlarni yozishda, ko'pincha ishlab chiquvchi dasturning ishlash vaqtida kerak bo'lgan barcha xususiyatlarini oldindan bilmaydi; Bundan tashqari, bu xususiyatlar yangi versiyalarda o'zgarishi mumkin.

Dasturimizning imkoniyatlarini yaxshiroq tushunish uchun biz cap_capable yadro funktsiyasi uchun kprobeni o'rnatadigan BCC qobiliyatiga ega vositani olishimiz mumkin:

/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

Xuddi shu narsaga cap_capable yadro funksiyasida bir qatorli kprob bilan bpftrace yordamida erishishimiz mumkin:

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

Agar kprobe dan keyin dasturimiz imkoniyatlari yoqilgan bo'lsa, bu quyidagi kabi chiqadi:

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

Beshinchi ustun jarayon uchun zarur bo'lgan imkoniyatlardir va bu chiqish auditorlikdan tashqari hodisalarni o'z ichiga olganligi sababli, biz barcha noaudit tekshiruvlarini va nihoyat audit bayrog'i (chiqishda oxirgi) 1 ga o'rnatilgan talab qilinadigan qobiliyatni ko'ramiz. Bizni qiziqtiradigan narsa CAP_NET_BIND_SERVICE bo'lib, u 10 identifikatori bilan include/uapi/linux/ability.h faylidagi yadro manba kodidagi konstanta sifatida aniqlanadi:

/* Allows binding to TCP/UDP sockets below 1024 */
/* Allows binding to ATM VCIs below 32 */
#define CAP_NET_BIND_SERVICE 10<source lang="go">

Imtiyozsiz rejimda ishlashga ruxsat berish uchun runC yoki Docker kabi konteynerlar uchun ko'pincha qobiliyatlar ish vaqtida yoqiladi, lekin ularga faqat ko'pgina ilovalarni ishga tushirish uchun zarur bo'lgan imkoniyatlarga ruxsat beriladi. Ilova muayyan imkoniyatlarni talab qilganda, Docker ularni --cap-add yordamida taqdim etishi mumkin:

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

Ushbu buyruq konteynerga CAP_NET_ADMIN qobiliyatini beradi va unga dummy0 interfeysini qo'shish uchun tarmoq havolasini sozlash imkonini beradi.

Keyingi bo'lim filtrlash kabi xususiyatlardan qanday foydalanishni ko'rsatadi, lekin o'z filtrlarimizni dasturiy ravishda amalga oshirishga imkon beruvchi boshqa texnikadan foydalanish.

Seccomp

Seccomp Xavfsiz hisoblash degan ma'noni anglatadi va Linux yadrosida amalga oshirilgan xavfsizlik qatlami bo'lib, ishlab chiquvchilarga ma'lum tizim qo'ng'iroqlarini filtrlash imkonini beradi. Seccomp imkoniyatlari bo'yicha Linux bilan solishtirish mumkin bo'lsa-da, uning ma'lum tizim qo'ng'iroqlarini boshqarish qobiliyati ularga nisbatan ancha moslashuvchan qiladi.

Seccomp va Linux xususiyatlari bir-birini istisno qilmaydi va ko'pincha ikkala yondashuvdan foyda olish uchun birgalikda ishlatiladi. Masalan, siz jarayonga CAP_NET_ADMIN qobiliyatini berishni xohlashingiz mumkin, lekin unga rozetka ulanishlarini qabul qilishga ruxsat bermaslik, tizim qo'ng'iroqlarini qabul qilish va qabul qilish4 ni bloklash.

Seccomp filtrlash usuli SECCOMP_MODE_FILTER rejimida ishlaydigan BPF filtrlariga asoslanadi va tizim chaqiruvlarini filtrlash paketlar bilan bir xil tarzda amalga oshiriladi.

Seccomp filtrlari PR_SET_SECCOMP operatsiyasi orqali prctl yordamida yuklanadi. Bu filtrlar seccomp_data strukturasi bilan ifodalangan har bir Seccomp paketi uchun bajariladigan BPF dasturi shaklini oladi. Ushbu tuzilma mos yozuvlar arxitekturasini, tizim chaqiruvi vaqtida protsessor ko'rsatmalariga ko'rsatgichni va uint64 sifatida ifodalangan ko'pi bilan oltita tizim chaqiruvi argumentini o'z ichiga oladi.

Linux/seccomp.h faylidagi yadro manba kodidan seccomp_data tuzilishi shunday ko'rinadi:

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

Ushbu strukturadan ko'rinib turibdiki, biz tizim chaqiruvi, uning argumentlari yoki ikkalasining kombinatsiyasi bo'yicha filtrlashimiz mumkin.

Har bir Seccomp paketini olgandan so'ng, filtr yakuniy qaror qabul qilish uchun qayta ishlashni amalga oshirishi va yadroga keyin nima qilish kerakligini aytishi kerak. Yakuniy qaror qaytish qiymatlaridan biri (holat kodlari) bilan ifodalanadi.

- SECCOMP_RET_KILL_PROCESS - shu sababli bajarilmagan tizim chaqiruvini filtrlagandan so'ng darhol butun jarayonni o'ldiradi.

- SECCOMP_RET_KILL_THREAD - shu sababli bajarilmagan tizim chaqiruvini filtrlagandan so'ng darhol joriy oqimni tugatadi.

— SECCOMP_RET_KILL — SECCOMP_RET_KILL_THREAD uchun taxallus, orqaga moslik uchun qoldirilgan.

- SECCOMP_RET_TRAP - tizim qo'ng'irog'i taqiqlangan va SIGSYS (Bad System Call) signali uni chaqiradigan vazifaga yuboriladi.

- SECCOMP_RET_ERRNO - Tizim chaqiruvi bajarilmaydi va SECCOMP_RET_DATA filtri qaytarish qiymatining bir qismi errno qiymati sifatida foydalanuvchi maydoniga uzatiladi. Xatoning sababiga qarab, turli xil xato qiymatlari qaytariladi. Xato raqamlari ro'yxati keyingi bo'limda keltirilgan.

- SECCOMP_RET_TRACE - Bu jarayonni ko'rish va boshqarish uchun tizim chaqiruvi bajarilganda ushlab qolish uchun - PTRACE_O_TRACESECCOMP yordamida ptrace tracerni xabardor qilish uchun ishlatiladi. Agar kuzatuvchi ulanmagan bo'lsa, xato qaytariladi, errno -ENOSYS ga o'rnatiladi va tizim chaqiruvi bajarilmaydi.

- SECCOMP_RET_LOG - tizim qo'ng'irog'i hal qilinadi va qayd qilinadi.

- SECCOMP_RET_ALLOW - tizim chaqiruviga oddiygina ruxsat beriladi.

ptrace - bu jarayonning bajarilishini kuzatish va nazorat qilish qobiliyatiga ega, tracee deb nomlangan jarayonda kuzatuv mexanizmlarini amalga oshirish uchun tizim chaqiruvi. Kuzatuv dasturi bajarilishiga samarali ta'sir qilishi va kuzatuvchining xotira registrlarini o'zgartirishi mumkin. Seccomp kontekstida ptrace SECCOMP_RET_TRACE holat kodi tomonidan ishga tushirilganda ishlatiladi, shuning uchun kuzatuvchi tizim chaqiruvining bajarilishiga to'sqinlik qilishi va o'z mantiqini amalga oshirishi mumkin.

Seccomp xatolar

Vaqti-vaqti bilan Seccomp bilan ishlashda siz turli xil xatolarga duch kelasiz, ular SECCOMP_RET_ERRNO tipidagi qaytish qiymati bilan aniqlanadi. Xato haqida xabar berish uchun seccomp tizimi chaqiruvi 1 o'rniga -0 qaytaradi.

Quyidagi xatolar mumkin:

- EACCESS - qo'ng'iroq qiluvchiga tizim qo'ng'irog'ini amalga oshirishga ruxsat berilmagan. Bu odatda CAP_SYS_ADMIN imtiyozlariga ega emasligi yoki no_new_privs prctl yordamida o'rnatilmaganligi sababli sodir bo'ladi (bu haqda keyinroq gaplashamiz);

— EFAULT — uzatilgan argumentlar (seccomp_data strukturasidagi argumentlar) haqiqiy manzilga ega emas;

— EINVAL — bu yerda toʻrtta sabab boʻlishi mumkin:

-so'ralgan operatsiya noma'lum yoki joriy konfiguratsiyadagi yadro tomonidan qo'llab-quvvatlanmaydi;

-ko'rsatilgan bayroqlar so'ralgan operatsiya uchun haqiqiy emas;

-operatsiya BPF_ABSni o'z ichiga oladi, lekin ko'rsatilgan ofset bilan bog'liq muammolar mavjud bo'lib, ular seccomp_data strukturasi hajmidan oshib ketishi mumkin;

-filtrga o'tkazilgan ko'rsatmalar soni maksimaldan oshib ketgan;

— ENOMEM — dasturni bajarish uchun xotira yetarli emas;

- EOPNOTSUPP - operatsiya SECCOMP_GET_ACTION_AVAIL bilan amal mavjud ekanligini ko'rsatdi, ammo yadro argumentlarda qaytishni qo'llab-quvvatlamaydi;

— ESRCH — boshqa oqimni sinxronlashda muammo yuzaga keldi;

- ENOSYS - SECCOMP_RET_TRACE amaliga hech qanday kuzatuvchi biriktirilmagan.

prctl - bu foydalanuvchi-kosmik dasturga jarayonning o'ziga xos tomonlarini, masalan, baytning o'zgarishi, ip nomlari, xavfsiz hisoblash rejimi (Seccomp), imtiyozlar, Perf hodisalari va boshqalarni boshqarish (o'rnatish va olish) imkonini beruvchi tizim chaqiruvidir.

Seccomp siz uchun sandbox texnologiyasidek tuyulishi mumkin, ammo unday emas. Seccomp - foydalanuvchilarga sandbox mexanizmini ishlab chiqish imkonini beruvchi yordamchi dastur. Endi to'g'ridan-to'g'ri Seccomp tizim chaqiruvi orqali chaqiriladigan filtr yordamida foydalanuvchilarning o'zaro ta'siri dasturlari qanday yaratilganligini ko'rib chiqamiz.

BPF Seccomp filtriga misol

Bu erda biz ilgari muhokama qilingan ikkita harakatni qanday birlashtirishni ko'rsatamiz, xususan:

— biz Seccomp BPF dasturini yozamiz, u qabul qilingan qarorlarga qarab turli qaytarish kodlari bilan filtr sifatida foydalaniladi;

— prctl yordamida filtrni yuklang.

Avval sizga standart kutubxona va Linux yadrosidan sarlavhalar kerak:

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

Ushbu misolni sinab ko'rishdan oldin, yadro CONFIG_SECCOMP va CONFIG_SECCOMP_FILTER bilan y ga o'rnatilganligiga ishonch hosil qilishimiz kerak. Ishlaydigan mashinada buni quyidagicha tekshirishingiz mumkin:

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

Kodning qolgan qismi ikki qismli install_filter funktsiyasidir. Birinchi qismda BPF filtrlash ko'rsatmalarimiz ro'yxati mavjud:

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

Ko'rsatmalar linux/filter.h faylida belgilangan BPF_STMT va BPF_JUMP makroslari yordamida o'rnatiladi.
Keling, ko'rsatmalarni ko'rib chiqaylik.

- BPF_STMT(BPF_LD + BPF_W + BPF_ABS (offsetof(struct seccomp_data, arch))) - tizim BPF_LD dan BPF_W so'zi ko'rinishida yuklanadi va to'planadi, paketli ma'lumotlar BPF_ABS o'zgarmas ofsetida joylashgan.

- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arch, 0, 3) - BPF_JEQ yordamida BPF_K akkumulyator konstantasidagi arxitektura qiymati arch ga teng yoki yo'qligini tekshiradi. Agar shunday bo'lsa, keyingi ko'rsatmaga 0 ofsetiga o'tadi, aks holda arch mos kelmasligi sababli xatoga yo'l qo'yish uchun 3 ofsetiga (bu holda) o'tadi.

- BPF_STMT(BPF_LD + BPF_W + BPF_ABS (offsetof(struct seccomp_data, nr))) - BPF_LD dan BPF_W so'zi ko'rinishida yuklaydi va to'planadi, bu BPF_ABS ning belgilangan ofsetidagi tizim chaqiruv raqami.

— BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, nr, 0, 1) — tizim chaqiruv raqamini nr o‘zgaruvchisi qiymati bilan solishtiradi. Agar ular teng bo'lsa, keyingi yo'riqnomaga o'tadi va tizim chaqiruvini o'chiradi, aks holda SECCOMP_RET_ALLOW bilan tizim chaqiruviga ruxsat beradi.

- BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ERRNO | (xato & SECCOMP_RET_DATA)) - dasturni BPF_RET bilan tugatadi va natijada err o'zgaruvchisi raqami bilan SECCOMP_RET_ERRNO xatosini keltirib chiqaradi.

- BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW) - dasturni BPF_RET bilan tugatadi va SECCOMP_RET_ALLOW yordamida tizim chaqiruvini bajarishga imkon beradi.

SECCOMP - CBPF
Nima uchun kompilyatsiya qilingan ELF ob'ekti yoki JIT kompilyatsiya qilingan C dasturi o'rniga ko'rsatmalar ro'yxati ishlatilganligi sizni qiziqtirgan bo'lishi mumkin.

Buning ikkita sababi bor.

• Birinchidan, Seccomp eBPF emas, balki cBPF (klassik BPF) dan foydalanadi, ya'ni: unda registrlar yo'q, faqat misolda ko'rinib turganidek, hisobning oxirgi natijasini saqlash uchun akkumulyator mavjud.

• Ikkinchidan, Seccomp to'g'ridan-to'g'ri BPF ko'rsatmalari qatoriga ko'rsatgichni qabul qiladi va boshqa hech narsa yo'q. Biz foydalangan makroslar ushbu ko'rsatmalarni dasturchiga qulay tarzda belgilashga yordam beradi.

Agar sizga ushbu yig'ilishni tushunishda ko'proq yordam kerak bo'lsa, xuddi shu narsani bajaradigan psevdokodni ko'rib chiqing:

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

Socket_filter tuzilmasida filtr kodini aniqlagandan so'ng, siz kodni va filtrning hisoblangan uzunligini o'z ichiga olgan sock_fprog ni belgilashingiz kerak. Ushbu ma'lumotlar strukturasi jarayonni keyinroq ishga tushirishni e'lon qilish uchun argument sifatida kerak:

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

install_filter funksiyasida faqat bitta narsa qoladi - dasturni o'zi yuklang! Buni amalga oshirish uchun biz xavfsiz hisoblash rejimiga kirish uchun PR_SET_SECCOMP variantini olib, prctl dan foydalanamiz. Keyin rejimga filtrni sock_fprog tipidagi prog o'zgaruvchisida joylashgan SECCOMP_MODE_FILTER yordamida yuklashni aytamiz:

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

Nihoyat, biz install_filter funksiyamizdan foydalanishimiz mumkin, lekin bundan oldin joriy bajarish uchun PR_SET_NO_NEW_PRIVS ni o'rnatish uchun prctl dan foydalanishimiz kerak va shu bilan bolalar jarayonlari ota-onalariga qaraganda ko'proq imtiyozlarga ega bo'lgan vaziyatdan qochishimiz kerak. Buning yordamida biz root huquqlariga ega bo'lmasdan install_filter funksiyasida quyidagi prctl qo'ng'iroqlarini amalga oshirishimiz mumkin.

Endi biz install_filter funksiyasini chaqirishimiz mumkin. Keling, X86-64 arxitekturasi bilan bog'liq barcha yozish tizimi qo'ng'iroqlarini bloklaymiz va shunchaki barcha urinishlarni bloklaydigan ruxsat beramiz. Filtrni o'rnatgandan so'ng, biz birinchi argument yordamida bajarishni davom ettiramiz:

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

Qani boshladik. Dasturimizni kompilyatsiya qilish uchun biz clang yoki gcc dan foydalanishimiz mumkin, har qanday holatda u faqat main.c faylini maxsus variantlarsiz kompilyatsiya qiladi:

clang main.c -o filter-write

Ta'kidlanganidek, biz dasturdagi barcha yozuvlarni blokladik. Buni sinab ko'rish uchun sizga biror narsa chiqaradigan dastur kerak - ls yaxshi nomzodga o'xshaydi. Odatda u o'zini shunday tutadi:

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

Ajoyib! Bizning o'rash dasturimizdan foydalanish quyidagicha ko'rinadi: Biz sinab ko'rmoqchi bo'lgan dasturni birinchi argument sifatida topshiramiz:

./filter-write "ls -la"

Amalga oshirilganda, bu dastur butunlay bo'sh natija beradi. Biroq, nima bo'layotganini ko'rish uchun biz strace dan foydalanishimiz mumkin:

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

Ishning natijasi sezilarli darajada qisqartirildi, ammo uning tegishli qismi yozuvlar EPERM xatosi bilan bloklanganligini ko'rsatadi - biz o'rnatganimiz bilan bir xil. Bu shuni anglatadiki, dastur hech narsa chiqarmaydi, chunki u yozish tizimi chaqiruviga kira olmaydi:

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

Endi siz Seccomp BPF qanday ishlashini tushunasiz va u bilan nima qilish mumkinligi haqida yaxshi tasavvurga egasiz. Ammo uning to'liq quvvatidan foydalanish uchun cBPF o'rniga eBPF bilan bir xil narsaga erishmoqchi emasmisiz?

eBPF dasturlari haqida o'ylashganda, ko'pchilik ularni shunchaki yozadi va ularni administrator imtiyozlari bilan yuklaydi deb o'ylaydi. Ushbu bayonot odatda to'g'ri bo'lsa-da, yadro turli darajadagi eBPF ob'ektlarini himoya qilish uchun bir qator mexanizmlarni amalga oshiradi. Ushbu mexanizmlar BPF LSM tuzoqlari deb ataladi.

BPF LSM tuzoqlari

Tizim hodisalarining arxitekturadan mustaqil monitoringini ta'minlash uchun LSM tuzoq kontseptsiyasini amalga oshiradi. Hook qo‘ng‘iroq texnik jihatdan tizim chaqiruviga o‘xshaydi, lekin tizimdan mustaqil va infratuzilma bilan integratsiyalashgan. LSM yangi kontseptsiyani taqdim etadi, unda abstraktsiya qatlami turli arxitekturalarda tizim chaqiruvlari bilan ishlashda yuzaga keladigan muammolarni oldini olishga yordam beradi.

Yozish vaqtida yadroda BPF dasturlari bilan bog'langan etti ilgak mavjud va SELinux ularni amalga oshiradigan yagona o'rnatilgan LSM hisoblanadi.

Qopqonlarning manba kodi include/linux/security.h faylidagi yadro daraxtida joylashgan:

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

Ularning har biri ijro etilishning turli bosqichlarida chaqiriladi:

— security_bpf — bajarilgan BPF tizimi chaqiruvlarini dastlabki tekshirishni amalga oshiradi;

- security_bpf_map - yadro xarita uchun fayl deskriptorini qachon qaytarishini tekshiradi;

- security_bpf_prog - yadro eBPF dasturi uchun fayl deskriptorini qachon qaytarishini tekshiradi;

— security_bpf_map_alloc — BPF xaritalari ichidagi xavfsizlik maydoni ishga tushirilganligini tekshiradi;

- security_bpf_map_free - BPF xaritalarida xavfsizlik maydoni tozalanganligini tekshiradi;

— security_bpf_prog_alloc — xavfsizlik maydoni BPF dasturlarida ishga tushirilganligini tekshiradi;

- security_bpf_prog_free - xavfsizlik maydoni BPF dasturlari ichida tozalanganligini tekshiradi.

Endi, bularning barchasini ko'rib, biz tushunamiz: LSM BPF to'xtatuvchilarining g'oyasi shundaki, ular har bir eBPF ob'ektini himoya qila oladi, faqat tegishli imtiyozlarga ega bo'lganlar kartalar va dasturlarda operatsiyalarni bajarishi mumkin.

Xulosa

Xavfsizlik - bu siz himoya qilmoqchi bo'lgan hamma narsa uchun bir xil tarzda amalga oshirishingiz mumkin bo'lgan narsa emas. Tizimlarni turli darajadagi va turli yo'llar bilan himoya qila olish muhimdir. Ishoning yoki ishonmang, tizimni himoya qilishning eng yaxshi usuli - bu har xil pozitsiyalardan himoyalanishning turli darajalarini tashkil qilishdir, shuning uchun bir darajadagi xavfsizlikni pasaytirish butun tizimga kirishga imkon bermaydi. Asosiy ishlab chiquvchilar bizga turli qatlamlar va teginish nuqtalari to'plamini taqdim etish bo'yicha ajoyib ish qildilar. Umid qilamizki, biz sizga qatlamlar nima ekanligini va ular bilan ishlashda BPF dasturlaridan qanday foydalanish haqida yaxshi tushuncha berdik.

Mualliflar haqida

Devid Kalavera Netlify kompaniyasining texnik direktori. U Docker qo'llab-quvvatlashida ishlagan va Runc, Go va BCC vositalarini, shuningdek, boshqa ochiq manba loyihalarini ishlab chiqishga hissa qo'shgan. Docker loyihalari va Docker plaginlari ekotizimini ishlab chiqishdagi ishi bilan tanilgan. Devid olovli grafiklarga juda ishtiyoqlidir va har doim ishlashni optimallashtirishga intiladi.

Lorenzo Fontana Sysdig’dagi ochiq manbalar jamoasida ishlaydi, u yerda u asosan Falco loyihasiga, Cloud Native Computing Foundation loyihasiga e’tibor qaratadi, u yadro moduli va eBPF orqali konteyner ish vaqti xavfsizligi va anomaliyalarni aniqlashni ta’minlaydi. U taqsimlangan tizimlar, dasturiy ta'minot bilan aniqlangan tarmoqlar, Linux yadrosi va ishlash tahliliga ishtiyoqlidir.

» Kitob haqida batafsil ma’lumotni quyidagi manzildan olishingiz mumkin nashriyot sayti
» Mundarija
» Parcha

Xabrozhiteley uchun kupondan foydalangan holda 25% chegirma - Linux

Kitobning qog'oz versiyasi uchun to'lov amalga oshirilgandan so'ng, elektron kitob elektron pochta orqali yuboriladi.

Manba: www.habr.com

a Izoh qo'shish