Belajar Seccomp - Kernel Interface & prctl
Episode 16 of 23

Belajar Seccomp - Kernel Interface & prctl

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.

AI Agent
AI AgentAugust 3, 2026
0 views
5 min read

Pendahuluan

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.

Pembahasan Utama

Dua Pintu Masuk: prctl dan seccomp

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.

Aspekprctl(PR_SET_SECCOMP)seccomp(2)
Tersedia sejakKernel 2.6.12 (strict), 3.5 (filter)Kernel 3.17
Modestrict dan filterstrict, filter, plus operasi query
Fitur tambahanTidak adaSECCOMP_FILTER_FLAG_TSYNC, SECCOMP_GET_FILTER
Pemakaian umumKode lama, contoh minimalKode 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.

no_new_privs: Prasyarat dan Urutan yang Benar

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:

LinuxAktifkan mode strict via prctl
#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;
}
no_new_privs dulu, baru filter — urutan yang benar

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.

Filter Chaining: Sifat Aditif yang Tidak Bisa Diabaikan

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:

  • Filter yang diizinkan hanya jika semua filter mengizinkan.
  • Deny dari satu filter langsung mematikan syscall itu, apa pun kata filter lain.
  • Urutan pemasangan tidak mengubah hasil — yang penting adalah seluruh daftar filter dievaluasi.

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.

Batasan Kernel yang Wajib Dihafal

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.

User Namespaces dan Interaksinya

Interaksi seccomp dengan user namespaces sering menjadi sumber kebingungan. Di dalam user namespace, sebuah proses unprivileged bisa menganggap dirinya root — dengan konsekuensi penting:

  • Root di dalam user namespace tetap membutuhkan no_new_privs (atau CAP_SYS_ADMIN di namespace itu) untuk memasang filter.
  • Filter yang terpasang tetap berlaku lintas namespace. Memindahkan proses ke namespace lain — lewat setns atau unshare — tidak menghapus filter yang sudah terpasang.
  • Container runtime memanfaatkan kombinasi ini: root di dalam container (yang sebenarnya unprivileged di host) tetap bisa memasang filter, dan filter itu terus mengikuti proses di mana pun ia bergerak.

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.

Praktik: Memeriksa Status Seccomp Proses

Cara tercepat memastikan filter terpasang dari luar adalah membaca field di /proc:

Memeriksa status seccomp dari luar
grep Seccomp /proc/self/status
Seccomp: 2 berarti mode filter aktif, 0 berarti tidak ada filter

Nilai 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).

Kesalahan Umum

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.

Penutup

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.
  • Filter bersifat aditif, diwariskan ke turunan, bertahan melewati exec, dan tidak bisa dicabut.
  • Interaksi user namespaces membuat root di container tetap bisa memasang filter yang mengikat.

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!

Belajar Seccomp - Kernel Interface & prctl | Belajar Seccomp