Halo warga Khabro! Mesin virtual BPF adalah salah satu komponen terpenting dari kernel Linux. Penggunaannya yang tepat akan memungkinkan insinyur sistem menemukan kesalahan dan memecahkan masalah yang paling rumit sekalipun. Anda akan belajar cara menulis program yang memantau dan memodifikasi perilaku kernel, cara mengimplementasikan kode dengan aman untuk memantau 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 memantau dan mengubah perilaku kernel Linux. - Menyuntikkan kode untuk memonitor kejadian kernel dengan aman tanpa harus mengkompilasi ulang kernel atau me-reboot sistem. — Gunakan contoh kode yang mudah digunakan dalam C, Go, atau Python. - Ambil kendali dengan memiliki siklus hidup program BPF.
Keamanan Kernel Linux, Fitur dan Seccompnya
BPF menyediakan cara ampuh untuk memperluas kernel tanpa mengorbankan stabilitas, keamanan, atau kecepatan. Oleh karena itu, pengembang kernel berpikir bahwa sebaiknya menggunakan keserbagunaannya untuk meningkatkan isolasi proses di Seccomp dengan menerapkan filter Seccomp yang didukung oleh program BPF, juga dikenal sebagai Seccomp BPF. Pada bab ini kami akan menjelaskan apa itu Seccomp dan cara penggunaannya. Kemudian Anda akan mempelajari cara menulis filter Seccomp menggunakan program BPF. Setelah itu, kita akan melihat hook BPF bawaan yang disertakan dalam kernel untuk modul keamanan Linux.
Modul Keamanan Linux (LSM) adalah kerangka kerja yang menyediakan serangkaian fungsi yang dapat digunakan untuk mengimplementasikan berbagai model keamanan dengan cara standar. LSM dapat digunakan langsung di pohon sumber kernel, seperti Apparmor, SELinux dan Tomoyo.
Mari kita mulai dengan membahas kemampuan Linux.
Kemampuan
Inti dari kemampuan Linux adalah Anda perlu memberikan izin proses yang tidak memiliki hak istimewa untuk melakukan tugas tertentu, tetapi tanpa menggunakan suid untuk tujuan itu, atau menjadikan proses tersebut hak istimewa, sehingga mengurangi kemungkinan serangan dan memungkinkan 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 memberikannya 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). Biasanya kami menjalankannya segera setelah kompilasi:
$ go build -o capabilities main.go
$ ./capabilitiesNamun, karena kami tidak memberikan hak akses root, kode ini akan menimbulkan kesalahan saat mengikat port:
2019/04/25 23:17:06 listen tcp :80: bind: permission denied
exit status 1capsh (shell manager) adalah alat yang menjalankan shell dengan serangkaian kemampuan tertentu.
Dalam hal ini, seperti yang telah disebutkan, alih-alih memberikan hak root penuh, Anda dapat mengaktifkan pengikatan port istimewa dengan menyediakan kemampuan cap_net_bind_service bersama dengan semua hal lain yang sudah ada dalam program. Untuk melakukan ini, kita dapat menyertakan program kita dalam huruf kapital:
# 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 tim ini.
- capsh - gunakan capsh sebagai cangkang.
- —caps='cap_net_bind_service+eip cap_setpcap,cap_setuid,cap_setgid+ep' - karena kita perlu mengubah pengguna (kita tidak ingin dijalankan sebagai root), kita akan menentukan cap_net_bind_service dan kemampuan untuk benar-benar mengubah ID pengguna dari root ke siapapun yaitu cap_setuid dan cap_setgid.
- —keep=1 — kami ingin mempertahankan kemampuan yang terinstal saat beralih dari akun root.
- —user=“nobody” — pengguna akhir yang menjalankan program ini bukanlah siapa-siapa.
- —addamb=cap_net_bind_service — mengatur penghapusan kemampuan terkait setelah beralih dari mode root.
- - -c "./capabilities" - jalankan saja programnya.
Kemampuan tertaut adalah jenis kemampuan khusus yang diwarisi oleh program anak ketika program saat ini mengeksekusinya menggunakan execve(). Hanya kapabilitas yang diperbolehkan untuk dikaitkan, atau dengan kata lain, sebagai kapabilitas lingkungan, yang dapat diwariskan.
Anda mungkin bertanya-tanya apa arti +eip setelah menentukan kemampuan dalam opsi --caps. Bendera ini digunakan untuk menentukan bahwa kemampuan:
-harus diaktifkan (p);
- tersedia untuk digunakan (e);
-dapat diwarisi oleh proses anak (i).
Karena kita ingin menggunakan cap_net_bind_service, kita perlu melakukannya dengan flag e. Kemudian kita akan memulai shell dengan perintah. Ini akan menjalankan kemampuan biner dan kita perlu menandainya dengan bendera i. Terakhir, kami ingin fitur tersebut diaktifkan (kami melakukan ini tanpa mengubah UID) dengan p. Sepertinya cap_net_bind_service+eip.
Anda dapat memeriksa hasilnya menggunakan ss. Mari kita persingkat outputnya sedikit agar sesuai dengan halaman, tetapi ini akan menampilkan port terkait dan ID pengguna selain 0, dalam hal ini 65:
# ss -tulpn -e -H | cut -d' ' -f17-
128 *:80 *:*
users:(("capabilities",pid=30040,fd=3)) uid:65534 ino:11311579 sk:2c v6only:0Dalam contoh ini kami menggunakan capsh, tetapi Anda dapat menulis shell menggunakan libcap. Untuk informasi lebih lanjut, lihat man 3 libcap.
Saat menulis program, seringkali pengembang tidak mengetahui terlebih dahulu semua fitur yang dibutuhkan program pada saat dijalankan; Selain itu, fitur-fitur ini mungkin berubah di versi baru.
Untuk lebih memahami kemampuan 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 1Kita dapat mencapai hal yang sama dengan menggunakan bpftrace dengan kprobe satu baris di 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 capabilitiesIni akan menampilkan sesuatu seperti berikut jika kemampuan 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 1Kolom kelima adalah kapabilitas yang dibutuhkan proses, dan karena keluaran ini mencakup peristiwa non-audit, kita melihat semua pemeriksaan non-audit dan terakhir kapabilitas yang diperlukan dengan tanda audit (yang terakhir pada keluaran) disetel ke 1. Kapabilitas salah satu yang kami minati adalah CAP_NET_BIND_SERVICE, ini didefinisikan sebagai konstanta dalam kode sumber kernel di file include/uapi/linux/ability.h dengan pengenal 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">Kemampuan sering kali diaktifkan saat runtime untuk container seperti runC atau Docker agar dapat berjalan dalam mode tanpa hak istimewa, namun kemampuan tersebut hanya diperbolehkan untuk kemampuan yang diperlukan untuk menjalankan sebagian besar aplikasi. Ketika suatu aplikasi memerlukan kemampuan tertentu, Docker dapat menyediakannya menggunakan --cap-add:
docker run -it --rm --cap-add=NET_ADMIN ubuntu ip link add dummy0 type dummyPerintah ini akan memberi wadah kemampuan CAP_NET_ADMIN, memungkinkannya mengonfigurasi tautan jaringan untuk menambahkan antarmuka dummy0.
Bagian selanjutnya menunjukkan cara menggunakan fitur seperti pemfilteran, namun menggunakan teknik berbeda yang memungkinkan kita mengimplementasikan filter kita sendiri secara terprogram.
Secomp
Seccomp adalah singkatan dari Secure Computing dan merupakan lapisan keamanan yang diterapkan di kernel Linux yang memungkinkan pengembang memfilter panggilan sistem tertentu. Meskipun Seccomp memiliki kemampuan yang sebanding dengan Linux, kemampuannya untuk mengelola panggilan sistem tertentu membuatnya jauh lebih fleksibel dibandingkan dengan Linux.
Fitur Seccomp dan Linux tidak eksklusif dan sering digunakan bersama untuk mendapatkan manfaat dari kedua pendekatan tersebut. Misalnya, Anda mungkin ingin memberi suatu proses kemampuan CAP_NET_ADMIN tetapi tidak mengizinkannya menerima koneksi soket, sehingga memblokir panggilan sistem terima dan terima4.
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 berbentuk program BPF yang dijalankan untuk setiap paket Seccomp yang diwakili oleh struktur seccomp_data. Struktur ini berisi arsitektur referensi, penunjuk ke instruksi prosesor pada saat panggilan sistem, dan maksimal enam argumen panggilan sistem, yang dinyatakan sebagai uint64.
Seperti inilah struktur seccomp_data dari kode sumber kernel di file linux/seccomp.h:
struct seccomp_data {
int nr;
__u32 arch;
__u64 instruction_pointer;
__u64 args[6];
};Seperti yang bisa Anda lihat dari struktur ini, kita dapat memfilter berdasarkan panggilan 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 dengan salah satu nilai yang dikembalikan (kode status).
- SECCOMP_RET_KILL_PROCESS - mematikan seluruh proses segera setelah memfilter panggilan sistem yang tidak dijalankan karena ini.
- SECCOMP_RET_KILL_THREAD - mengakhiri thread saat ini segera setelah memfilter panggilan sistem yang tidak dijalankan karena ini.
— SECCOMP_RET_KILL — alias untuk SECCOMP_RET_KILL_THREAD, tersisa untuk kompatibilitas ke belakang.
- SECCOMP_RET_TRAP - panggilan sistem dilarang, dan sinyal SIGSYS (Panggilan Sistem Buruk) dikirim ke tugas yang memanggilnya.
- SECCOMP_RET_ERRNO - Panggilan sistem tidak dijalankan, dan bagian dari nilai pengembalian filter SECCOMP_RET_DATA diteruskan ke ruang pengguna sebagai nilai errno. Bergantung pada penyebab kesalahan, nilai errno yang berbeda akan dikembalikan. Daftar nomor kesalahan disediakan di bagian selanjutnya.
- SECCOMP_RET_TRACE - Digunakan untuk memberi tahu pelacak ptrace menggunakan - PTRACE_O_TRACESECCOMP untuk mencegat ketika panggilan sistem dijalankan untuk melihat dan mengontrol proses itu. Jika pelacak tidak terhubung, kesalahan dikembalikan, errno diatur ke -ENOSYS, dan panggilan sistem tidak dijalankan.
- SECCOMP_RET_LOG - panggilan sistem diselesaikan dan dicatat.
- SECCOMP_RET_ALLOW - panggilan sistem diperbolehkan.
ptrace adalah panggilan sistem untuk mengimplementasikan mekanisme penelusuran dalam proses yang disebut tracee, dengan kemampuan untuk memantau dan mengontrol pelaksanaan proses. Program penelusuran dapat secara efektif mempengaruhi eksekusi dan memodifikasi register memori pelacak. 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 detik
Dari waktu ke waktu, saat bekerja dengan Seccomp, Anda akan menemukan berbagai kesalahan, yang diidentifikasi dengan nilai kembalian bertipe SECCOMP_RET_ERRNO. Untuk melaporkan kesalahan, panggilan sistem seccomp akan mengembalikan -1, bukan 0.
Kesalahan berikut mungkin terjadi:
- EACCESS - Penelepon tidak diperbolehkan melakukan panggilan sistem. Ini biasanya terjadi karena tidak memiliki hak istimewa CAP_SYS_ADMIN atau no_new_privs tidak disetel menggunakan prctl (kita akan membicarakannya nanti);
— EFAULT — argumen yang diteruskan (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;
-operasi mencakup 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 — memori tidak cukup untuk menjalankan program;
- EOPNOTSUPP - operasi menunjukkan bahwa dengan SECCOMP_GET_ACTION_AVAIL tindakan tersedia, tetapi kernel tidak mendukung pengembalian dalam argumen;
— ESRCH — masalah terjadi saat menyinkronkan aliran lain;
- ENOSYS - Tidak ada pelacak yang melekat pada tindakan SECCOMP_RET_TRACE.
prctl adalah panggilan sistem yang memungkinkan program ruang pengguna untuk memanipulasi (mengatur dan mendapatkan) aspek tertentu dari suatu proses, seperti byte endianness, nama thread, mode komputasi aman (Seccomp), hak istimewa, peristiwa Perf, dll.
Seccomp mungkin tampak seperti teknologi kotak pasir bagi Anda, tetapi sebenarnya tidak. Seccomp adalah utilitas yang memungkinkan pengguna mengembangkan mekanisme sandbox. Sekarang mari kita lihat bagaimana program interaksi pengguna dibuat menggunakan filter yang dipanggil langsung oleh panggilan sistem Seccomp.
Contoh Filter Seccomp BPF
Disini kami akan menunjukkan cara menggabungkan dua tindakan yang telah dibahas sebelumnya, yaitu:
— kami akan menulis program Seccomp BPF, yang akan digunakan sebagai filter dengan kode pengembalian yang berbeda tergantung pada keputusan yang dibuat;
— memuat filter menggunakan prctl.
Pertama, Anda memerlukan header dari perpustakaan 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 harus memastikan bahwa kernel dikompilasi dengan CONFIG_SECCOMP dan CONFIG_SECCOMP_FILTER diatur ke y. Pada mesin yang berfungsi, Anda dapat memeriksanya seperti ini:
cat /proc/config.gz| zcat | grep -i CONFIG_SECCOMP
Kode selanjutnya 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),
}; Instruksi diatur menggunakan makro BPF_STMT dan BPF_JUMP yang ditentukan dalam 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 dari 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, lompat pada offset 0 ke instruksi berikutnya, jika tidak, lompat pada offset 3 (dalam hal ini) untuk menimbulkan kesalahan karena lengkungan tidak cocok.
- BPF_STMT(BPF_LD + BPF_W + BPF_ABS (offsetof(struct seccomp_data, nr))) - Memuat dan diakumulasikan dari 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 sama, lanjutkan ke instruksi berikutnya dan nonaktifkan panggilan sistem, jika tidak, izinkan 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 akibatnya menghasilkan kesalahan SECCOMP_RET_ERRNO dengan nomor dari variabel err.
- BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW) - mengakhiri program dengan BPF_RET dan mengizinkan panggilan sistem dijalankan menggunakan SECCOMP_RET_ALLOW.
SECCOMP ADALAH CBPF
Anda mungkin bertanya-tanya mengapa daftar instruksi digunakan sebagai pengganti objek ELF yang dikompilasi atau program C yang dikompilasi JIT.Ada dua alasan untuk ini.
• Pertama, Seccomp menggunakan cBPF (BPF klasik) dan bukan eBPF, artinya: tidak memiliki register, tetapi hanya akumulator untuk menyimpan hasil perhitungan terakhir, seperti terlihat pada contoh.
• Kedua, Seccomp menerima pointer ke array instruksi BPF secara langsung dan tidak menerima yang lain. Makro yang kami gunakan hanya membantu menentukan instruksi ini dengan cara yang ramah pemrogram.
Jika Anda memerlukan 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 perhitungan panjang filter. Struktur data ini diperlukan sebagai argumen untuk mendeklarasikan proses yang akan dijalankan nantinya:
struct sock_fprog prog = {
.len = (unsigned short)(sizeof(filter) / sizeof(filter[0])),
.filter = filter,
};Hanya ada satu hal yang perlu dilakukan dalam fungsi install_filter - memuat program itu sendiri! Untuk melakukan ini, kami menggunakan prctl, menggunakan PR_SET_SECCOMP sebagai opsi untuk memasuki mode komputasi aman. Kemudian kita memberitahu mode untuk memuat filter menggunakan SECCOMP_MODE_FILTER, yang terdapat dalam variabel prog bertipe sock_fprog:
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
perror("prctl(PR_SET_SECCOMP)");
return 1;
}
return 0;
}Terakhir, 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 menerima lebih banyak hak istimewa daripada induknya. Dengan ini, kita dapat melakukan panggilan prctl berikut di fungsi install_filter tanpa memiliki hak root.
Sekarang kita dapat memanggil fungsi install_filter. Mari kita blokir semua panggilan sistem tulis yang terkait dengan arsitektur X86-64 dan cukup berikan izin yang memblokir semua upaya. Setelah memasang filter, kami melanjutkan 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. Untuk mengkompilasi program kita, kita dapat menggunakan clang atau gcc, yang mana pun itu hanya mengkompilasi file main.c tanpa opsi khusus:
clang main.c -o filter-writeSebagaimana disebutkan, kami telah memblokir semua entri dalam program ini. Untuk mengujinya, Anda memerlukan program yang menghasilkan sesuatu - ls sepertinya kandidat yang bagus. 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
Luar biasa! Berikut tampilan penggunaan program wrapper kami: Kami cukup meneruskan program yang ingin kami uji sebagai argumen pertama:
./filter-write "ls -la"Ketika 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 terkait menunjukkan bahwa catatan diblokir dengan kesalahan EPERM - sama dengan yang kami konfigurasikan. Ini berarti 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 gagasan bagus tentang apa yang dapat Anda lakukan dengannya. Namun tidakkah Anda ingin mencapai hal yang sama dengan eBPF, bukan cBPF, untuk memanfaatkan kekuatan penuhnya?
Ketika memikirkan program eBPF, kebanyakan orang berpikir bahwa mereka hanya menulisnya 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.
Perangkap BPF LSM
Untuk menyediakan pemantauan peristiwa sistem yang independen terhadap arsitektur, LSM menerapkan konsep jebakan. Panggilan hook secara teknis mirip dengan panggilan sistem, namun merupakan sistem independen dan terintegrasi dengan infrastruktur. LSM memberikan konsep baru dimana lapisan abstraksi dapat membantu menghindari masalah yang dihadapi ketika menangani panggilan sistem pada arsitektur yang berbeda.
Pada saat penulisan, kernel memiliki tujuh hook yang terkait dengan program BPF, dan SELinux adalah satu-satunya LSM bawaan yang mengimplementasikannya.
Kode sumber untuk jebakan terletak di pohon kernel dalam 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 telah diinisialisasi;
- security_bpf_map_free - memeriksa apakah bidang keamanan di dalam peta BPF dikosongkan;
— 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: gagasan di balik pencegat LSM BPF adalah bahwa mereka dapat memberikan perlindungan pada setiap objek eBPF, memastikan bahwa hanya mereka yang memiliki hak istimewa yang sesuai yang dapat melakukan operasi pada kartu dan program.
Ringkasan
Keamanan bukanlah sesuatu yang dapat Anda terapkan dengan cara yang universal untuk semua hal yang ingin Anda lindungi. Penting untuk dapat melindungi sistem pada tingkat yang berbeda dan dengan cara yang berbeda. Percaya atau tidak, cara terbaik untuk mengamankan suatu sistem adalah dengan mengatur tingkat perlindungan yang berbeda dari posisi yang berbeda, sehingga mengurangi keamanan pada satu tingkat tidak memungkinkan akses ke seluruh sistem. Pengembang inti telah melakukan pekerjaan luar biasa dalam memberi kami serangkaian lapisan dan titik kontak yang berbeda. Kami harap kami dapat memberi Anda pemahaman yang baik tentang apa itu lapisan dan bagaimana menggunakan program BPF untuk bekerja dengannya.
Tentang Penulis
David Calavera adalah CTO di Netlify. Dia bekerja di dukungan Docker dan 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 tertarik dengan grafik api dan selalu berupaya mengoptimalkan kinerja.
Lorenzo Fontana bekerja di tim open source di Sysdig, fokus utamanya adalah pada Falco, sebuah proyek Cloud Native Computing Foundation yang menyediakan keamanan runtime container dan deteksi anomali melalui modul kernel dan eBPF. Dia tertarik pada sistem terdistribusi, jaringan yang ditentukan perangkat lunak, kernel Linux, dan analisis kinerja.
»Detail lebih lanjut tentang buku ini dapat ditemukan di
»
»
Untuk Khabrozhiteley diskon 25% menggunakan kupon - Linux
Setelah pembayaran buku versi kertas, buku elektronik akan dikirim melalui email.
Sumber: www.habr.com
