BPF untuk Buku Pemantauan Linux

gambarHalo para Penduduk! Mesin virtual BPF adalah salah satu komponen terpenting dari kernel Linux. Aplikasi cerdasnya akan memungkinkan para insinyur sistem menemukan kegagalan dan memecahkan masalah yang paling kompleks sekalipun. Anda akan belajar bagaimana menulis program yang memantau dan memodifikasi perilaku kernel, dapat dengan aman memasukkan kode untuk mengamati kejadian di kernel, dan banyak lagi. David Calavera dan Lorenzo Fontana akan membantu Anda membuka kekuatan BPF. Perluas pengetahuan Anda tentang pengoptimalan kinerja, jaringan, keamanan. - Gunakan BPF untuk melacak dan mengubah perilaku kernel Linux. - Masukkan kode untuk memantau kejadian di kernel dengan aman - tanpa perlu mengkompilasi ulang kernel atau mem-boot ulang sistem. - Gunakan contoh kode praktis di C, Go, atau Python. - Kelola situasi dengan memiliki siklus hidup program BPF.





Keamanan kernel Linux, fitur dan Seccomp



BPF menyediakan cara yang ampuh untuk memperluas kernel tanpa mengorbankan stabilitas, keamanan, atau kecepatan. Untuk alasan ini, pengembang kernel berpikir bahwa akan menjadi ide yang baik untuk memanfaatkan keserbagunaannya untuk meningkatkan isolasi proses di Seccomp dengan menerapkan filter Seccomp yang didukung oleh program BPF alias Seccomp BPF. Dalam bab ini, kami akan menjelaskan apa itu Seccomp dan bagaimana penerapannya. Kemudian Anda akan belajar bagaimana menulis filter Seccomp menggunakan program BPF. Setelah itu, mari kita lihat kait BPF bawaan yang dimiliki kernel untuk modul keamanan Linux.



Modul Keamanan Linux (LSM) adalah platform yang menyediakan serangkaian fungsi yang dapat digunakan untuk menstandarkan implementasi berbagai model keamanan. LSM dapat digunakan langsung di pohon sumber kernel seperti Apparmor, SELinux, dan Tomoyo.



Mari kita mulai dengan membahas fitur-fitur Linux.



Kemampuan



Inti dari kemampuan Linux adalah bahwa Anda perlu memberikan izin proses yang tidak memiliki hak istimewa untuk melakukan tugas tertentu, tetapi tanpa suid untuk tujuan itu, atau sebaliknya membuat proses tersebut diistimewakan, mengurangi kemungkinan serangan dan mengizinkan proses untuk melakukan tugas tertentu. Misalnya, jika aplikasi Anda perlu membuka port dengan hak istimewa, katakanlah 80, alih-alih menjalankan proses sebagai root, Anda cukup memberinya kemampuan 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 melayani server HTTP pada port 80 (ini adalah port istimewa). Kami biasanya menjalankannya tepat setelah kompilasi:



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


Namun, karena kami tidak memberikan hak akses root, kode ini akan memunculkan kesalahan saat mengikat port:



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


capsh (alat kendali shell) adalah alat yang meluncurkan shell dengan sekumpulan kemampuan tertentu.


Dalam kasus ini, seperti yang telah disebutkan, alih-alih memberikan hak root penuh, Anda dapat mengaktifkan pengikatan port dengan hak istimewa dengan mengaktifkan cap_net_bind_service bersama dengan semua hal lain yang sudah ada dalam program. Untuk melakukan ini, kita dapat membungkus program kita 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 pahami sedikit tentang perintah ini.



  • capsh - gunakan capsh sebagai shell.
  • --caps = 'cap_net_bind_service + eip cap_setpcap, cap_setuid, cap_setgid + ep' - karena kami perlu mengubah pengguna (kami tidak ingin menjalankan sebagai root), kami akan menentukan cap_net_bind_service dan kemampuan untuk benar-benar mengubah ID pengguna dari root ke siapa pun, yaitu cap_setuid dan cap_setgid ...
  • --keep=1 — , root.
  • --user=«nobody» — , , nobody.
  • --addamb=cap_net_bind_service — root.
  • — -c "./capabilities" — .


