AizuDemy

Tutorial BPF LSM Go: Blokir Eksekusi Process & Kernel Module di VPS

Tutorial BPF LSM Go: Blokir Eksekusi Process & Kernel Module di VPS
IKLAN
IDCloudHost
๐ŸŽง
Dengarkan Artikel Ini
Suara AI Otomatis โ€ข 6 mnt baca baca
โšก TL;DR

Poin Kunci Artikel Ini:

  • Bayangkan skenario ini: server VPS tempat kamu menaruh aplikasi production mendadak kebobolan.
  • Penyerang berhasil menemukan celah Remote Code Execution (RCE) di web app.
  • Tapi mari jujur: berapa banyak dari kita yang langsung pusing saat membuka konfigurasi SELinux?
๐Ÿ“‹ Daftar Isi Materi Tutup โ–ด

Bayangkan skenario ini: server VPS tempat kamu menaruh aplikasi production mendadak kebobolan. Penyerang berhasil menemukan celah Remote Code Execution (RCE) di web app. Dalam hitungan detik, mereka mencoba menjalankan reverse shell pakai nc (netcat) atau menancapkan rootkit berupa kernel module (.ko).

Sebagai sysadmin atau DevOps, reflek pertama biasanya melihat AppArmor atau SELinux. Tapi mari jujur: berapa banyak dari kita yang langsung pusing saat membuka konfigurasi SELinux? Aturan kaku, sintaks rumit, dan sering kali malah bikin aplikasi mati total sampai akhirnya kita pasrah ketik setenforce 0. Lebih parah lagi, LSM tradisional berbasis path file seperti AppArmor bisa dikecoh jika attacker pintar memanfaatkan symlink atau pemindahan binary.

Untungnya, sejak Linux kernel 5.7, ada cara jauh lebih modern dan fleksibel: BPF LSM (Linux Security Modules). Dengan teknologi ini, kita bisa membuat aturan keamanan kernel sendiri memakai kombinasi bahasa C dan Go. Tidak perlu kompilasi ulang kernel, tidak perlu pusing aturan SELinux, dan performanya sangat cepat.

Kenapa AppArmor dan SELinux Sering Bikin Frustrasi?

Sebelum masuk ke kodingan, kita bahas sebentar kenapa cara lama sering bikin pusing.

LSM tradisional bekerja ibarat satpam yang cuma berpatokan pada papan nama di pintu depan.

  • AppArmor: Memeriksa keamanan berdasarkan path atau lokasi file. Kalau penyerang memindahkan binary nc ke /tmp/kebal atau pakai symlink, AppArmor bisa terkecoh.
  • SELinux: Sangat ampuh karena berbasis label (type enforcement). Tapi kurva belajarnya luar biasa curam. Konfigurasinya begitu kompleks sampai-sampai joke paling populer di dunia Linux sysadmin adalah "cara terbaik install SELinux adalah mematikannya".
  • BPF LSM: Pendekatan baru. Kita memasang snippet kode eBPF langsung di titik-titik krusial (security hooks) internal kernel. Keputusan untuk mengizinkan (kembalikan nilai 0) atau memblokir (kembalikan -EPERM) diambil secara dinamis oleh logika program C/Go yang kita tulis sendiri.

BPF LSM menggabungkan fleksibilitas pemrograman eBPF dengan kontrol penuh LSM hook di kernel space.

Arsitektur Solusi: C untuk Kernel Space, Go untuk User Space

Sistem penangkal yang akan kita bangun ini terdiri dari dua komponen utama:

  1. Program C (Kernel Space): Kode kecil eBPF yang berjalan langsung di dalam kernel untuk mencegat syscall.
  2. Daemon Go (User Space): Program Go yang memuat bytecode eBPF ke kernel, menempelkan hook, dan menjaga lifecycle daemon menggunakan library cilium/ebpf.

1. Menulis Program eBPF LSM (C Kernel Space)

Buat file bernama bpf_lsm.c. Program ini menangkap dua security hook: bprm_check_security (diakses saat execve dipanggil) dan kernel_read_file (diakses saat module kernel dimuat).

#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>

char LICENSE[] SEC("license") = "GPL";

// Hook 1: Blokir eksekusi biner terlarang (contoh: nc / netcat)
SEC("lsm/bprm_check_security")
int BPF_PROG(restrict_exec, struct linux_binprm *bprm) {
    char comm[16];
    bpf_get_current_comm(&comm, sizeof(comm));

    // Periksa apakah nama proses diawali "nc"
    if (comm[0] == 'n' && comm[1] == 'c') {
        bpf_printk("BPF LSM: Eksekusi diblokir untuk process: %s\n", comm);
        return -1; // Return -EPERM untuk menolak eksekusi
    }

    return 0; // Izinkan eksekusi
}

// Hook 2: Blokir pemuatan Kernel Module (insmod/modprobe)
SEC("lsm/kernel_read_file")
int BPF_PROG(restrict_module_load, struct file *file, enum kernel_read_file_id id) {
    if (id == READING_MODULE) {
        bpf_printk("BPF LSM: Percobaan muat kernel module diblokir!\n");
        return -1; // Return -EPERM
    }

    return 0;
}

