Filesystem container kini terkunci rapat: writable layer dibuang, lubang tulis hanya tmpfs yang kita pilih, dan proses root sekalipun ditolak kernel saat mencoba menulis ke disk. Namun penutup Chapter Read-Only Filesystem sengaja meninggalkan dua lapisan terdalam dari lima lapisan isolasi yang masih bekerja diam-diam dengan pengaturan bawaannya: seccomp dan LSM. Keduanyalah yang tetap bertugas pada detik terburuk — ketika penyerang sudah memegang shell di dalam container — karena keduanya menyaring di tempat yang belum pernah kita sentuh sejauh ini: pintu syscall menuju kernel host. Chapter ini membuka kedua lapisan tersebut, dari filter seccomp yang dipasang runc, profil AppArmor yang menempel otomatis di Ubuntu, label SELinux di keluarga Red Hat, sampai cara ketiganya diverifikasi dan dikustomisasi.

Seccomp: Menyaring Syscall di Pintu Kernel #

Setiap hal yang dilakukan proses container — membuka berkas, mengirim paket, mengalokasikan memori — pada akhirnya adalah satu panggilan syscall ke kernel host, persis pemahaman yang kita bangun sejak Chapter Prinsip Keamanan Container. Seccomp (secure computing mode) berdiri tepat di pintu itu: sebuah filter BPF (Berkeley Packet Filter) yang dipasang runc ke proses container sesaat sebelum proses pertama dieksekusi. Sejak detik itu, setiap syscall dari proses container melewati filter lebih dulu; syscall yang tidak lolos tidak pernah menyentuh kode kernel.

Profil bawaan Docker bekerja sebagai allowlist: sekitar 300 syscall yang dikenal aman dan umum dibutuhkan aplikasi diizinkan lewat, sementara sekitar 60 syscall lainnya ditolak. Penolakan itu tidak berupa pesan “syscall tidak dikenal”, melainkan galat EPERM — aplikasi melihat Operation not permitted, sama persis dengan penolakan capability yang kita amati pada Chapter Linux Capabilities. Daftar yang ditolak memuat syscall yang hampir tidak pernah dibutuhkan aplikasi normal, tetapi sangat dibutuhkan eksploit yang ingin melompat dari container ke host:

Syscall yang diblokirTugas aslinya di kernelKenapa diblokir untuk container
kexec_load, kexec_file_loadmemuat kernel pengganti untuk reboot tanpa hidup ulang mesinmengganti kernel host yang sedang berjalan
bpfmemuat program eBPF untuk diproses di dalam kernelmengeksekusi kode di dalam kernel host
perf_event_openmengukur event kernel untuk profilingkebocoran data lintas proses lewat pengukuran
init_module, finit_modulememuat modul kernelmenyuntik kode ke kernel host
open_by_handle_atmembuka berkas lewat handle, bukan pathmenembus isolasi mount menuju berkas host
keyctlmengelola keyring kredensial di kernelmembaca rahasia milik proses lain

Baris bpf pada tabel itu bekerja dengan aturan bersyarat: profil bawaan memuat aturan yang mengizinkan bpf hanya bila container sengaja diberi CAP_SYS_ADMIN. Container bawaan tidak membawa capability itu — daftar dua belas dari Chapter Linux Capabilities tidak memuatnya — sehingga bpf tetap tertutup; hanya container yang secara sadar diberi capability tinggi yang membukanya.

Profil Kustom: Allowlist yang Dirancang Sendiri #

Skenario nyatanya begini: Siti diminta menjalankan sebuah alat profiling internal di dalam container — alat itu sudah ia telusuri dengan tracer selama development sehingga ia tahu persis syscall mana yang dipanggilnya. Daripada mewarisi allowlist 300 syscall profil bawaan, Siti menyempitkan dunia alat itu menjadi belasan syscall yang terbukti dipakai, dalam berkas profil-diagnostik.json:

json
{
  "defaultAction": "SCMP_ACT_ERRNO",
  "architectures": [
    "SCMP_ARCH_X86_64",
    "SCMP_ARCH_X86",
    "SCMP_ARCH_X32"
  ],
  "syscalls": [
    {
      "names": [
        "access", "arch_prctl", "brk", "close", "execve", "exit_group",
        "fstat", "getdents64", "getuid", "ioctl", "mmap", "mprotect",
        "munmap", "newfstatat", "open", "openat", "pread64", "prlimit64",
        "read", "rt_sigaction", "rt_sigprocmask", "set_tid_address",
        "set_robust_list", "write", "writev"
      ],
      "action": "SCMP_ACT_ALLOW"
    }
  ]
}

Mari kita telaah tiga komponen utama berkas tersebut:

  1. "defaultAction": "SCMP_ACT_ERRNO" — kebijakan untuk semua syscall yang tidak disebut di berkas: tolak dengan galat EPERM; ini yang membuat profil bekerja sebagai allowlist ketat, kebalikan dari blocklist.
  2. "architectures" — daftar tabel nama syscall yang dikenal filter; nomor syscall berbeda antar arsitektur, jadi daftar ini memastikan nama yang sama berlaku konsisten pada host x86_64 maupun turunannya.
  3. "syscalls" dengan names dan "action": "SCMP_ACT_ALLOW" — satu-satunya pintu terbuka: 25 syscall hasil telusuran Siti, mencakup membaca direktori (getdents64), membuka berkas (openat, newfstatat), dan menulis keluaran (write, writev) — tetapi tidak memuat socket, connect, atau unshare.

Profil itu ditempelkan lewat flag --security-opt, dan hasilnya bisa kita uji dengan dua perintah yang berbeda nasib:

bash
$ docker run --rm --security-opt seccomp=profil-diagnostik.json \
    alpine:3.20 ls /

Output yang Dihasilkan:

text
bin    etc    lib    proc    run    srv    tmp    var
dev    home   media  root    sbin   sys    usr
bash
$ docker run --rm --security-opt seccomp=profil-diagnostik.json \
    alpine:3.20 ping -c 1 127.0.0.1

Output yang Dihasilkan:

text
ping: socket: Operation not permitted

Mari kita telaah kontras kedua output tersebut:

  1. ls / berhasil — seluruh syscall yang dibutuhkannya (openat, newfstatat, getdents64, write) ada di daftar allowlist Siti.
  2. ping ditolak sebelum menyentuh kernel — membuat socket jaringan menuntut syscall socket yang tidak ada di daftar; filter mengembalikan EPERM dan busybox melaporkannya sebagai Operation not permitted.
  3. Penolakan terjadi di dunia jaringan, bukan berkas — profil Siti memang sengaja membiarkan alat profiling bekerja pada berkas lokal tanpa satu pun pintu keluar jaringan.

AppArmor: Profil per Program di Ubuntu #

Seccomp menyaring berdasarkan nomor syscall; LSM (Linux Security Modules) memeriksa operasi yang dibangun di atasnya. AppArmor — bawaan keluarga Ubuntu — bekerja dengan profil per program yang menyatakan operasi apa yang boleh: profil docker-default dipasang Docker pada setiap container di host Ubuntu yang mengaktifkan AppArmor, membatasi operasi klasik yang dipakai breakout — mount dengan tipe berbahaya, pembuatan raw socket, dan penulisan ke area sensitif /proc dan /sys.

Profil itu aktif tanpa satu flag pun dari kita, dan statusnya bisa diganti per container: --security-opt apparmor=nama-profil menempelkan profil kustom yang dimuat lebih dulu ke kernel host dengan apparmor_parser, sementara --security-opt apparmor=unconfined melepasnya. Kustomisasi profil AppArmor biasanya dimulai dari profil bawaan yang disalin lalu disempitkan — menambah aturan penolakan untuk path yang aplikasi kita pasti tidak butuh.

SELinux: Label pada Setiap Objek di Keluarga Red Hat #