— , , execve(). , , , , .


Anda mungkin bertanya-tanya apa arti + eip setelah menentukan kapabilitas dalam opsi --caps. Bendera ini digunakan untuk menetapkan bahwa fitur:



-harus diaktifkan (p);



-tersedia untuk aplikasi (e);



-dapat diwarisi oleh proses anak (i).



Karena kita ingin menggunakan cap_net_bind_service, kita perlu melakukannya dengan flag e. Lalu kita mulai shell in command. Ini akan meluncurkan kapabilitas biner dan kita perlu menandainya dengan bendera i. Akhirnya, kami ingin fitur tersebut diaktifkan (kami melakukan ini tanpa mengubah UID) dengan p. Sepertinya cap_net_bind_service + eip.



Anda dapat memeriksa hasilnya dengan ss. Kecilkan output sedikit agar sesuai dengan halaman, tetapi itu akan menunjukkan port terkait dan ID pengguna selain 0, dalam hal ini 65.534:



# 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 dapat menulis shell menggunakan libcap. Lihat man 3 libcap untuk informasi lebih lanjut.



Saat menulis program, pengembang sering kali tidak mengetahui terlebih dahulu semua kemampuan yang dibutuhkan oleh program saat runtime; selain itu, fitur ini dapat berubah di versi baru.



Untuk lebih memahami kapabilitas program kita, kita dapat menggunakan alat berkemampuan BCC, yang menyetel 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 dapat mencapai hal yang sama dengan menggunakan bpftrace dengan satu baris kprobe di fungsi kernel cap_capable:



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


Ini akan menghasilkan sesuatu seperti berikut jika kapabilitas program kita diaktifkan setelah 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


Kolom kelima adalah kapabilitas yang dibutuhkan proses, dan karena output ini mencakup event non-audit, kita melihat semua pemeriksaan non-audit dan akhirnya kapabilitas yang diperlukan dengan flag audit (yang terakhir dalam output) ditetapkan ke 1. Kapabilitas. yang kami minati adalah CAP_NET_BIND_SERVICE, ini didefinisikan sebagai konstanta dalam kode sumber kernel di file include / uapi / linux / ability.h dengan ID 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">


Fitur sering digunakan saat runtime untuk container seperti runC atau Docker agar berjalan dalam mode tidak memiliki hak istimewa, tetapi hanya diizinkan fitur yang diperlukan untuk menjalankan sebagian besar aplikasi. Ketika sebuah aplikasi membutuhkan kapabilitas tertentu, Docker dapat menyediakannya dengan --cap-add:



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


Perintah ini akan memberikan wadah dengan kapabilitas CAP_NET_ADMIN, yang akan memungkinkannya mengkonfigurasi tautan jaringan untuk menambahkan antarmuka dummy0.



Bagian selanjutnya menunjukkan cara menggunakan fitur seperti pemfilteran, tetapi dengan metode berbeda yang memungkinkan kita menerapkan filter kita sendiri secara terprogram.



Seccomp



Seccomp adalah singkatan dari Secure Computing, itu adalah lapisan keamanan yang diterapkan di kernel Linux yang memungkinkan pengembang untuk menyaring panggilan sistem tertentu. Meskipun Seccomp sebanding dengan kemampuan Linux, kemampuannya untuk menangani panggilan sistem tertentu membuatnya jauh lebih fleksibel daripada sebelumnya.



Kemampuan Seccomp dan Linux tidak eksklusif satu sama lain, dan sering kali digunakan bersama untuk memanfaatkan kedua pendekatan tersebut. Misalnya, Anda mungkin ingin memberikan kemampuan CAP_NET_ADMIN pada suatu proses, tetapi tidak mengizinkannya menerima koneksi soket dengan memblokir panggilan sistem accept dan accept4.



Metode pemfilteran Seccomp didasarkan pada filter BPF yang beroperasi dalam mode SECCOMP_MODE_FILTER, dan pemfilteran panggilan sistem dilakukan dengan cara yang sama seperti untuk paket.



