Belajar Seccomp - Konsep Dasar & Arsitektur Utama
Episode 2 of 23

Belajar Seccomp - Konsep Dasar & Arsitektur Utama

Episode ini membedah arsitektur seccomp: mode SECCOMP_MODE_STRICT dan SECCOMP_MODE_FILTER berbasis BPF, peran prctl dan syscall seccomp, komponen libseccomp dan seccomp_export_bpf, serta bagaimana seccomp terintegrasi dengan runc, crun, systemd, dan OpenSSH.

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

Pendahuluan

Di episode 1 kalian sudah memahami sejarah seccomp dan masalah yang ia pecahkan. Sekarang waktunya membuka kap mesin: bagaimana seccomp bekerja di dalam sistem? Episode ini membedah arsitekturnya — dari alur sebuah syscall, dua mode operasi (SECCOMP_MODE_STRICT dan SECCOMP_MODE_FILTER), pembagian peran antara kernel dan libseccomp, hingga bagaimana runtime container dan systemd memanfaatkannya.

Jika episode 1 menjawab kenapa, episode 2 ini menjawab bagaimana. Pahami baik-baik arsitekturnya, karena seluruh episode berikutnya — return actions, API libseccomp, hingga arg filter — hanyalah pengembangan dari kerangka yang kita bangun di sini.

Alur Sebuah Syscall

Sebelum membahas seccomp, mari luruskan dulu apa yang terjadi ketika sebuah program memanggil syscall. Perhatikan alur berikut:

Alur syscall dengan filter seccomp terpasang
Aplikasi (user mode)
   │  memanggil syscall, misalnya execve

glibc / libc (menerjemahkan ke instruksi syscall)

Kernel: menerima syscall

Filter seccomp terpasang? ── tidak ──► jalankan syscall normal
   │ ya

Kernel mengevaluasi filter BPF

   ├── ALLOW ──► jalankan syscall
   └── DENY  ──► kernel menolak sesuai return action

Perhatikan poin kuncinya: filter dievaluasi di kernel, tepat setelah syscall diterima dan sebelum handler syscall dieksekusi. Ini berarti tidak ada jalan bagi aplikasi untuk menghindarinya — bahkan jika aplikasi sudah sepenuhnya dikompromikan oleh attacker, keputusan tetap ada di tangan kernel.

Mode Seccomp

Seccomp memiliki dua mode operasi, dan keduanya merupakan jawaban dari dua era sejarah yang kita bahas di episode 1.

SECCOMP_MODE_STRICT

Mode tertua (kernel 2.6.12). Begitu diaktifkan, proses hanya boleh memanggil read, write, _exit, dan sigreturn. Pelanggaran apa pun langsung membunuh proses. Diaktifkan lewat prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT) dan bersifat tidak dapat dibatalkan.

Mengaktifkan strict mode lewat prctl
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/prctl.h>
#include <linux/seccomp.h>
 
int main(void) {
    if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT) == -1) {
        perror("prctl");
        syscall(SYS_exit, 1);
    }
    write(STDOUT_FILENO, "strict mode aktif\n", 18);
    syscall(SYS_exit, 0);
}

Note

Perhatikan bahwa program di atas tidak memakai return 0 di akhir main. Di Linux, return dari main memicu syscall exit_group — yang TIDAK diizinkan strict mode — sehingga proses akan di-kill. Inilah alasan kenapa kita memanggil syscall(SYS_exit, 0) secara langsung, dan sekaligus bukti betapa tidak praktisnya strict mode untuk aplikasi nyata.

SECCOMP_MODE_FILTER (Seccomp-bpf)

Mode modern (kernel 3.5). Proses memasang satu atau lebih filter BPF melalui prctl atau syscall seccomp(). Setiap filter adalah program BPF yang menerima seccomp data — berisi nomor syscall, arsitektur, instruction pointer, dan enam argumen — lalu mengembalikan sebuah return action (izinkan, tolak, bunuh, dan lain-lain).

Berbeda dengan strict mode, filter seccomp-bpf:

  • Bisa menyaring syscall secara selektif, bukan membabi buta.
  • Bisa memeriksa argumen syscall (kita bahas di episode 5).
  • Bisa distack — beberapa filter boleh terpasang berlapis, dan kernel memilih aksi paling restriktif.
  • Masih bersifat tidak bisa dibatalkan (dalam praktiknya).

Hampir semua penggunaan seccomp di dunia nyata — Docker, Kubernetes, systemd, Chromium — memakai mode ini.

Komponen Utama: Kernel vs libseccomp

Seccomp terdiri dari dua lapisan yang bekerja sama: kernel sebagai penegak kebijakan, dan libseccomp sebagai pembantu pembuat kebijakan.

Kernel: prctl dan Syscall seccomp

Di sisi kernel, filter dipasang melalui dua jalur:

  • prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) — cara tradisional.
  • Syscall seccomp(2) — cara modern, mendukung flag seperti SECCOMP_FILTER_FLAG_TSYNC untuk menyinkronkan filter ke seluruh thread.

Setiap kali syscall dipanggil, kernel mengumpulkan data berikut ke dalam struktur seccomp_data dan memberikannya ke program BPF:

FieldIsi
nrNomor syscall (misalnya 59 untuk execve di x86_64)
archIdentitas arsitektur (misalnya SCMP_ARCH_X86_64)
instruction_pointerAlamat instruksi pemanggil syscall
args[0] sampai args[5]Enam argumen syscall

