Menulis policy dengan CIL: deklarasi tipe, atribut, dan rule modern, kompilasi dengan secilc, hierarki policy modular, perbedaan modul .pp dan CIL, dependensi antar modul, hingga best practices maintainability.

Di episode 12 kalian menulis modul pertama — baik dalam Type Enforcement klasik maupun CIL. Di episode 16 kalian belajar menyelidiki mesin live sampai ke jembatan kernel-nya. Sekarang kita gabungkan keduanya pada skala yang lebih besar: bagaimana policy produksi yang sehat disusun sebagai sistem modul, bukan satu file raksasa?
Episode ini adalah puncak teknik authoring. Kita bedah CIL sebagai bahasa tengah modern — deklarasi tipe, atribut, dan rule dalam S-expression — lalu kompilasi dengan secilc. Berikutnya kita masuk ke arsitektur: hierarki policy modular, perbedaan modul .pp vs CIL, pengelolaan dependensi antar modul, dan best practices yang menjaga policy tetap terpelihara selama bertahun-tahun, bukan sekadar berfungsi hari ini.
Setiap policy SELinux modern — termasuk yang dihasilkan dari source refpolicy — pada akhirnya diubah menjadi CIL (Common Intermediate Language) sebelum dikompilasi menjadi binary yang dimakan kernel. Pikirkan CIL seperti bytecode antara: bukan bahasa "tingkat tinggi" seperti .te, tapi juga bukan binary. Karena posisinya itu, CIL menjadi titik temu yang stabil: apapun bahasa sumbernya, semuanya berakhir di CIL.
Ada satu konsekuensi praktis yang menyenangkan: kalian bisa menulis policy langsung dalam CIL tanpa menyentuh .te sama sekali, dan hasilnya akan dimuat oleh kernel modern tanpa perantara. Inilah jalur tercepat dari ide menjadi policy yang berjalan.
Sintaks CIL adalah S-expression: semua hal dibungkus kurung. Mari bedah deklarasi intinya:
(type myapp_t)
(type myapp_exec_t)
(typeattribute daemon)
(typeattributeset daemon (myapp_t))
(roletype system_r myapp_t)
(allow myapp_t self (process (sigchld)))
(allow myapp_t etc_t (file (read)))
(typeattributeset cil_gen_require (etc_t))Baris demi baris:
(type myapp_t) — mendeklarasikan tipe, setara type di TE.(typeattribute daemon) — mendeklarasikan atribut (sebuah "kategori" yang menampung banyak tipe).(typeattributeset daemon (myapp_t)) — memasukkan tipe ke dalam atribut; setara typeattribute+type di TE.(roletype system_r myapp_t) — mengizinkan tipe dipakai sebagai proses sistem.(allow ...) — aturan; urutan argumen sama: source, target, class, dan set permission.(typeattributeset cil_gen_require (etc_t)) — mekanisme dependensi CIL: "saya butuh tipe etc_t yang didefinisikan modul lain". Ini setara blok require di TE.Perhatikan bagaimana struktur yang di TE tersebar di beberapa keyword (type, typeattribute, require) menjadi seragam di CIL. Keseragaman ini yang membuat CIL lebih mudah di-parse, di-validasi, dan di-generate oleh tool.
Compiler CIL adalah secilc. Alur yang sama dengan episode 12, tapi kini langsung dari teks CIL:
secilc -o myapp.cil.bin myapp.cil
semodule -i myapp.cil.binCompilation succeeded.Dua hal yang layak dicatat. Pertama, semodule -i juga menerima file .cil mentah — libsemanage akan memanggil secilc di balik layar; namun mengompilasi secara eksplisit memberi kalian kontrol dan umpan balik error yang lebih cepat saat pengembangan. Kedua, secilc menjalankan pemeriksaan neverallow (episode 14) pada saat kompilasi — jadi error "neverallow violation" muncul lebih awal, sebelum policy menyentuh mesin produksi. Ini garansi yang tidak bisa dibeli lewat config management biasa.
Sebuah sistem SELinux produksi tidak terdiri dari satu policy — ia terdiri dari base policy + kumpulan modul. Base policy dibawa distro (dalam selinux-policy-targeted), dan modul-modul (termasuk milik kalian) ditumpuk di atasnya. Lihat hierarkinya:
semodule --list-modules400 myapp cil
400 mydaemon pp
100 base baseStruktur ini adalah arsitektur yang disengaja: base policy berisi fondasi (tipe inti, class, aturan yang hampir selalu berlaku), dan setiap modul menambah satu area tanggung jawab. Karena modul dimuat berlapis, perbaikan tidak pernah memaksa kalian membangun ulang seluruh policy — cukup semodule -i untuk modul yang berubah. Bandingkan dengan mengganti seluruh base policy untuk mengubah satu aplikasi: itulah yang dihindari desain modular ini.
Dua format modul yang perlu kalian pahami perbedaannya:
| Aspek | Modul .pp | Modul .cil |
|---|---|---|
| Sumber | Kompilasi dari .te | Teks CIL langsung |
| Biner? | Ya, terkompilasi | Teks atau .cil.bin |
| Mudah di-review | Sulit (biner) | Mudah (teks) |
| Fitur modern | Terbatas | Penuh (block, macro, optional) |
| Audit/versioning | Sulit | Mudah (diff di git) |
.pp masih ada karena kompatibilitas — banyak modul distro dan tooling lama memproduksinya (checkmodule + semodule_package). Namun untuk policy yang kalian tulis sendiri, CIL adalah pilihan yang lebih baik: bisa di-diff, bisa di-review dalam code review, dan membawa fitur yang tidak dimiliki .pp.
Modul tidak hidup sendiri. Saat modul A memakai tipe milik modul B, muncul dependensi. Di CIL, dependensi dinyatakan eksplisit dengan cil_gen_require, dan CIL memberi alat untuk membuat dependensi opsional — sebuah fitur yang membuat hierarki modular kalian kuat terhadap perubahan:
(optional httpd_integration
(typeattributeset cil_gen_require (httpd_t))
(allow httpd_t myapp_t (tcp_socket (name_connect)))
)
(macro daemon_domain ((type domain_t))
(roletype system_r domain_t)
(allow domain_t self (process (sigchld)))
)Blok optional berisi aturan yang baru diaktifkan jika dependensinya terpenuhi — jika modul yang menyediakan httpd_t tidak terpasang, blok ini di-skip tanpa error, dan saat modul itu datang kemudian, policy tidak perlu dibangun ulang. Sementara macro memungkinkan kalian mendefinisikan template: daemon_domain adalah resep "buat jadi daemon sistem" yang dipanggil berulang dengan tipe berbeda. Dua mekanisme ini, optional dan macro, adalah kunci agar modul kalian fleksibel dan tidak rapuh terhadap penambahan layanan baru.
Warning
Ketergantungan antar modul yang tidak terkontrol menciptakan "kue spageti". Aturan praktis: setiap modul satu aplikasi/satu layanan, dan modul tidak boleh saling memakai tipe internal satu sama lain kecuali lewat interface yang disengaja (atau cil_gen_require). Jika kalian menemukan modul A meng-allow tipe modul B untuk hal yang bukan interaksi nyata, itu alarm desain — pindahkan aturan tersebut ke tempat yang benar sebelum menumpuk lebih banyak.
Policy adalah kode — perlakukan seperti kode, bukan artefak sekali pakai:
myapp, bukan all-apps). Ini memudahkan debugging: semodule -l | grep langsung menunjuk ke tersangka.typeattribute dan macro (lihat contoh di atas) menghindarkan kalian dari menduplikasi aturan yang sama untuk tipe-tipe yang mirip. Satu perubahan di macro, semua pengguna ikut berubah.dontaudit untuk menutup masalah. dontaudit (episode 8) menelan denial diam-diam; itu menyembunyikan sinyal, bukan menyelesaikan akar masalah. Gunakan sebagai pengecualian yang terdokumentasi, bukan pola.secilc (dan selint jika tersedia) sebagai gate CI. Kompilasi yang lolos berarti modul kalian valid; selint memberi peringatan gaya yang mencegah bug desain sejak awal./usr/share/selinux/devel/Makefile — dengan make -f /usr/share/selinux/devel/Makefile myapp.pp kalian mendapatkan kompilasi TE→PP yang konsisten dengan versi distro, sambil tetap menyimpan source di git.Pada episode 17 ini kalian telah mencapai puncak authoring policy: memahami CIL sebagai bahasa tengah yang menjadi titik temu semua policy modern, menulis deklarasi tipe, atribut, dan rule dalam S-expression, mengompilasi dengan secilc (dengan pemeriksaan neverallow bawaan), memahami hierarki policy modular (base + modul), membedakan modul .pp dan CIL beserta kapan memilih masing-masing, mengelola dependensi antar modul dengan optional dan macro, serta menerapkan best practices maintainability untuk policy yang bertahan bertahun-tahun.
Inti yang harus kalian bawa:
secilc mengompilasi dan memeriksa neverallow — validasi gratis sebelum produksi..pp untuk kompatibilitas, CIL untuk masa depan — pilih sesuai konteks.optional dan macro menjaga modul tetap fleksibel terhadap perubahan.Dengan ini kalian resmi menjadi policy engineer — bukan sekadar operator yang menekan tombol. Policy yang kalian bangun kini modular, terdokumentasi, dan teruji. Namun ada pertanyaan yang baru akan terasa saat skala naik: apakah semua hierarki ini tetap efisien ketika policy-nya besar dan mesin-nya ramai? Di episode 18 berikutnya kita masuk ke Performance & Scalability: cache AVC, cache policy userspace, dampak boolean terhadap kecepatan keputusan, serta strategi memelihara policy besar tanpa mengorbankan responsivitas. Sampai jumpa di episode 18!