Filter Seccomp dimuat menggunakan prctl melalui operasi PR_SET_SECCOMP. Filter ini dalam bentuk program BPF yang berjalan untuk setiap paket Seccomp yang diwakili oleh struktur seccomp_data. Struktur ini berisi arsitektur referensi, penunjuk ke instruksi prosesor selama panggilan sistem, dan maksimal enam argumen panggilan sistem, yang dinyatakan sebagai uint64.



Beginilah tampilan struktur seccomp_data dari sumber kernel di file linux / seccomp.h:



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


Seperti yang Anda lihat dari struktur ini, kita dapat memfilter berdasarkan pemanggilan sistem, argumennya, atau kombinasi keduanya.



Setelah menerima setiap paket Seccomp, filter harus melakukan pemrosesan untuk membuat keputusan akhir dan memberi tahu kernel apa yang harus dilakukan selanjutnya. Keputusan akhir dinyatakan dalam salah satu nilai yang dikembalikan (kode status).



- SECCOMP_RET_KILL_PROCESS - penghentian seluruh proses segera setelah memfilter panggilan sistem yang tidak dijalankan karena ini



- SECCOMP_RET_KILL_THREAD - penghentian utas saat ini segera setelah memfilter panggilan sistem, yang karenanya tidak dijalankan.



- SECCOMP_RET_KILL - alias untuk SECCOMP_RET_KILL_THREAD, tersisa untuk kompatibilitas ke belakang.



- SECCOMP_RET_TRAP - Panggilan sistem dinonaktifkan dan sinyal SIGSYS (Panggilan Sistem Buruk) dikirim ke tugas panggilan.



- SECCOMP_RET_ERRNO - Panggilan sistem tidak dijalankan, dan bagian dari nilai kembalian filter SECCOMP_RET_DATA diteruskan ke ruang pengguna sebagai errno. Nilai errno yang berbeda dikembalikan tergantung pada penyebab kesalahan. Nomor kesalahan terdaftar di bagian selanjutnya.



- SECCOMP_RET_TRACE - Digunakan untuk memberi tahu ptrace dengan - PTRACE_O_TRACESECCOMP untuk mencegat saat panggilan sistem dibuat untuk melihat dan mengontrol proses ini. Jika pelacak tidak terhubung, kesalahan dikembalikan, errno diatur ke -ENOSYS, dan panggilan sistem tidak dijalankan.



- SECCOMP_RET_LOG - Panggilan sistem diperbolehkan dan dicatat.



- SECCOMP_RET_ALLOW - panggilan sistem diperbolehkan.



ptrace adalah panggilan sistem untuk mengimplementasikan mekanisme pelacakan dalam proses yang disebut jejak, dengan kemampuan untuk memantau dan mengontrol eksekusi proses. Program jejak dapat secara efektif mempengaruhi eksekusi dan mengubah register memori jejak. Dalam konteks Seccomp, ptrace digunakan ketika dipicu oleh kode status SECCOMP_RET_TRACE, sehingga pelacak dapat mencegah panggilan sistem dijalankan dan mengimplementasikan logikanya sendiri.


Kesalahan Seccomp



Dari waktu ke waktu, saat bekerja dengan Seccomp, Anda akan menemukan berbagai kesalahan, yang diidentifikasi dengan nilai kembalian tipe SECCOMP_RET_ERRNO. Untuk melaporkan kesalahan, panggilan sistem seccomp akan mengembalikan -1 bukan 0.



Kesalahan berikut mungkin terjadi:



- EACCESS - Penelepon tidak diizinkan untuk melakukan panggilan sistem. Ini biasanya terjadi karena ia tidak memiliki hak istimewa CAP_SYS_ADMIN atau no_new_privs tidak disetel dengan prctl (lebih lanjut tentang itu nanti);



- EFAULT - argumen yang diberikan (argumen dalam struktur seccomp_data) tidak memiliki alamat yang valid;



- EINVAL - ada empat alasan di sini: -



operasi yang diminta tidak diketahui atau tidak didukung oleh kernel dalam konfigurasi saat ini;



