Tempah "BPF untuk Pemantauan Linux"

Tempah "BPF untuk Pemantauan Linux"Hello, penduduk Khabro! Mesin maya BPF adalah salah satu komponen terpenting dalam kernel Linux. Penggunaannya yang betul akan membolehkan jurutera sistem mencari kesilapan dan menyelesaikan masalah yang paling kompleks sekalipun. Anda akan belajar cara menulis program yang memantau dan mengubah suai tingkah laku kernel, cara melaksanakan kod dengan selamat untuk memantau acara dalam kernel, dan banyak lagi. David Calavera dan Lorenzo Fontana akan membantu anda membuka kunci kuasa BPF. Kembangkan pengetahuan anda tentang pengoptimuman prestasi, rangkaian, keselamatan. - Gunakan BPF untuk memantau dan mengubah suai tingkah laku kernel Linux. - Suntikan kod untuk memantau peristiwa kernel dengan selamat tanpa perlu menyusun semula kernel atau but semula sistem. — Gunakan contoh kod yang mudah dalam C, Go atau Python. - Ambil kawalan dengan memiliki kitaran hayat program BPF.

Keselamatan Kernel Linux, Ciri-cirinya dan Seccomp

BPF menyediakan cara yang berkuasa untuk memanjangkan kernel tanpa mengorbankan kestabilan, keselamatan atau kelajuan. Atas sebab ini, pembangun kernel berpendapat adalah idea yang baik untuk menggunakan serba bolehnya untuk meningkatkan pengasingan proses dalam Seccomp dengan melaksanakan penapis Seccomp yang disokong oleh program BPF, juga dikenali sebagai Seccomp BPF. Dalam bab ini kami akan menerangkan apa itu Seccomp dan bagaimana ia digunakan. Kemudian anda akan belajar cara menulis penapis Seccom menggunakan program BPF. Selepas itu, kami akan melihat cangkuk BPF terbina dalam yang disertakan dalam kernel untuk modul keselamatan Linux.

Modul Keselamatan Linux (LSM) ialah rangka kerja yang menyediakan satu set fungsi yang boleh digunakan untuk melaksanakan pelbagai model keselamatan dalam cara yang standard. LSM boleh digunakan secara langsung dalam pokok sumber kernel, seperti Apparmor, SELinux dan Tomoyo.

Mari kita mulakan dengan membincangkan keupayaan Linux.

peluang

Intipati keupayaan Linux ialah anda perlu memberikan kebenaran proses yang tidak mempunyai hak istimewa untuk melaksanakan tugas tertentu, tetapi tanpa menggunakan suid untuk tujuan itu, atau sebaliknya menjadikan proses itu istimewa, mengurangkan kemungkinan serangan dan membenarkan proses melaksanakan tugas tertentu. Contohnya, jika aplikasi anda perlu membuka port istimewa, katakan 80, dan bukannya menjalankan proses sebagai akar, anda hanya boleh memberikannya keupayaan CAP_NET_BIND_SERVICE.

Pertimbangkan program Go bernama main.go:

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

Program ini menyediakan pelayan HTTP pada port 80 (ini adalah port istimewa). Biasanya kami menjalankannya sejurus selepas penyusunan:

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

Walau bagaimanapun, kerana kami tidak memberikan keistimewaan root, kod ini akan menimbulkan ralat semasa mengikat port:

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

capsh (pengurus shell) ialah alat yang menjalankan shell dengan set keupayaan tertentu.

Dalam kes ini, seperti yang telah disebutkan, bukannya memberikan hak root penuh, anda boleh mendayakan pengikatan port istimewa dengan menyediakan keupayaan cap_net_bind_service bersama-sama dengan semua perkara lain yang sudah ada dalam program. Untuk melakukan ini, kami boleh melampirkan program kami dalam 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"

Mari kita fahami sedikit pasukan ini.

  • capsh - gunakan capsh sebagai cangkerang.
  • —caps='cap_net_bind_service+eip cap_setpcap,cap_setuid,cap_setgid+ep' - memandangkan kami perlu menukar pengguna (kami tidak mahu menjalankan sebagai root), kami akan menentukan cap_net_bind_service dan keupayaan untuk benar-benar menukar ID pengguna daripada root to nobody, iaitu cap_setuid dan cap_setgid.
  • —keep=1 — kami mahu mengekalkan keupayaan yang dipasang apabila beralih daripada akaun root.
  • —user=“nobody” — pengguna akhir yang menjalankan program ini ialah bukan sesiapa.
  • —addamb=cap_net_bind_service — tetapkan pembersihan keupayaan berkaitan selepas beralih daripada mod akar.
  • - -c "./capabilities" - hanya jalankan program.