SELinux menegakkan kebijakan lewat label yang menempel pada setiap subjek dan objek: proses container berjalan dengan tipe container_t, berkas milik image bertipe container_file_t, dan kernel menolak interaksi yang tidak diizinkan kebijakan antara kedua tipe itu. Docker menambah satu lapisan lagi bernama label MCS (Multi-Category Security): setiap container mendapat pasangan kategori acak seperti s0:c412,c743, sehingga container A tidak bisa membaca berkas yang dilabeli kategori container B — isolasi antar container yang seccomp dan AppArmor tidak memberikan. Kontrolnya tersedia lewat --security-opt label=type:container_t untuk mengganti tipe, dan label=disable untuk mematikan pelabelan sama sekali.

Satu gotcha yang hampir semua orang temui saat pertama kali memakai SELinux: volume atau bind mount dari host tidak membawa label container_file_t, sehingga proses container yang tampak punya izin berkas tetap ditolak kernel. Suffix :z pada deklarasi mount memerintahkan Docker melabeli ulang isi volume agar cocok dengan label container (dipakai bersama), sedangkan :Z melabeli khusus untuk satu container itu saja — mekanisme mount yang kita kenal dari Chapter Volume, kini dengan makna label di baliknya.

Ketiganya dalam Satu Pandangan #

AspekSeccompAppArmorSELinux
Objek yang disaringnomor syscalloperasi per profil programoperasi per label tipe dan kategori
Cara kerjafilter BPF di depan syscallprofil path dan kapabilitaskebijakan label pada kernel
Status bawaan Dockerprofil allowlist ±300 syscallprofil docker-default aktif di Ubuntutipe container_t + label MCS di Red Hat
Cara mengganti--security-opt seccomp=berkas.json--security-opt apparmor=nama-profil--security-opt label=type:...
Kapan butuh kustomaplikasi minimal atau alat diagnosaaturan path lebih ketatintegrasi label dengan storage host

Bendera --privileged yang kita bedah pada Chapter Privileged Container mematikan ketiganya sekaligus: seccomp tidak dipasang, profil AppArmor dilepas, dan pelabelan SELinux dinonaktifkan — satu alasan lagi mengapa bendera itu dibaca sebagai bendera merah. Satu opsi pelengkap yang layak dihafal: --security-opt no-new-privileges mencegah proses di dalam container memperoleh privilege baru lewat berkas setuid — murah, tanpa efek samping bagi aplikasi normal, dan menutup satu trik eskalasi klasik.

Membaca Status di docker info #

Ketiga mekanisme bergantung pada kernel host, jadi langkah pertama di mesin mana pun adalah membaca apa yang sebenarnya aktif:

bash
$ docker info

Output yang Dihasilkan (dipotong):

text
Client:
 Version:    29.9.0
...
Server:
 Containers: 14
...
 Security Options:
  apparmor
  seccomp
   Profile: builtin
 Kernel Version: 6.8.0-52-generic
 Operating System: Ubuntu 24.04.1 LTS
...

Mari kita telaah baris-baris yang penting bagi kita:

  1. apparmor tercantum di Security Options — host ini mengaktifkan AppArmor, dan daemon mendeteksinya; profil docker-default akan menempel pada container berikutnya.
  2. seccomp dengan Profile: builtin — filter seccomp aktif memakai profil bawaan Docker; kalau baris ini hilang, setiap container di host ini berjalan tanpa penyaringan syscall.
  3. Ubuntu 24.04.1 LTS pada Engine 29.9.0 — konteks penting saat membaca dokumentasi: perintah dan perilaku keamanan yang kita bahas di chapter ini diuji pada kombinasi ini.

Lapisan demi lapisan, kerangka keamanan container kini lengkap kita kendalikan: identitas diturunkan, capability dicabut, filesystem dibekukan, syscall disaring, dan operasi diperiksa LSM. Namun satu kelas barang masih berpindah tangan dalam bentuk paling rapuh sepanjang course ini: rahasia. Password database rahasia123 kita masih melintas sebagai teks polos di environment variable sejak Section Docker Compose. Chapter Secret Management membangun tangga enam tingkat — dari hardcode yang paling buruk sampai secret manager produksi — lengkap dengan demonstrasi bagaimana rahasia di environment bocor dalam satu perintah docker inspect.

Referensi #