-bendera yang ditentukan tidak valid untuk operasi yang diminta;



-operation termasuk BPF_ABS, tetapi ada masalah dengan offset yang ditentukan, yang mungkin melebihi ukuran struktur seccomp_data;



- jumlah instruksi yang diteruskan ke filter melebihi jumlah maksimum;



- ENOMEM - tidak cukup memori untuk menjalankan program;



- EOPNOTSUPP - operasi menunjukkan bahwa tindakan telah tersedia dengan SECCOMP_GET_ACTION_AVAIL, tetapi kernel tidak mendukung pengembalian dalam argumen;



- ESRCH - ada masalah saat menyinkronkan aliran lain;



- ENOSYS - tidak ada pelacak yang dipasang ke tindakan SECCOMP_RET_TRACE.



prctl adalah panggilan sistem yang memungkinkan program ruang pengguna untuk memanipulasi (mengatur dan mendapatkan) aspek tertentu dari suatu proses, seperti urutan byte, nama utas, mode komputasi aman (Seccomp), hak istimewa, acara Perf, dan sebagainya.


Seccomp mungkin tampak seperti teknologi kotak pasir bagi Anda, tetapi sebenarnya tidak. Seccomp adalah utilitas yang memungkinkan pengguna mengembangkan mekanisme kotak pasir. Sekarang mari kita lihat cara membuat program interaksi khusus menggunakan filter yang dipanggil langsung oleh panggilan sistem Seccomp.



Contoh filter BPF Seccomp



Di sini kami akan menunjukkan bagaimana menggabungkan dua tindakan yang dipertimbangkan sebelumnya, yaitu:



- menulis program Seccomp BPF, yang akan digunakan sebagai filter dengan kode pengembalian yang berbeda tergantung pada keputusan yang dibuat;



- muat filter menggunakan prctl.



Pertama, Anda memerlukan header dari pustaka standar 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 mencoba contoh ini, kita perlu memastikan bahwa kernel dikompilasi dengan CONFIG_SECCOMP dan CONFIG_SECCOMP_FILTER disetel ke y. Di mesin produksi, Anda dapat mengujinya seperti ini:



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



Sisa kode adalah fungsi install_filter dua bagian. Bagian pertama berisi daftar instruksi pemfilteran 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),
  };


Petunjuk disetel menggunakan makro BPF_STMT dan BPF_JUMP yang ditentukan di file linux / filter.h.

Mari kita lihat instruksinya.



- BPF_STMT (BPF_LD + BPF_W + BPF_ABS (offsetof (struct seccomp_data, arch))) - sistem memuat dan terakumulasi dengan BPF_LD dalam bentuk kata BPF_W, data paket terletak pada offset tetap BPF_ABS.



- BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, arch, 0, 3) - memeriksa menggunakan BPF_JEQ apakah nilai arsitektur dalam konstanta akumulator BPF_K sama dengan arch. Jika demikian, ia melompat pada offset 0 ke instruksi berikutnya; jika tidak, ia melompat pada offset 3 (dalam kasus ini) untuk memunculkan kesalahan, karena arch tidak cocok.



- BPF_STMT (BPF_LD + BPF_W + BPF_ABS (offsetof (struct seccomp_data, nr))) - mengunduh dan terakumulasi dengan BPF_LD dalam bentuk kata BPF_W, yang merupakan nomor panggilan sistem yang terdapat dalam offset tetap BPF_ABS.



- BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, nr, 0, 1) - membandingkan nomor panggilan sistem dengan nilai variabel nr. Jika mereka sama, itu melanjutkan ke pernyataan berikutnya dan melarang panggilan sistem; jika tidak, aktifkan panggilan sistem dengan SECCOMP_RET_ALLOW.



- BPF_STMT (BPF_RET + BPF_K, SECCOMP_RET_ERRNO | (error & SECCOMP_RET_DATA)) - menghentikan program dengan BPF_RET dan, sebagai hasilnya, mengeluarkan error SECCOMP_RET_ERRNO dengan nomor dari variabel err.