Keupayaan terpaut ialah jenis keupayaan istimewa yang diwarisi oleh program kanak-kanak apabila program semasa melaksanakannya menggunakan execve(). Hanya keupayaan yang dibenarkan untuk dikaitkan, atau dengan kata lain, sebagai keupayaan persekitaran, boleh diwarisi.

Anda mungkin tertanya-tanya apa maksud +eip selepas menyatakan keupayaan dalam pilihan --caps. Bendera ini digunakan untuk menentukan bahawa keupayaan:

-mesti diaktifkan (p);

-tersedia untuk digunakan (e);

-boleh diwarisi oleh proses kanak-kanak (i).

Oleh kerana kita ingin menggunakan cap_net_bind_service, kita perlu melakukan ini dengan bendera e. Kemudian kita akan memulakan shell dalam arahan. Ini akan menjalankan binari keupayaan dan kita perlu menandakannya dengan bendera i. Akhir sekali, kami mahu ciri tersebut didayakan (kami melakukan ini tanpa menukar UID) dengan p. Ia kelihatan seperti cap_net_bind_service+eip.

Anda boleh menyemak keputusan menggunakan ss. Mari kita pendekkan sedikit output untuk dimuatkan pada halaman, tetapi ia akan menunjukkan port dan ID pengguna yang berkaitan selain daripada 0, dalam kes ini 65:

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

Dalam contoh ini kami menggunakan capsh, tetapi anda boleh menulis shell menggunakan libcap. Untuk maklumat lanjut, lihat man 3 libcap.

Semasa menulis atur cara, selalunya pembangun tidak mengetahui terlebih dahulu semua ciri yang diperlukan oleh program pada masa berjalan; Selain itu, ciri ini mungkin berubah dalam versi baharu.

Untuk lebih memahami keupayaan program kami, kami boleh menggunakan alat berkebolehan BCC, yang menetapkan kprobe untuk fungsi kernel 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

Kita boleh mencapai perkara yang sama dengan menggunakan bpftrace dengan kprobe satu pelapik dalam fungsi 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

Ini akan mengeluarkan sesuatu seperti berikut jika keupayaan program kami didayakan selepas 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

Lajur kelima ialah keupayaan yang diperlukan oleh proses dan memandangkan output ini termasuk peristiwa bukan audit, kami melihat semua semakan bukan audit dan akhirnya keupayaan yang diperlukan dengan bendera audit (terakhir dalam output) ditetapkan kepada 1. Keupayaan. satu yang kami minati ialah CAP_NET_BIND_SERVICE, ia ditakrifkan sebagai pemalar dalam kod sumber kernel dalam fail include/uapi/linux/ability.h dengan pengecam 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">

Keupayaan selalunya didayakan pada masa jalan untuk bekas seperti runC atau Docker untuk membolehkan mereka berjalan dalam mod yang tidak mempunyai keistimewaan, tetapi mereka hanya dibenarkan keupayaan yang diperlukan untuk menjalankan kebanyakan aplikasi. Apabila aplikasi memerlukan keupayaan tertentu, Docker boleh menyediakannya menggunakan --cap-add:

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

Perintah ini akan memberikan bekas keupayaan CAP_NET_ADMIN, membenarkan ia mengkonfigurasi pautan rangkaian untuk menambah antara muka dummy0.

Bahagian seterusnya menunjukkan cara menggunakan ciri seperti penapisan, tetapi menggunakan teknik berbeza yang membolehkan kami melaksanakan penapis kami sendiri secara pemrograman.

Seccomp

Seccomp adalah singkatan kepada Secure Computing dan merupakan lapisan keselamatan yang dilaksanakan dalam kernel Linux yang membolehkan pembangun menapis panggilan sistem tertentu. Walaupun keupayaan Seccomp setanding dengan Linux, keupayaannya untuk mengurus panggilan sistem tertentu menjadikannya lebih fleksibel berbanding dengannya.

