Membedah dua pintu masuk seccomp di level kernel: prctl dengan PR_SET_SECCOMP dan syscall seccomp dengan SECCOMP_SET_MODE_FILTER. Termasuk prasyarat no_new_privs, filter chaining yang bersifat aditif, warisan filter pada fork dan exec, serta interaksinya dengan user namespaces.

Di episode 15 kalian belajar mengamati filter dari luar: audit log, SECCOMP_RET_LOG, dan telemetri denial rate. Semua itu mengamati filter yang sudah terpasang. Namun ada pertanyaan mendasar yang selama ini kita tunda: bagaimana sebenarnya sebuah filter sampai ke kernel?
Jawabannya ada di dua antarmuka: prctl(2) — syscall serbaguna yang juga mengatur flags proses — dan seccomp(2), syscall khusus yang lahir belakangan. Keduanya adalah "pintu masuk" ke mekanisme seccomp, dan memahami keduanya bukan sekadar pengetahuan trivia. Dari antarmuka inilah lahir batasan-batasan yang harus kalian hafal: filter bersifat aditif, diwariskan ke anak proses, tidak bisa dicabut, dan punya interaksi rumit dengan user namespaces.
Analoginya: prctl dan seccomp adalah dua gerbang resmi untuk memasuki gedung seccomp. Kalian sudah tahu aturan di dalam gedung — kini saatnya tahu cara masuk dan apa yang terjadi pada kartu akses kalian ketika proses men-fork atau men-exec. Mari mulai.
prctl(PR_SET_SECCOMP, ...) adalah antarmuka tertua. Sejak kernel 2.6.12, prctl digunakan untuk mengaktifkan seccomp strict, dan sejak 3.5 untuk memasang filter BPF. Kode lama dan banyak program C sederhana masih memakainya.
Syscall seccomp(2) lahir di kernel 3.17 sebagai antarmuka khusus. Ia lebih ekspresif: mendukung flag seperti SECCOMP_SET_MODE_FILTER dan SECCOMP_SET_MODE_STRICT, plus operasi query seperti SECCOMP_GET_ACTION_AVAIL dan SECCOMP_GET_FILTER untuk memeriksa filter yang terpasang.
| Aspek | prctl(PR_SET_SECCOMP) | seccomp(2) |
|---|---|---|
| Tersedia sejak | Kernel 2.6.12 (strict), 3.5 (filter) | Kernel 3.17 |
| Mode | strict dan filter | strict, filter, plus operasi query |
| Fitur tambahan | Tidak ada | SECCOMP_FILTER_FLAG_TSYNC, SECCOMP_GET_FILTER |
| Pemakaian umum | Kode lama, contoh minimal | Kode modern dan libseccomp |
Dalam praktik, kalian jarang memanggil salah satu secara langsung — libseccomp memilih seccomp(2) ketika tersedia dan jatuh ke prctl di kernel lama. Tapi memahami perbedaannya membantu saat membaca kode C runtime container dan saat debugging.
Aturan kernel yang wajib dihafal: proses unprivileged hanya boleh memasang filter jika flag no_new_privs sudah aktif — atau jika ia memegang CAP_SYS_ADMIN. Ini bukan rekomendasi, melainkan pemeriksaan kernel: tanpa salah satu syarat itu, pemasangan filter ditolak dengan EPERM.
Urutan pemasangannya juga berpola tetap, dan menukarnya adalah kesalahan klasik:
#define _GNU_SOURCE
#include <sys/prctl.h>
#include <linux/seccomp.h>
#include <stdio.h>
#include <unistd.h>
int main(void) {
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) {
perror("no_new_privs");
return 1;
}
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT) != 0) {
perror("seccomp");
return 1;
}
printf("mode strict aktif, seccomp terpasang\n");
return 0;
}Perhatikan urutan di atas: PR_SET_NO_NEW_PRIVS dipanggil sebelum PR_SET_SECCOMP. Mode strict hanya mengizinkan read, write, _exit, dan sigreturn — jauh terlalu sempit untuk aplikasi modern, tetapi sempurna untuk mengilustrasikan cara kerja antarmuka. Dalam kode nyata, mode yang dipakai hampir selalu filter BPF lewat libseccomp, yang pada gilirannya memanggil seccomp(2).
Important
no_new_privs bukan sekadar formalitas agar filter diterima — ia adalah jaring pengaman yang mencegah filter dibatalkan oleh kenaikan hak. Proses yang men-set flag ini, dan seluruh turunannya, tidak akan pernah memperoleh hak baru dari setuid binary, file capabilities, atau ambient capabilities. Pasang selalu, di urutan pertama.
Sejak kernel 4.14, sebuah proses bisa memasang lebih dari satu filter. Setiap panggilan seccomp(SECCOMP_SET_MODE_FILTER, ...) menambahkan satu filter lagi ke daftar. Ketika sebuah syscall dipanggil, kernel mengeksekusi semua filter secara berurutan — dan keputusan paling restriktif yang menang.
Sifat ini disebut aditif. Bayangkan tiga petugas keamanan di pintu yang sama: kalian harus melewati ketiganya, dan satu petugas yang menolak berarti seluruh rombongan ditolak. Secara praktis:
Ini membuka pola komposisi yang akan kita manfaatkan di episode 17: filter dasar yang diblokir di mana-mana (misalnya deny mount), ditambah filter per-aplikasi yang mempersempit syscall sesuai workload. Keduanya berjalan berdampingan, saling menguatkan, tanpa konflik.
Ada beberapa batasan yang menentukan perilaku filter di seluruh siklus hidup proses:
Filter diwariskan ke anak pada fork dan clone. Setiap proses turunan mewarisi seluruh daftar filter induknya. Ini otomatis dan tidak bisa dimatikan — tidak perlu memasang filter ulang di setiap anak.
Filter bertahan melewati execve. Berbeda dengan flag proses lain yang direset saat exec, filter seccomp tetap terpasang. Sebuah proses tidak bisa melarikan diri dari filternya dengan mengeksekusi program lain.
Filter tidak bisa dicabut. Sekali terpasang, tidak ada operasi resmi untuk melepas filter. Satu-satunya "reset" adalah proses keluar. (Operasi SECCOMP_GET_FILTER hanya untuk membaca, bukan menghapus.)
Thread mengikuti proses. Dalam proses multithread, filter berlaku untuk seluruh thread. Flag SECCOMP_FILTER_FLAG_TSYNC memastikan filter baru tersinkronisasi ke semua thread sekaligus saat dipasang.
Konsekuensinya terasa jelas di container: begitu runtime memasang filter, seluruh proses di dalam container — aplikasi, worker, subproses apa pun — hidup di bawah filter yang sama. Inilah mengapa desain filter harus memperhitungkan semua mode operasi aplikasi, bukan hanya jalur utama.
Interaksi seccomp dengan user namespaces sering menjadi sumber kebingungan. Di dalam user namespace, sebuah proses unprivileged bisa menganggap dirinya root — dengan konsekuensi penting:
no_new_privs (atau CAP_SYS_ADMIN di namespace itu) untuk memasang filter.setns atau unshare — tidak menghapus filter yang sudah terpasang.Inilah salah satu alasan no_new_privs menjadi standar emas: ia membuat seccomp bekerja secara konsisten di seluruh skenario namespace, tanpa bergantung pada siapa yang kebetulan memegang CAP_SYS_ADMIN.
Cara tercepat memastikan filter terpasang dari luar adalah membaca field di /proc:
grep Seccomp /proc/self/statusNilai 2 pada field Seccomp berarti mode filter aktif; 1 berarti mode strict; 0 berarti tidak ada filter. Untuk proses lain, ganti self dengan PID — misalnya grep Seccomp /proc/1234/status. Pemeriksaan ini wajib dilakukan dalam checklist verifikasi filter production (dibahas di episode 19).
1. Memasang filter sebelum no_new_privs. Panggilan ditolak dengan EPERM pada proses unprivileged. Urutkan: flag dulu, filter kemudian.
2. Mengira filter bisa dicabut atau di-reset saat exec. Filter bertahan melewati execve dan tidak punya operasi pencabutan. Desain filter sekali, dan pastikan benar sejak awal.
3. Lupa sifat aditif. Menambahkan filter baru tidak pernah "menggantikan" filter lama — keduanya dievaluasi bersama, dan deny dari mana pun menang. Perluas filter dengan hati-hati.
4. Menggunakan mode strict untuk aplikasi nyata. Mode strict hanya mengizinkan empat syscall. Untuk aplikasi modern, pakai mode filter dengan allow-list yang diukur dari profil workload.
Pada episode 16 ini kalian memahami lapisan terendah seccomp: antarmuka kernel. prctl(PR_SET_SECCOMP) sebagai pintu masuk klasik dan syscall seccomp(2) sebagai antarmuka modern, prasyarat no_new_privs yang harus dipasang lebih dulu, sifat filter yang aditif dan diwariskan pada fork serta exec, batasan bahwa filter tidak bisa dicabut, dan interaksinya dengan user namespaces yang memungkinkan container runtime bekerja seperti sekarang.
Poin kunci yang perlu kalian bawa:
prctl dan seccomp(2) adalah dua antarmuka; libseccomp memilih yang tersedia.no_new_privs adalah prasyarat dan pengaman sekaligus — pasang di urutan pertama.Sekarang kalian tahu antarmuka dan batasannya. Pertanyaan selanjutnya adalah seni merancangnya: bagaimana menyusun filter yang presisi, efisien, dan aman untuk workload tertentu. Di episode 17 berikutnya kita akan membahas advanced filter design — optimasi BPF, aturan argumen presisi, komposisi filter berlapis, hingga pola minimal syscall set yang diukur dengan strace. Sampai jumpa!