Belajar SELinux - Containers (Podman, Docker, CRI-O)
Episode 10 of 23

Belajar SELinux - Containers (Podman, Docker, CRI-O)

Meninjau bagaimana SELinux mengamankan container: domain container_t dan spc_t, relabeling bind mount dengan opsi :Z dan :z, pengaturan label keamanan pada Podman dan Docker, serta konfigurasi SELinux di Kubernetes pod security context dan integrasi dengan seccomp dan LSM.

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

Pendahuluan

Di episode 9, kalian melindungi web server dan database dengan label yang tepat. Sekarang saatnya teknologi yang mengubah cara deployment berjalan: container. Namespaces dan cgroups memisahkan container secara logika, tetapi keduanya tidak menghentikan proses yang lolos untuk mengakses resource host. Di sinilah SELinux muncul sebagai lapisan isolasi kedua — dan karena itu ia berperan besar di runtime seperti Podman, Docker, dan CRI-O.

Jika SELinux mati di host container, satu container yang kompromi bisa membaca data container lain di host yang sama. Episode ini menjelaskan bagaimana SELinux mencegah skenario itu.

Domain: container_t dan spc_t

Policy container SELinux memperkenalkan dua domain penting:

  • spc_t (Super Privileged Container) — domain untuk runtime engine seperti dockerd, containerd, podman, dan conmon. Engine ini dipercaya penuh: ia yang membangun dan mengelola semua container.
  • container_t — domain untuk proses yang berjalan di dalam container. Ia terkunci ketat dan tidak otomatis bisa membaca file host.

Note

Analoginya: spc_t adalah manajer gedung yang memegang kunci master semua unit, sedangkan container_t adalah penyewa yang hanya punya kunci unitnya sendiri. Pisahkan keduanya dengan tegas — ketika kalian mengeksekusi sesuatu di dalam container, kalian berada di dunia container_t, bukan di dunia host.

Karena proses container berada dalam domain berbeda dari proses host, privilege yang bocor dari dalam container tidak langsung menjadi akses penuh ke host — ia tetap harus melewati aturan tipe SELinux. Inilah alasan container yang menjalankan proses sebagai root tetap bukan "root host" di mata SELinux.

Bind Mount dan Relabeling: :Z dan :z

Kebutuhan paling umum adalah memindahkan direktori host ke dalam container dengan bind mount: -v /data:/data. Masalahnya, file /data biasanya berlabel untuk domain tertentu (misalnya default_t atau home user), dan container_t tidak diizinkan membacanya.

Solusinya bukan mengubah label host selamanya, melainkan relabeling saat mount. Dua opsi utama:

  • :Z — relabel mount dengan label unik per container (privasi penuh). Setiap container mendapat kategori MCS sendiri, sehingga container lain tidak bisa membaca file tersebut.
  • :z — relabel mount dengan label yang dibagi oleh semua container (berbagi). Semua container yang memakai volume itu bisa saling mengakses.
Relabel bind mount dengan Podman
podman run -v /data:/data:Z nginx
Relabel bind mount dengan Docker
docker run -v /data:/data:Z nginx

Important

Pilih dengan sadar. :Z memberi isolasi antar container, :z mengizinkan berbagi antar semua container. Jika sebuah volume dipakai bersama beberapa container yang memang harus saling membaca — misalnya volume cache — pakai :z. Untuk data yang hanya milik satu container, selalu :Z. Salah memakai :z pada data sensitif berarti membuka pintu antar container yang seharusnya terisolasi.

Opsi Label Keamanan pada Runtime

Selain :Z dan :z, kedua runtime menyediakan kontrol label yang lebih halus melalui --security-opt:

Menyesuaikan label container
podman run --security-opt label=type:container_t nginx
podman run --security-opt label=level:s0:c123,c456 nginx
podman run --security-opt label=disable nginx

Opsi yang tersedia:

  • label=type:... — paksa tipe domain tertentu.
  • label=user:... dan label=role:... — set user dan role pada label.
  • label=level:... — set kategori MCS, berguna untuk isolasi antar tenant.
  • label=disable — matikan SELinux untuk container tersebut.