Ciri Seccom dan Linux tidak saling eksklusif dan sering digunakan bersama untuk mendapat manfaat daripada kedua-dua pendekatan. Sebagai contoh, anda mungkin mahu memberikan proses keupayaan CAP_NET_ADMIN tetapi tidak membenarkannya menerima sambungan soket, menyekat panggilan sistem terima dan terima4.

Kaedah penapisan Seccomp adalah berdasarkan penapis BPF yang beroperasi dalam mod SECCOMP_MODE_FILTER dan penapisan panggilan sistem dilakukan dengan cara yang sama seperti untuk paket.

Penapis Seccomp dimuatkan menggunakan prctl melalui operasi PR_SET_SECCOMP. Penapis ini mengambil bentuk program BPF yang dilaksanakan untuk setiap paket Seccomp yang diwakili oleh struktur seccomp_data. Struktur ini mengandungi seni bina rujukan, penunjuk kepada arahan pemproses pada masa panggilan sistem, dan maksimum enam argumen panggilan sistem, dinyatakan sebagai uint64.

Inilah rupa struktur seccomp_data daripada kod sumber kernel dalam fail linux/seccomp.h:

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

Seperti yang anda boleh lihat daripada struktur ini, kami boleh menapis mengikut panggilan sistem, hujahnya atau gabungan kedua-duanya.

Selepas menerima setiap paket Seccomp, penapis mesti melakukan pemprosesan untuk membuat keputusan muktamad dan memberitahu kernel apa yang perlu dilakukan seterusnya. Keputusan muktamad dinyatakan oleh salah satu nilai pulangan (kod status).

- SECCOMP_RET_KILL_PROCESS - membunuh keseluruhan proses serta-merta selepas menapis panggilan sistem yang tidak dilaksanakan kerana ini.

- SECCOMP_RET_KILL_THREAD - menamatkan rangkaian semasa serta-merta selepas menapis panggilan sistem yang tidak dilaksanakan kerana ini.

— SECCOMP_RET_KILL — alias untuk SECCOMP_RET_KILL_THREAD, kiri untuk keserasian ke belakang.

- SECCOMP_RET_TRAP - panggilan sistem adalah dilarang, dan isyarat SIGSYS (Panggilan Sistem Buruk) dihantar ke tugas yang memanggilnya.

- SECCOMP_RET_ERRNO - Panggilan sistem tidak dilaksanakan dan sebahagian daripada nilai pulangan penapis SECCOMP_RET_DATA dihantar ke ruang pengguna sebagai nilai errno. Bergantung pada punca ralat, nilai errno yang berbeza dikembalikan. Senarai nombor ralat disediakan dalam bahagian seterusnya.

- SECCOMP_RET_TRACE - Digunakan untuk memberitahu pengesan ptrace menggunakan - PTRACE_O_TRACESECCOMP untuk memintas apabila panggilan sistem dilaksanakan untuk melihat dan mengawal proses tersebut. Jika pengesan tidak disambungkan, ralat dikembalikan, errno ditetapkan kepada -ENOSYS, dan panggilan sistem tidak dilaksanakan.

- SECCOMP_RET_LOG - panggilan sistem dibenarkan dan dilog.

- SECCOMP_RET_ALLOW - panggilan sistem hanya dibenarkan.

ptrace ialah panggilan sistem untuk melaksanakan mekanisme pengesanan dalam proses yang dipanggil tracee, dengan keupayaan untuk memantau dan mengawal pelaksanaan proses tersebut. Program jejak boleh mempengaruhi pelaksanaan dan mengubah suai daftar ingatan jejaki dengan berkesan. Dalam konteks Seccomp, ptrace digunakan apabila dicetuskan oleh kod status SECCOMP_RET_TRACE, jadi pengesan boleh menghalang panggilan sistem daripada melaksanakan dan melaksanakan logiknya sendiri.

Ralat Seccomp

Dari semasa ke semasa, semasa bekerja dengan Seccomp, anda akan menghadapi pelbagai ralat, yang dikenal pasti oleh nilai pulangan jenis SECCOMP_RET_ERRNO. Untuk melaporkan ralat, panggilan sistem seccomp akan kembali -1 dan bukannya 0.

Ralat berikut adalah mungkin:

- EACCESS - Pemanggil tidak dibenarkan membuat panggilan sistem. Ini biasanya berlaku kerana ia tidak mempunyai keistimewaan CAP_SYS_ADMIN atau no_new_privs tidak ditetapkan menggunakan prctl (kita akan bercakap tentang perkara ini kemudian);

— EFAULT — argumen yang diluluskan (args dalam struktur seccomp_data) tidak mempunyai alamat yang sah;

— EINVAL — mungkin terdapat empat sebab di sini:

-operasi yang diminta tidak diketahui atau tidak disokong oleh kernel dalam konfigurasi semasa;

-bendera yang dinyatakan tidak sah untuk operasi yang diminta;

-operasi termasuk BPF_ABS, tetapi terdapat masalah dengan offset yang ditentukan, yang mungkin melebihi saiz struktur seccomp_data;

-bilangan arahan yang dihantar kepada penapis melebihi maksimum;

— ENOMEM — memori tidak mencukupi untuk melaksanakan program;

- EOPNOTSUPP - operasi menunjukkan bahawa dengan SECCOMP_GET_ACTION_AVAIL tindakan itu tersedia, tetapi kernel tidak menyokong pengembalian dalam argumen;

— ESRCH — masalah berlaku semasa menyegerakkan aliran lain;

- ENOSYS - Tiada pengesan yang dilampirkan pada tindakan SECCOMP_RET_TRACE.

prctl ialah panggilan sistem yang membenarkan atur cara ruang pengguna untuk memanipulasi (menetapkan dan mendapatkan) aspek tertentu proses, seperti endian bait, nama benang, mod pengiraan selamat (Seccomp), keistimewaan, acara Perf, dsb.

Seccom mungkin kelihatan seperti teknologi kotak pasir kepada anda, tetapi tidak. Seccomp ialah utiliti yang membolehkan pengguna membangunkan mekanisme kotak pasir. Sekarang mari kita lihat bagaimana program interaksi pengguna dicipta menggunakan penapis yang dipanggil terus oleh panggilan sistem Seccomp.

Contoh Penapis BPF Seccom

Di sini kami akan menunjukkan bagaimana untuk menggabungkan dua tindakan yang dibincangkan sebelum ini, iaitu:

— kami akan menulis program Seccom BPF, yang akan digunakan sebagai penapis dengan kod pulangan berbeza bergantung pada keputusan yang dibuat;

— muatkan penapis menggunakan prctl.

Mula-mula anda memerlukan pengepala dari perpustakaan standard dan kernel 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>

Sebelum mencuba contoh ini, kita mesti memastikan bahawa kernel disusun dengan CONFIG_SECCOMP dan CONFIG_SECCOMP_FILTER ditetapkan kepada y. Pada mesin yang berfungsi, anda boleh menyemak ini seperti ini:

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

Kod selebihnya ialah fungsi install_filter dua bahagian. Bahagian pertama mengandungi senarai arahan penapisan BPF kami:

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

Arahan ditetapkan menggunakan makro BPF_STMT dan BPF_JUMP yang ditakrifkan dalam fail linux/filter.h.
Mari kita pergi melalui arahan.

- BPF_STMT(BPF_LD + BPF_W + BPF_ABS (offsetof(struct seccomp_data, arch))) - sistem dimuatkan dan terkumpul daripada BPF_LD dalam bentuk perkataan BPF_W, data paket terletak pada offset tetap BPF_ABS.

- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arch, 0, 3) - semak menggunakan BPF_JEQ sama ada nilai seni bina dalam pemalar penumpuk BPF_K adalah sama dengan arch. Jika ya, lompat pada offset 0 ke arahan seterusnya, jika tidak lompat pada offset 3 (dalam kes ini) untuk melontar ralat kerana gerbang tidak sepadan.

- BPF_STMT(BPF_LD + BPF_W + BPF_ABS (offsetof(struct seccomp_data, nr))) - Dimuat dan terkumpul daripada BPF_LD dalam bentuk perkataan BPF_W, iaitu nombor panggilan sistem yang terkandung dalam ofset tetap BPF_ABS.

— BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, nr, 0, 1) — membandingkan nombor panggilan sistem dengan nilai pembolehubah nr. Jika ia sama, teruskan ke arahan seterusnya dan lumpuhkan panggilan sistem, jika tidak, benarkan panggilan sistem dengan SECCOMP_RET_ALLOW.

- BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ERRNO | (ralat & SECCOMP_RET_DATA)) - menamatkan atur cara dengan BPF_RET dan akibatnya menghasilkan ralat SECCOMP_RET_ERRNO dengan nombor daripada pembolehubah ralat.

- BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW) - menamatkan program dengan BPF_RET dan membenarkan panggilan sistem dilaksanakan menggunakan SECCOMP_RET_ALLOW.

SECCOMP ADALAH CBPF
Anda mungkin tertanya-tanya mengapa senarai arahan digunakan dan bukannya objek ELF yang disusun atau program C yang disusun JIT.

Terdapat dua sebab untuk ini.

• Pertama, Seccomp menggunakan cBPF (BPF klasik) dan bukan eBPF, yang bermaksud: ia tidak mempunyai daftar, tetapi hanya penumpuk untuk menyimpan hasil pengiraan terakhir, seperti yang boleh dilihat dalam contoh.

• Kedua, Seccomp menerima penuding kepada tatasusunan arahan BPF secara langsung dan tiada yang lain. Makro yang kami gunakan hanya membantu menentukan arahan ini dengan cara yang mesra pengaturcara.

Jika anda memerlukan lebih banyak bantuan untuk memahami perhimpunan ini, pertimbangkan pseudokod yang melakukan perkara yang sama:

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

Selepas mentakrifkan kod penapis dalam struktur socket_filter, anda perlu menentukan sock_fprog yang mengandungi kod dan panjang penapis yang dikira. Struktur data ini diperlukan sebagai hujah untuk mengisytiharkan proses untuk dijalankan kemudian:

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

Hanya ada satu perkara yang perlu dilakukan dalam fungsi install_filter - muatkan program itu sendiri! Untuk melakukan ini, kami menggunakan prctl, mengambil PR_SET_SECCOMP sebagai pilihan untuk memasuki mod pengkomputeran selamat. Kemudian kami memberitahu mod untuk memuatkan penapis menggunakan SECCOMP_MODE_FILTER, yang terkandung dalam pembolehubah prog jenis sock_fprog:

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

Akhir sekali, kami boleh menggunakan fungsi install_filter kami, tetapi sebelum itu kami perlu menggunakan prctl untuk menetapkan PR_SET_NO_NEW_PRIVS untuk pelaksanaan semasa dan dengan itu mengelakkan situasi di mana proses kanak-kanak menerima lebih banyak keistimewaan daripada ibu bapa mereka. Dengan ini, kita boleh membuat panggilan prctl berikut dalam fungsi install_filter tanpa mempunyai hak root.

Sekarang kita boleh memanggil fungsi install_filter. Mari sekat semua panggilan sistem tulis yang berkaitan dengan seni bina X86-64 dan berikan kebenaran yang menyekat semua percubaan. Selepas memasang penapis, kami meneruskan pelaksanaan menggunakan hujah pertama:

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

Mari kita mulakan. Untuk menyusun atur cara kami, kami boleh menggunakan sama ada clang atau gcc, sama ada ia hanya menyusun fail main.c tanpa pilihan khas:

clang main.c -o filter-write

Seperti yang dinyatakan, kami telah menyekat semua penyertaan dalam program ini. Untuk menguji ini, anda memerlukan program yang mengeluarkan sesuatu - ls kelihatan seperti calon yang baik. Ini adalah cara dia biasanya berkelakuan:

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

Hebat! Begini rupa penggunaan program pembalut kami: Kami hanya lulus program yang ingin kami uji sebagai hujah pertama:

./filter-write "ls -la"

Apabila dilaksanakan, program ini menghasilkan output kosong sepenuhnya. Walau bagaimanapun, kita boleh menggunakan strace untuk melihat perkara yang berlaku:

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

Hasil kerja sangat dipendekkan, tetapi bahagian yang sepadan menunjukkan bahawa rekod disekat dengan ralat EPERM - yang sama yang kami konfigurasikan. Ini bermakna program tidak mengeluarkan apa-apa kerana ia tidak boleh mengakses panggilan sistem tulis:

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

Kini anda memahami cara Seccom BPF berfungsi dan mempunyai idea yang baik tentang perkara yang boleh anda lakukan dengannya. Tetapi tidakkah anda ingin mencapai perkara yang sama dengan eBPF dan bukannya cBPF untuk memanfaatkan kuasa penuhnya?

