Menjelajahi profil seccomp bawaan Docker (docker-default), daftar syscall yang diblokir, dan perilaku container yang melanggarnya. Termasuk format profil JSON untuk runtime OCI serta pembuatan profil kustom allowlist lewat --security-opt.

Di episode 7 kalian menguasai debugging filter: membongkar BPF, mengamati syscall, dan menguji deny behavior. Sekarang waktunya memakai semua itu di tempat yang paling banyak ditemui di dunia nyata: container. Container bukan mesin virtual — ia berbagi kernel yang sama dengan host. Dan ketika kernel itu dimiliki bersama, syscall berbahaya dari satu container bisa mengguncang semuanya.
Episode 8 ini membedah bagaimana Docker dan runtime OCI (runc/crun) menerapkan seccomp: profil bawaan docker-default, daftar syscall yang diblokir, format JSON profil, dan cara membangun profil kustom untuk aplikasi kalian.
Sebuah container adalah kumpulan proses di kernel host yang diisolasi dengan namespaces, cgroups, dan — bila diatur — seccomp. Analoginya seperti apartemen dalam satu gedung: setiap penghuni punya kamar sendiri (namespaces) dan jatah listrik sendiri (cgroups), tapi semuanya berbagi satu fondasi dan satu mesin listrik (kernel). Kalau satu penghuni bisa menyentuh saklar utama, semua penghuni terdampak.
Syscall seperti reboot, kexec_load, atau mount adalah "saklar utama" itu. Namespaces tidak menghalanginya — ia tetap masuk ke kernel yang sama. Seccomp-lah yang memutuskan syscall tersebut tidak boleh dipanggil sama sekali.
Sejak Docker 1.10, setiap container yang dijalankan otomatis menerima profil seccomp bernama docker-default — tanpa konfigurasi tambahan. Ini salah satu momen terpenting dalam sejarah keamanan container: seccomp jadi pertahanan default, bukan fitur yang diaktifkan manual.
Profil ini bekerja sebagai denylist: defaultAction-nya adalah SCMP_ACT_ERRNO(EPERM), artinya semua syscall diblokir kecuali yang eksplisit diizinkan. Daftar yang diblokir sekitar empat puluh syscall, dengan yang paling sering dikutip seperti ini:
acct, add_key, bpf, clock_adjtime, clock_settime, create_module,
delete_module, finit_module, get_kernel_syms, init_module, io_cancel,
ioperm, iopl, kexec_file_load, kexec_load, keyctl, lookup_dcookie,
mbind, mount, move_pages, name_to_handle_at, nfsservctl,
open_by_handle_at, perf_event_open, personality, pivot_root,
process_vm_readv, process_vm_writev, ptrace, quotactl, reboot,
request_key, sethostname, setns, settimeofday, shmctl, sysfs,
swapon, swapoff, umount, umount2, unshare, uselib, userfaultfdLihat polanya, bukan sekadar menghafal daftarnya:
mount, umount, reboot, sethostname, swapon, kexec_load, init_module — menyentuh host secara langsung.unshare, setns, pivot_root — membangun atau melompat ke namespace baru, jalur pelarian klasik.ptrace, perf_event_open, process_vm_readv, process_vm_writev, keyctl — membaca memori atau men-debug proses tetangga.ioperm, iopl, vm86, sysfs, _sysctl — syscall usang yang tidak dibutuhkan aplikasi modern.Dua hal yang perlu kalian tahu soal clone: docker-default tidak memblokir clone sepenuhnya, tapi memblokir pemanggilannya dengan flag pembuatan namespace tertentu (misalnya CLONE_NEWUSER). Di sinilah kekuatan argfilter dari episode 5 dipakai — memblokir syscall dengan argumen tertentu, bukan syscall itu sendiri.
Bila aplikasi memanggil syscall yang diblokir, ia akan menerima EPERM — aplikasi melihat "Operation not permitted" tanpa tahu siapa yang menolak. Inilah momen pertama kalian pakai keterampilan episode 7: konfirmasi dengan ausearch -m SECCOMP bahwa yang menolak adalah seccomp, bukan permission biasa.
Profil ini aktif secara otomatis. Untuk memastikan daemon Docker kalian mendukungnya, cek daftar SecurityOptions:
docker info --format '{{.SecurityOptions}}'Pastikan ada name=seccomp di daftarnya.
Note
Profil bisa dimatikan dengan --security-opt seccomp=unconfined, tapi itu sama saja melepas sabuk pengaman. Pertahankan seccomp default; jadikan unconfined hanya sebagai alat debugging sementara.
Profil seccomp container bukan binary C, melainkan JSON yang mengikuti Open Container Initiative (OCI) runtime spec. runc, crun, containerd, dan CRI-O semuanya membaca format yang sama — kalian belajar sekali, berlaku di mana-mana. Struktur dasarnya: defaultAction, defaultErrnoRet, archMap, dan daftar syscalls:
{
"defaultAction": "SCMP_ACT_ERRNO",
"defaultErrnoRet": 1,
"archMap": [
{
"architecture": "SCMP_ARCH_X86_64",
"subArchitectures": ["SCMP_ARCH_X86", "SCMP_ARCH_X32"]
}
],
"syscalls": [
{
"names": [
"read", "write", "readv", "writev", "openat", "close",
"fstat", "lseek", "mmap", "mprotect", "munmap", "brk",
"exit", "exit_group", "rt_sigaction", "clone", "execve",
"getpid", "access", "fcntl", "futex", "clock_gettime",
"nanosleep", "socket", "connect", "sendto", "recvfrom",
"shutdown", "setsockopt", "getsockopt"
],
"action": "SCMP_ACT_ALLOW"
}
]
}Membaca bagian-bagiannya:
defaultAction — apa yang terjadi pada syscall yang tidak tercantum. SCMP_ACT_ERRNO berarti ditolak dengan errno.defaultErrnoRet — errno default (1 = EPERM). Di sinilah perilaku "deny" diatur secara global.archMap — arsitektur yang valid beserta sub-arsitektur kompatibelnya. Perlu diisi agar profil tidak salah jalan saat binary 32-bit berjalan di kernel 64-bit.syscalls — daftar aturan; setiap entri punya names, action, dan opsional args untuk aturan berbasis argumen (bentuk JSON dari argfilter episode 5).Perhatikan ini adalah profil allowlist: kalian menulis syscall yang boleh, sisanya ditolak. Pendekatan ini jauh lebih ketat daripada denylist docker-default, dan itulah yang kalian inginkan untuk aplikasi yang dipahami perilakunya.
Menyimpan profil sebagai file JSON, lalu menunjukkannya ke Docker lewat --security-opt:
docker run --rm --security-opt seccomp=profile-allowlist.json nginx:1.27Cara yang sama berlaku di Compose file (via security_opt) dan di runtime lain: containerd dan CRI-O pada akhirnya menurunkan semuanya ke config.json runc, dan objek seccomp di config tersebut adalah JSON yang sama persis. Kalian belajar satu format, dan format itu ikut ke mana pun.
Warning
Hati-hati dengan --security-opt seccomp=unconfined. Ini sangat berguna untuk debugging cepat — "apakah seccomp yang memblokir atau bukan" — tapi jangan pernah menjadi kondisi produksi yang permanen. Container tanpa seccomp berarti setiap syscall di host terbuka untuknya.
Membangun profil dari nol terdengar menakutkan, padahal alurnya sudah kalian kuasai di episode 7:
strace -f -c.names di JSON.brk, mmap, mprotect, rt_sigaction, openat sering muncul di detik-detik pertama; aplikasi yang gagal boot biasanya kehilangan ini.defaultAction menjadi SCMP_ACT_LOG, jalankan di staging, dan baca ausearch -m SECCOMP untuk syscall yang belum masuk daftar.SCMP_ACT_ERRNO.Mulailah dari profil kecil dan perluas secara bertahap. Profil yang terlalu kurus membuat aplikasi crash di detik pertama; profil yang terlalu gemuk adalah denylist yang menyamar sebagai allowlist. Iterasi log-dulu-enforce-kemudian adalah keseimbangan yang benar.
Di episode 8 ini kalian telah memahami bahwa container berbagi kernel host, sehingga syscall berbahaya wajib dipangkas — dan Docker sudah melakukannya secara default dengan profil docker-default yang memblokir puluhan syscall. Kalian bisa membaca format profil OCI (JSON dengan defaultAction, archMap, dan syscalls), menerapkannya lewat --security-opt seccomp=profile.json, serta membangun allowlist dari baseline strace.
Kunci yang harus dibawa pulang:
docker-default adalah denylist baseline; aplikasi kalian layak dapat allowlist.strace, uji dengan LOG, baru kunci dengan ERRNO.Profil JSON itu hebat, tapi bukan satu-satunya jalan. Banyak daemon berjalan langsung di host tanpa container — dan mereka juga butuh seccomp. Di episode 9, kita masuk ke Seccomp di systemd & Service Units: SystemCallFilter, SystemCallArchitectures, dan RestrictAddressFamilies untuk mengunci service tanpa mengubah satu baris kode aplikasi.