Program BPF membaca field-field ini dan mengembalikan return action. Keindahannya: BPF seccomp dijamin berhenti — kernel menolak filter yang berpotensi loop selamanya, jadi overheadnya bisa diprediksi.

libseccomp: Abstraksi Berbahasa Manusia

Menulis BPF dengan tangan itu mungkin, tapi menyakitkan. Bayangkan harus menulis bytecode untuk aturan sederhana seperti "tolak execve tapi izinkan sisanya" — apalagi harus menangani perbedaan nomor syscall antar arsitektur. Di sinilah libseccomp masuk.

libseccomp adalah library C yang mengabstraksi seluruh kerumitan tersebut. Kalian mendeskripsikan kebijakan secara deklaratif:

  • Tentukan default action (SCMP_ACT_ALLOW untuk deny-list, SCMP_ACT_ERRNO untuk allow-list).
  • Tambahkan aturan per syscall lewat seccomp_rule_add.
  • Biarkan libseccomp yang menyusun program BPF yang benar, termasuk handling arsitektur.

Dan karena kebijakan dihasilkan sebagai program BPF, ia bisa diekspor ke file dan dibedah — inilah peran seccomp_export_bpf:

Ekspor BPF program dari sebuah binary
# libseccomp juga dilengkapi alat bantu untuk membedah BPF
seccomp-tools dump ./program-dengan-filter
 line  CODE  JT   JF   K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x00 0xc000003e  if (A != ARCH_X86_64) goto 0003
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x15 0x00 0x01 0x0000003b  if (A == execve) goto 0005
 0004: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0005: 0x06 0x00 0x00 0x00050000  return ERRNO(1)

Integrasi dengan Ekosistem

runc dan crun

Runtime OCI seperti runc dan crun membaca profile seccomp dari config.json setiap kali container dijalankan. Profile tersebut adalah deskripsi JSON yang sangat mirip dengan apa yang kita buat lewat libseccomp:

Profil seccomp di OCI config.json
{
  "defaultAction": "SCMP_ACT_ERRNO",
  "defaultErrnoRet": 1,
  "architectures": ["SCMP_ARCH_X86_64", "SCMP_ARCH_X86"],
  "syscalls": [
    {
      "names": ["read", "write", "openat", "close", "mmap", "exit_group"],
      "action": "SCMP_ACT_ALLOW"
    },
    {
      "names": ["ptrace", "reboot", "kexec_load", "swapon"],
      "action": "SCMP_ACT_ERRNO"
    }
  ]
}

Inilah profile default yang dihasilkan Docker dan dipasang di setiap container — kalian bisa melihat versi lengkapnya di mesin kalian dengan docker info lalu mencari bagian SecurityOptions: seccomp.

systemd

Untuk daemon yang dijalankan sebagai service unit, systemd menyediakan direktif seccomp tanpa perlu menulis JSON. Cukup pasang SystemCallFilter di unit file:

LinuxService unit systemd dengan SystemCallFilter
[Service]
ExecStart=/usr/sbin/sshd -D
NoNewPrivileges=yes
SystemCallFilter=@system-service
SystemCallFilter=~@obsolete @mount @privileged
SystemCallErrorNumber=EPERM

SystemCallFilter menerima allow-list (@system-service) dan deny-list (prefix ~). systemd menerjemahkan direktif ini menjadi filter seccomp yang sama dengan yang dibangun libseccomp — dengan SystemCallErrorNumber=EPERM sebagai default action.

OpenSSH

OpenSSH menggunakan seccomp untuk privilege separation: proses preauth yang menghadapi permintaan koneksi jaringan (dan karenanya paling berisiko) dijalankan dengan filter seccomp ketat agar syscall berbahaya seperti execve, connect, dan openat diblokir. Begitu autentikasi berhasil, proses baru dengan hak pengguna dibangun dengan cara yang lebih terkontrol. Inilah pola yang sama yang akan kalian temui berulang kali: bagian program yang paling rentan berjalan di bawah filter seccomp yang paling ketat, sementara bagian yang bisa dipercaya berjalan normal.

Penutup

Di episode 2 ini kalian sudah memahami kerangka arsitektur seccomp:

  • Syscall dievaluasi oleh filter seccomp di kernel, sebelum handler syscall dijalankan.
  • Ada dua mode: SECCOMP_MODE_STRICT (4 syscall, kaku) dan SECCOMP_MODE_FILTER (BPF, fleksibel).
  • Kernel menegakkan kebijakan lewat prctl / syscall seccomp(2); libseccomp menyusun program BPF-nya.
  • seccomp_data menyediakan nomor syscall, arsitektur, dan enam argumen untuk dievaluasi BPF.
  • runc/crun memuat profile JSON dari config.json, systemd lewat SystemCallFilter, dan OpenSSH lewat privilege separation.

Di episode 3 berikutnya kita akan membedah jantung pengambilan keputusan seccomp: modes & return actions — perbandingan mendalam strict vs filter, plus panduan kapan memakai SECCOMP_RET_KILL_PROCESS, TRAP, ERRNO, TRACE, ALLOW, LOG, dan NOTIFY. Di sana kalian akan belajar bagaimana sebuah filter "menjawab" setiap syscall. Sampai jumpa!