Kode C di atas memanfaatkan bpf_get_current_comm untuk mengambil nama binary yang dipanggil. Jika namanya diawali huruf 'n' dan 'c', program mengembalikan nilai -1 (EPERM), menolak eksekusi syscall tersebut.

2. Auto-generate Go Code dengan bpf2go

Daripada mengompilasi file C secara manual dan menulis parser ELF raksasa, kita gunakan utilitas bpf2go buatan Cilium. Tools ini mengompilasi C menjadi bytecode eBPF sekaligus membuat binding Go otomatis.

go install github.com/cilium/ebpf/cmd/bpf2go@latest
go run github.com/cilium/ebpf/cmd/bpf2go bpf bpf_lsm.c -- -I/usr/include/bpf

Perintah ini me-generate file Go seperti bpf_bpfel.go yang siap di-import langsung oleh aplikasi Go kita.

3. Menulis Loader Daemon dengan Go (User Space)

Sekarang buat file main.go. Tugas aplikasi ini adalah menembakkan bytecode eBPF tadi ke dalam kernel saat startup dan melepasnya dengan bersih saat aplikasi ditutup.

package main

import (
	"log"
	"os"
	"os/signal"
	"syscall"

	"github.com/cilium/ebpf/link"
	"github.com/cilium/ebpf/rlimit"
)

func main() {
	// Hapus batasan memory lock untuk eBPF
	if err := rlimit.RemoveMemlock(); err != nil {
		log.Fatalf("Gagal menghapus memlock limit: %v", err)
	}

	// Load eBPF objects yang di-generate oleh bpf2go
	objs := bpfObjects{}
	if err := loadBpfObjects(&objs, nil); err != nil {
		log.Fatalf("Gagal memuat BPF objects: %v", err)
	}
	defer objs.Close()

	// Attach Hook Execve
	lsmExec, err := link.AttachLSM(link.LSMOptions{
		Program: objs.RestrictExec,
	})
	if err != nil {
		log.Fatalf("Gagal attach LSM restrict_exec: %v", err)
	}
	defer lsmExec.Close()

	// Attach Hook Kernel Module Load
	lsmMod, err := link.AttachLSM(link.LSMOptions{
		Program: objs.RestrictModuleLoad,
	})
	if err != nil {
		log.Fatalf("Gagal attach LSM restrict_module_load: %v", err)
	}
	defer lsmMod.Close()

	log.Println("[+] BPF LSM Daemon Aktif. Memblokir eksekusi 'nc' dan pemuatan Kernel Module...")

	// Menunggu sinyal termination (CTRL+C / SIGTERM)
	stop := make(chan os.Signal, 1)
	signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
	<-stop

	log.Println("[+] Mematikan BPF LSM Daemon. Security hooks dilepas.")
}

Konfigurasi & Troubleshooting Kernel VPS

Penerapan BPF LSM di lingkungan VPS production membutuhkan beberapa penyesuaian awal pada kernel.

1. Verifikasi Aktivasi BPF LSM di Kernel VPS

Tidak semua distro Linux mengaktifkan BPF LSM secara default. Cek daftar LSM aktif di VPS Anda:

cat /sys/kernel/security/lsm

Jika string bpf tidak ada di dalam daftar output, aktifkan melalui parameter GRUB. Edit file /etc/default/grub:

GRUB_CMDLINE_LINUX="lsm=landlock,lockdown,yama,integrity,apparmor,bpf"

Update bootloader lalu reboot server:

sudo update-grub
sudo reboot

2. Memastikan Dukungan BTF (BPF Type Format)

BPF LSM memerlukan metadata kernel bernama BTF. Pastikan file berikut ada di VPS Anda:

ls -la /sys/kernel/btf/vmlinux

Distro modern seperti Ubuntu 22.04+, Debian 11+, atau RHEL 9+ sudah menyertakan BTF secara default. Jika memakai custom kernel, pastikan flag CONFIG_DEBUG_INFO_BTF=y diaktifkan saat kompilasi kernel.

3. Debugging dengan Trace Pipe

Untuk melihat log pergerakan program C kernel (seperti pemicuan bpf_printk), buka terminal lain dan ketik:

sudo cat /sys/kernel/debug/tracing/trace_pipe

Kesimpulan & Aksi Nyata

BPF LSM membuka paradigma baru dalam mengamankan server Linux. Daripada pusing menyusun aturan statis SELinux yang rawan bypass, kita bisa membuat aturan logis secara programmatic menggunakan Go dan eBPF yang jauh lebih presisi dan mudah dipelihara.

Sekarang giliran kamu mencoba: uji coba script ini di environment staging, pastikan kernel VPS kamu sudah mendukung BPF LSM dan BTF, lalu pasang daemon Go ini sebagai systemd service di VPS production.

A
Aizu Dev

Tim penulis AizuDemy yang menyajikan tutorial teknologi, cloud, dan pengembangan perangkat lunak dalam Bahasa Indonesia.

๐Ÿ“– Artikel Terkait