Warning

label=disable menghapus lapisan SELinux sepenuhnya untuk container itu. Gunakan hanya untuk troubleshooting jangka pendek atau kasus khusus yang memang memerlukannya — dan pahami bahwa ia meniadakan proteksi yang sedang kita bangun. Kebiasaan "tinggal tambah label=disable" saat denial muncul adalah jalan pintas yang mahal di kemudian hari.

Podman memiliki keuntungan ergonomis: ia otomatis merelabel volume yang dipasang, sehingga banyak kasus :Z/:z tidak perlu ditulis manual. Namun tetap penting memahami apa yang terjadi di balik layar.

SELinux di Kubernetes

Di Kubernetes, runtime (biasanya CRI-O atau containerd) mengambil instruksi SELinux dari pod security context. Konfigurasi dibuat per pod, bukan per container:

KubernetesseLinuxOptions pada Pod SecurityContext
apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  securityContext:
    seLinuxOptions:
      user: system_u
      role: system_r
      type: container_t
      level: "s0:c123,c456"
  containers:
    - name: app
      image: nginx

Setiap pod diberi level MCS unik. Konsekuensinya sangat penting: pod A dengan level s0:c123 tidak bisa membaca file milik pod B dengan level s0:c456 — meski keduanya sama-sama bertipe container_t. Inilah isolasi antar workload yang sama-sama berjalan di host, tanpa perlu repot mengubah label satu per satu.

Tip

Di cluster besar, jangan mengelola level MCS manual — biarkan runtime atau tooling (misalnya melalui PSP/GAC atau policy engine seperti Kyverno) yang mengalokasikan level unik per pod. Pengelolaan manual mudah salah dan justru menciptakan celah isolasi. Yang perlu dikelola manusia hanyalah pilihan type, user, dan role yang benar untuk beban kerja.

LSM, Seccomp, dan AppArmor

Container runtime memakai beberapa mekanisme keamanan sekaligus, dan penting membedakannya:

  • SELinux adalah LSM (Linux Security Module) — hook di kernel yang memutuskan akses berdasarkan label. Secara historis, hanya satu LSM utama (SELinux, AppArmor, atau Smack) yang aktif per kernel. Inilah kenapa distro keluarga RHEL memakai SELinux sementara Ubuntu dan Debian memakai AppArmor — keduanya jarang berjalan bersamaan di sistem yang sama.
  • Seccomp berbeda kategori: ia bukan LSM, melainkan filter panggilan syscall. Karena jalurnya terpisah, seccomp bisa aktif bersamaan dengan SELinux — dan memang itulah praktik yang benar.

Kombinasi ideal di Kubernetes: profile seccomp membatasi syscall, SELinux membatasi akses berdasarkan label, dan cgroups membatasi resource. Tiga lapisan yang bekerja di level berbeda dan saling melengkapi — inilah arti defense in depth yang sesungguhnya.

Penutup

Pada episode 10 ini kalian sudah melihat bagaimana SELinux menjadi lapisan isolasi kedua untuk container: domain spc_t untuk runtime dan container_t untuk proses container, relabeling bind mount dengan :Z dan :z, kontrol label halus lewat --security-opt, serta konfigurasi seLinuxOptions di Kubernetes dengan level MCS unik per pod.

Kunci yang harus dibawa pulang:

  • Container di domain container_t tidak otomatis bisa membaca host — itulah proteksinya.
  • :Z untuk privasi per container, :z untuk berbagi antar container.
  • Seccomp dan SELinux bekerja bersamaan: satu membatasi syscall, satu membatasi akses berdasarkan label.

Sejauh ini semua storage masih lokal di host. Di episode 11, kita akan membawa SELinux ke dunia terdistribusi: NFS, Samba & Network Services — bagaimana labeling bekerja di NFS dengan opsi mount dan noexec, konsekuensi berbagi storage antar host, boolean samba untuk ekspor direktori dan home, serta labeling port untuk layanan non-standar.

Belajar SELinux - Containers (Podman, Docker, CRI-O) | Belajar SELinux