Apabila memikirkan tentang program eBPF, kebanyakan orang berfikir bahawa mereka hanya menulisnya dan memuatkannya dengan keistimewaan pentadbir. Walaupun kenyataan ini secara amnya benar, kernel melaksanakan satu set mekanisme untuk melindungi objek eBPF pada pelbagai peringkat. Mekanisme ini dipanggil perangkap BPF LSM.

Perangkap BPF LSM

Untuk menyediakan pemantauan bebas seni bina bagi peristiwa sistem, LSM melaksanakan konsep perangkap. Panggilan cangkuk secara teknikalnya serupa dengan panggilan sistem, tetapi adalah sistem bebas dan bersepadu dengan infrastruktur. LSM menyediakan konsep baharu di mana lapisan abstraksi boleh membantu mengelakkan masalah yang dihadapi semasa menangani panggilan sistem pada seni bina yang berbeza.

Pada masa penulisan, kernel mempunyai tujuh cangkuk yang dikaitkan dengan program BPF, dan SELinux ialah satu-satunya LSM terbina dalam yang melaksanakannya.

Kod sumber untuk perangkap terletak dalam pokok kernel dalam 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);

Setiap daripada mereka akan dipanggil pada peringkat pelaksanaan yang berbeza:

— security_bpf — melakukan semakan awal bagi panggilan sistem BPF yang dilaksanakan;

- security_bpf_map - menyemak apabila kernel mengembalikan deskriptor fail untuk peta;

- security_bpf_prog - menyemak apabila kernel mengembalikan deskriptor fail untuk program eBPF;

— security_bpf_map_alloc — menyemak sama ada medan keselamatan dalam peta BPF dimulakan;

- security_bpf_map_free - menyemak sama ada medan keselamatan dikosongkan di dalam peta BPF;

— security_bpf_prog_alloc — menyemak sama ada medan keselamatan dimulakan dalam program BPF;

- security_bpf_prog_free - menyemak sama ada medan keselamatan dikosongkan dalam program BPF.

Sekarang, melihat semua ini, kami faham: idea di sebalik pemintas BPF LSM ialah mereka boleh memberikan perlindungan kepada setiap objek eBPF, memastikan bahawa hanya mereka yang mempunyai keistimewaan yang sesuai boleh melakukan operasi pada kad dan program.

Ringkasan

Keselamatan bukanlah sesuatu yang anda boleh laksanakan dalam satu cara yang sesuai untuk semua yang anda ingin lindungi. Adalah penting untuk dapat melindungi sistem pada tahap yang berbeza dan dengan cara yang berbeza. Percaya atau tidak, cara terbaik untuk menjamin sistem adalah dengan mengatur tahap perlindungan yang berbeza dari kedudukan yang berbeza, supaya mengurangkan keselamatan satu tahap tidak membenarkan akses kepada keseluruhan sistem. Pembangun teras telah melakukan kerja yang hebat dalam memberikan kami satu set lapisan dan titik sentuh yang berbeza. Kami berharap kami telah memberi anda pemahaman yang baik tentang lapisan dan cara menggunakan program BPF untuk bekerja dengannya.

Mengenai pengarang

David Calavera ialah CTO di Netlify. Beliau bekerja dalam sokongan Docker dan menyumbang kepada pembangunan alat Runc, Go dan BCC, serta projek sumber terbuka yang lain. Terkenal dengan kerjanya pada projek Docker dan pembangunan ekosistem pemalam Docker. David sangat berminat dengan graf nyala dan sentiasa mencari untuk mengoptimumkan prestasi.

Lorenzo Fontana bekerja pada pasukan sumber terbuka di Sysdig, di mana beliau tertumpu terutamanya pada Falco, sebuah projek Cloud Native Computing Foundation yang menyediakan keselamatan masa jalan kontena dan pengesanan anomali melalui modul kernel dan eBPF. Beliau berminat dengan sistem teragih, rangkaian yang ditakrifkan perisian, kernel Linux dan analisis prestasi.

» Butiran lanjut tentang buku boleh didapati di laman web penerbit
» jadual kandungan
» Petikan

Untuk Khabrozhiteley diskaun 25% menggunakan kupon - Linux

Selepas pembayaran versi kertas buku itu, buku elektronik akan dihantar melalui e-mel.

Sumber: www.habr.com

Tambah komen