- BPF_STMT (BPF_RET + BPF_K, SECCOMP_RET_ALLOW) - mengakhiri program dengan BPF_RET dan memungkinkan eksekusi panggilan sistem menggunakan SECCOMP_RET_ALLOW.



SECCOMP IS CBPF Anda

mungkin bertanya-tanya mengapa daftar instruksi digunakan sebagai ganti objek ELF yang dikompilasi atau program C yang dikompilasi JIT.



Ada dua alasan untuk ini.



• Pertama, Seccomp menggunakan cBPF (BPF klasik), bukan eBPF, yang berarti tidak memiliki register, tetapi hanya akumulator untuk menyimpan hasil komputasi terakhir, seperti yang Anda lihat pada contoh.



• Kedua, Seccomp mengambil pointer ke larik instruksi BPF secara langsung dan tidak ada yang lain. Makro yang kami gunakan hanya membantu untuk menentukan instruksi ini dalam bentuk yang nyaman bagi pemrogram.


Jika Anda membutuhkan bantuan lebih lanjut untuk memahami perakitan ini, pertimbangkan kodesemu yang melakukan hal yang sama:



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


Setelah menentukan kode filter dalam struktur socket_filter, Anda perlu mendefinisikan sock_fprog yang berisi kode dan panjang filter yang dihitung. Struktur data ini diperlukan sebagai argumen untuk menyatakan pekerjaan proses di masa depan:



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


Hanya ada satu hal yang harus dilakukan dalam fungsi install_filter - unduh program itu sendiri! Untuk melakukan ini, kami menggunakan prctl, menggunakan PR_SET_SECCOMP sebagai opsi untuk memasuki mode komputasi aman. Kemudian kami memberi tahu mode untuk memuat filter menggunakan SECCOMP_MODE_FILTER, yang terdapat dalam variabel prog jenis sock_fprog:



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


Akhirnya, kita dapat menggunakan fungsi install_filter, tetapi sebelumnya kita perlu menggunakan prctl untuk menyetel PR_SET_NO_NEW_PRIVS untuk eksekusi saat ini dan dengan demikian menghindari situasi di mana proses anak mendapatkan lebih banyak hak istimewa daripada induknya. Namun, kita dapat melakukan panggilan berikut ke prctl dalam fungsi install_filter tanpa memiliki hak root.



Sekarang kita dapat memanggil fungsi install_filter. Mari kita memblokir semua panggilan sistem tulis yang terkait dengan arsitektur X86-64, dan berikan izin saja, yang memblokir semua upaya. Setelah menginstal filter, lanjutkan eksekusi menggunakan argumen 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 mulai. Kita dapat menggunakan clang atau gcc untuk mengkompilasi program kita, bagaimanapun juga, itu hanya mengkompilasi file main.c tanpa opsi khusus:



clang main.c -o filter-write


Seperti disebutkan, kami telah memblokir semua entri dalam program ini. Untuk mengujinya, Anda memerlukan program yang mengeluarkan sesuatu - sepertinya adalah kandidat yang baik. Beginilah biasanya dia berperilaku:



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


Sempurna! Seperti inilah tampilan program shell kami: kami hanya meneruskan program yang ingin kami uji sebagai argumen pertama:



./filter-write "ls -la"


Saat dijalankan, program ini menghasilkan keluaran yang benar-benar kosong. Namun, kita dapat menggunakan strace untuk melihat apa yang terjadi:



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


Hasil pekerjaan sangat dipersingkat, tetapi bagian yang sesuai menunjukkan bahwa record diblokir dengan kesalahan EPERM - yang sama dengan yang kita konfigurasikan. Ini berarti bahwa program tidak mengeluarkan apa pun karena tidak dapat 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)


Sekarang Anda memahami cara kerja Seccomp BPF dan memiliki ide bagus tentang apa yang dapat dilakukan dengannya. Tapi bukankah Anda ingin melakukan hal yang sama dengan eBPF daripada cBPF untuk menggunakan kekuatan penuhnya?



