Memasang filter seccomp langsung di dalam program memakai libseccomp, python-seccomp, dan Go, dengan pemangkasan syscall per fitur. Termasuk pembatasan keluarga socket untuk daemon jaringan serta blokir syscall berbahaya seperti unshare, mount, dan ptrace.

Di episode 10 kalian memasang seccomp dari luar: pod, service unit, container. Semua pendekatan itu punya satu kelemahan: mereka mengandalkan orang yang menjalankan aplikasi. Bagaimana jika aplikasi bisa mengunci dirinya sendiri — membatasi syscall yang boleh dipanggilnya tanpa menunggu admin memasang profil?
Episode 11 ini membahas self-confinement: aplikasi memasang filter seccomp dari dalam kodenya sendiri. Kalian akan melihat libseccomp sebagai fondasinya, python-seccomp dan Go sebagai cara praktis, serta pola-pola nyata: memangkas syscall per fitur, membatasi keluarga socket untuk daemon jaringan, dan memblokir syscall berbahaya.
Bayangkan pilot yang mengunci pintu kokpit setelah lepas landas. Sebelum lepas landas, kru butuh akses bebas — membuka file konfigurasi, menghubungkan database, membuka port. Setelah itu, kebutuhan itu hilang, dan setiap pintu yang tersisa hanya memperbesar risiko. Prinsip yang sama berlaku untuk aplikasi: lakukan inisialisasi dulu, lalu kunci.
Pola yang benar selalu berurutan:
setuid/setgid) bila diperlukan.Setelah langkah 3, syscall yang tidak terdaftar ditolak — bahkan jika aplikasi berhasil dieksploitasi, setiap langkah berikutnya (spawn shell, baca file, buka socket) sudah dipangkas. Ini adalah defense in depth di garis terdepan: dekat sekali dengan kode yang rentan.
Note
Self-confinement bukan pengganti profil eksternal dari episode 8–10, tapi pelengkapnya. Profil container memangkas dari luar, filter di dalam aplikasi memangkas dari dalam. Dua lapisan yang memeriksa dimensi yang sama dari arah berbeda — mempersulit penyerang dua kali lipat.
libseccomp adalah library C yang membungkus pembuatan filter — seccomp_init membuat konteks, seccomp_rule_add menambahkan aturan, seccomp_load memuat ke kernel. Semua binding bahasa lain hanyalah pembungkus dari tiga fungsi ini:
#include <seccomp.h>
#include <errno.h>
int main(void) {
scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ERRNO(EPERM));
if (ctx == NULL) return 1;
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
seccomp_load(ctx);
seccomp_release(ctx);
return 0;
}Bacalah alurnya: init menetapkan action default (ERRNO(EPERM) untuk semua syscall), rule_add mengizinkan yang spesifik, dan load mengirim filter ke kernel. Setelah load, aturan tidak bisa diubah lagi — kunci sudah terkunci.
Di Python, binding resminya adalah python-seccomp, diinstall dengan pip install python-seccomp. Konfigurasi yang sama jadi jauh lebih ringkas:
import seccomp
f = seccomp.SyscallFilter(defaction=seccomp.ERRNO(1))
for name in [
"read", "write", "openat", "close", "mmap",
"mprotect", "futex", "exit_group", "rt_sigaction",
]:
f.add_rule(seccomp.ALLOW, name)
f.load()Perhatikan detail yang sering menjebak: daftar syscall tidak boleh melupakan yang dipakai interpreter itu sendiri. Python runtime memanggil mmap, mprotect, futex, dan rt_sigaction untuk alokasi memori dan sinyal — filter yang terlalu kurus membuat interpreter mati di detik pertama. Selalu jalankan test suite setelah load().
Di Go, dua paket yang paling relevan: github.com/seccomp/libseccomp-golang untuk membangun filter langsung, dan github.com/containers/common/pkg/seccomp yang bisa memuat profil JSON gaya Docker ke dalam proses Go. Contoh yang pertama:
package main
import (
"log"
seccomp "github.com/seccomp/libseccomp-golang"
)
func main() {
filter, err := seccomp.NewFilter(seccomp.ActErrno.SetReturnCode(1))
if err != nil {
log.Fatal(err)
}
defer filter.Release()
names := []string{"read", "write", "openat", "close",
"mmap", "mprotect", "futex", "exit_group"}
for _, name := range names {
id, err := seccomp.GetSyscallFromName(name)
if err != nil {
log.Fatal(err)
}
if err := filter.AddRule(id, seccomp.ActAllow); err != nil {
log.Fatal(err)
}
}
if err := filter.Load(); err != nil {
log.Fatal(err)
}
}Keunggulan GetSyscallFromName adalah portabilitas nama: kalian menulis nama syscall, bukan nomor, dan library menerjemahkannya sesuai arsitektur. Kode yang sama berjalan di x86_64 dan ARM64 tanpa diubah.
Daemon jaringan membutuhkan socket, bind, dan connect — tetapi tidak semua keluarga socket. Sebuah web server seharusnya tidak perlu AF_PACKET (raw packet) atau AF_NETLINK (komunikasi dengan kernel). Dengan argfilter, kalian bisa mengizinkan socket hanya untuk keluarga tertentu. Di Python:
import seccomp
f = seccomp.SyscallFilter(defaction=seccomp.ERRNO(1))
f.add_rule(seccomp.ALLOW, "socket", seccomp.Arg(0, seccomp.EQ, 1)) # AF_UNIX
f.add_rule(seccomp.ALLOW, "socket", seccomp.Arg(0, seccomp.EQ, 2)) # AF_INET
f.add_rule(seccomp.ALLOW, "bind")
f.add_rule(seccomp.ALLOW, "connect")
f.add_rule(seccomp.ALLOW, "accept4")
f.add_rule(seccomp.ALLOW, "getsockname")
f.load()seccomp.Arg(0, seccomp.EQ, 1) berarti: argumen ke-0 dari socket harus sama dengan 1 (AF_UNIX). Kombinasi ini mengunci daemon agar hanya bisa berkomunikasi lewat socket Unix dan IPv4 — setiap upaya membuka AF_INET6, AF_PACKET, atau AF_NETLINK langsung ditolak, bahkan jika aplikasi sudah terkompromi. Ini bentuk kunci dari episode 5 yang diaplikasikan ke kehidupan nyata daemon.
Tip
Prinsipnya sama untuk semua syscall yang punya argumen pembatas: openat bisa dibatasi per path, clone bisa dibatasi per flag namespace, connect bisa dibatasi per port. Pangkas syscall per fitur, bukan per nama saja — semakin spesifik, semakin sempit celahnya.
Ada kelas syscall yang hampir tidak pernah dibutuhkan daemon biasa, tapi sangat berharga bagi penyerang. Blokir secara eksplisit, bahkan ketika daemon berjalan tanpa privilege:
unshare # membuat namespace baru — jalur pelarian sandbox
mount # mengubah filesystem host
ptrace # men-debug atau menyuntik proses lain
bpf # memanipulasi program BPF kernel
perf_event_open # mengintip aktivitas sistem
keyctl # mengelola keyring kernel
process_vm_readv # membaca memori proses lain
process_vm_writev # menulis ke memori proses lainMengapa memblokir syscall yang "butuh privilege" jika daemon tidak punya privilege? Karena defense in depth. Penyerang yang menemukan cara menaikkan privilege masih harus menghadapi lapisan berikutnya — dan filter ini memotongnya di level syscall, jauh sebelum privilege itu berguna. unshare khususnya penting: ia menjadi bahan baku eksploitasi container escape, dan memblokirnya dari dalam aplikasi memutus jalur itu sejak awal.
Warning
Memuat filter bukan alasan untuk berhenti menurunkan privilege. Seccomp membatasi aksi apa yang bisa dipanggil; capabilities dan drop UID membatasi dengan hak apa. Kombinasi keduanya — bukan pengganti satu sama lain — itulah yang membuat sandbox benar-benar solid.
Rahasia filter yang bertahan lama bukan di daftar panjang, tapi di pemetaan fitur ke syscall. Tanyakan per fitur: apa yang dibutuhkan untuk menjalankannya?
read/write file, mmap, exit_group; blokir socket, connect, execve.socket (dibatasi AF_INET/AF_INET6), bind, accept, sendto, recvfrom; blokir ptrace, keyctl, unshare.clone, execve, openat yang tidak dibutuhkan lagi.Setiap fitur menghasilkan satu baris pemikiran, bukan satu baris kode. Hasilnya adalah filter yang bisa dijelaskan — dan filter yang bisa dijelaskan adalah filter yang bisa dirawat.
Di episode 11 ini kalian telah memahami self-confinement: aplikasi memasang filter dari dalam kodenya dengan libseccomp sebagai fondasi, python-seccomp dan libseccomp-golang sebagai pembungkus yang lebih nyaman, serta pola pangkas-per-fitur yang membatasi syscall sesuai kebutuhan aktual. Kalian juga bisa membatasi keluarga socket daemon dengan argfilter dan memblokir syscall berbahaya seperti unshare, mount, ptrace, bpf, dan perf_event_open.
Kunci yang harus dibawa pulang:
Filter di dalam aplikasi menuntut kedisiplinan yang tinggi: aplikasi harus tahu persis apa yang dilakukannya. Bagaimana mekanisme seccomp berinteraksi dengan lapisan keamanan kernel lainnya — capabilities, namespaces, dan LSM? Di episode 12, kita masuk ke Seccomp & Capabilities Kombinasi: menyusun profil yang memangkas syscall sekaligus menajamkan capability set, agar pertahanan berlapis bekerja satu sama lain, bukan saling mengalahkan.