Ketika memikirkan tentang program eBPF, kebanyakan orang berpikir bahwa mereka hanya menulis dan memuatnya dengan hak administrator. Meskipun pernyataan ini secara umum benar, kernel mengimplementasikan serangkaian mekanisme untuk melindungi objek eBPF di berbagai tingkatan. Mekanisme ini disebut perangkap BPF LSM.



Menjebak BPF LSM



Untuk menyediakan pemantauan peristiwa sistem yang tidak bergantung arsitektur, LSM menerapkan konsep jebakan. Panggilan hook secara teknis mirip dengan panggilan sistem, tetapi sistem independen dan terintegrasi dengan infrastruktur. LSM memberikan konsep baru di mana lapisan abstraksi dapat membantu menghindari masalah yang muncul saat menangani panggilan sistem pada arsitektur yang berbeda.



Pada saat penulisan ini, kernel memiliki tujuh kait yang terkait dengan program BPF, dan SELinux adalah satu-satunya LSM bawaan yang mengimplementasikannya.



Kode sumber untuk hook terletak di pohon kernel di file 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);


Masing-masing akan dipanggil pada tahapan eksekusi yang berbeda:



- security_bpf - melakukan pemeriksaan awal terhadap panggilan sistem BPF yang dijalankan;



- security_bpf_map - memeriksa kapan kernel mengembalikan deskriptor file untuk peta;



- security_bpf_prog - Memeriksa kapan kernel mengembalikan deskriptor file untuk program eBPF;



- security_bpf_map_alloc - memeriksa apakah bidang keamanan di dalam peta BPF diinisialisasi;



- security_bpf_map_free - memeriksa apakah bidang keamanan di dalam peta BPF telah dihapus;



- security_bpf_prog_alloc - memeriksa apakah bidang keamanan diinisialisasi di dalam program BPF;



- security_bpf_prog_free - memeriksa apakah bidang keamanan di dalam program BPF dikosongkan.



Sekarang, melihat semua ini, kami memahami bahwa ide di balik interseptor LSM BPF adalah bahwa mereka dapat memberikan perlindungan untuk setiap objek eBPF, memastikan bahwa hanya mereka yang memiliki hak istimewa yang dapat melakukan operasi pada peta dan program.



Ringkasan



Keamanan bukanlah sesuatu yang dapat Anda terapkan dengan cara satu ukuran untuk semua untuk apa pun yang ingin Anda lindungi. Penting untuk dapat melindungi sistem di berbagai tingkat dan dengan cara yang berbeda. Percaya atau tidak, cara terbaik untuk mengamankan sistem adalah dengan mengatur tingkat perlindungan yang berbeda dari posisi yang berbeda sehingga degradasi keamanan satu tingkat mencegah akses ke seluruh sistem. Pengembang kernel telah melakukan pekerjaan yang hebat dengan menyediakan kami serangkaian lapisan dan titik kontak yang berbeda. Kami berharap kami telah memberi Anda pemahaman yang baik tentang apa itu layer dan bagaimana menggunakan program BPF untuk bekerja dengannya.



Tentang Penulis



David Calavera adalah CTO di Netlify. Dia telah bekerja untuk Dukungan Docker dan telah berkontribusi pada pengembangan alat Runc, Go dan BCC, serta proyek sumber terbuka lainnya. Dikenal karena karyanya pada proyek Docker dan pengembangan ekosistem plugin Docker. David sangat menyukai grafik api dan selalu berusaha untuk mengoptimalkan kinerja.



Lorenzo Fontana adalah bagian dari tim pengembangan open source di Sysdig, di mana dia terutama terlibat dalam Falco, sebuah proyek Cloud Native Computing Foundation yang menyediakan keamanan runtime container dan deteksi anomali melalui modul kernel dan eBPF. Dia sangat tertarik dengan sistem terdistribusi, jaringan yang ditentukan perangkat lunak, kernel Linux, dan analisis kinerja.



»Rincian lebih lanjut tentang buku dapat ditemukan di situs web rumah penerbitan

» Daftar Isi

» Kutipan



Untuk Habitants diskon 25% untuk kupon - Linux



Setelah pembayaran untuk versi kertas buku, sebuah e-book dikirim melalui email.



All Articles