Belajar AppArmor - Attack Surface Reduction & Policy Refinement
Episode 12 of 23

Belajar AppArmor - Attack Surface Reduction & Policy Refinement

Memperkecil permukaan serang profile dengan aturan deny untuk eksekusi shell dan binary setuid yang tidak dibutuhkan, memangkas abstractions yang terlalu lebar, dan menjalankan siklus complain menuju enforce yang disiplin. Kemudian workflow memperbarui profile saat aplikasi naik versi: mendeteksi regresi, membandingkan dengan diff, dan menguji sebelum kembali ke enforce.

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

Pendahuluan

Di episode 11 kalian sudah mengamankan desktop dan system services: profile untuk aplikasi desktop, integrasi snap dan flatpak, sampai unit systemd. Sekarang kita masuk fase berikutnya yang sering dilupakan: memperkecil permukaan serang. Profile yang berhasil dimuat bukanlah akhir cerita — pertanyaannya, apakah profile itu benar-benar mengecilkan permukaan serang, atau justru sedemikian longgar sehingga hampir setara dengan tidak terkonfinasi?

Banyak profile bawaan distro memang sengaja longgar, karena harus melayani semua pengguna dan semua skenario. Tugas kita sebagai operator adalah menyempitkannya: melarang eksekusi yang tidak diperlukan, menutup jalur privilege escalation, memangkas abstractions, dan memastikan setiap perubahan aplikasi tidak membuat policy usang. Semua itu kita lakukan lewat siklus complain menuju enforce yang disiplin.

Prinsip: Sesuatu yang Dilarang Tidak Bisa Dieksploitasi

Bayangkan profile sebagai daftar pintu yang boleh dibuka di dalam sebuah gedung. Setiap aturan izin tambahan adalah satu pintu ekstra yang suatu hari bisa didobrak. Sebaliknya, sesuatu yang tidak pernah diizinkan sejak awal tidak punya pintu sama sekali — tidak ada yang bisa dieksploitasi dari pintu yang tidak ada.

Inilah inti dari attack surface reduction: mengubah asumsi default. Banyak operator terlalu fokus menambah aturan allow sampai aplikasi "jalan", lalu berhenti di situ. Padahal pekerjaan sesungguhnya dimulai setelahnya: menghapus izin yang tidak pernah dipakai. AppArmor membantu karena mode default deny berlaku di tingkat profile — akses yang tidak ditulis di policy akan ditolak. Sayangnya, abstractions dan aturan generik sering membuka banyak hal sekaligus, sehingga default deny kehilangan maknanya. Di episode ini kita menutupnya satu per satu.

Melarang Eksekusi yang Tidak Perlu

Daemon seperti Nginx atau Postgres tidak pernah membutuhkan shell interaktif, compiler, atau interpreter. Namun sebuah bug remote code execution biasanya berpijak pada kemampuan mengeksekusi program tambahan — dan shell adalah langkah pertamanya. Dengan melarang eksekusi interpreter, kita memaksa attacker bekerja hanya dengan program yang sudah tersedia, yang jauh lebih sulit.

LinuxDeny eksekusi shell dan interpreter
deny /bin/bash x,
deny /bin/dash x,
deny /bin/sh x,
deny /usr/bin/python3 x,
deny /usr/bin/perl x,

Huruf x di sini adalah akses eksekusi yang sudah kalian kenal dari episode 4. Aturan deny bersifat mutlak: deny dievaluasi lebih dulu dan menang atas allow. Artinya, sekalipun sebuah abstractions memberi izin x ke /bin/bash, aturan deny di atas tetap memblokirnya.

Tip

Saat menulis aturan deny, ingat bahwa path symlink ikut dievaluasi. Pada sistem dengan usrmerge, /bin/sh adalah symlink ke /usr/bin/dash — blokir kedua path-nya sekaligus agar tidak ada celah.

Dari sudut pandang attacker, privilege escalation adalah tujuan kedua setelah mendapatkan eksekusi kode. Binary setuid seperti sudo, su, dan pkexec adalah alat utamanya: dijalankan dari proses biasa, tapi berjalan dengan privilege root. Sebuah daemon web yang bisa mengeksekusi sudo adalah bencana yang menunggu terjadi.

LinuxDeny binary setuid untuk daemon
deny /usr/bin/sudo x,
deny /usr/bin/su x,
deny /usr/bin/pkexec x,
deny /usr/bin/passwd x,
deny /usr/lib/openssh/ssh-keysign x,

Alasannya bukan sekadar "daemon tidak boleh pakai sudo", melainkan "daemon tidak perlu punya akses ke mekanisme yang menaikkan privilege sama sekali". Dengan deny di atas, meskipun attacker berhasil mengeksekusi kode di dalam proses, ia tidak bisa memanggil sudo untuk meminta root — jalur escalasi langsung tertutup.

Memangkas Abstractions

Abstractions adalah paket aturan siap pakai yang sudah kita bahas di episode 8. Masalahnya, abstractions dirancang generik. abstractions/base hampir selalu dibutuhkan, tetapi abstractions seperti abstractions/nameservice, abstractions/dbus, atau abstractions/openssl membawa banyak izin yang mungkin tidak pernah terpakai oleh aplikasi kalian. Setiap baris izin yang tidak terpakai adalah permukaan serang yang tidak disengaja.

Kebiasaan yang baik: audit setiap #include di profile. Tanyakan dua hal — apakah abstractions ini benar-benar dibutuhkan, dan bisakah diganti dengan aturan langsung yang lebih sempit? Jika aplikasi hanya perlu resolusi DNS dan parsing konfigurasi, mungkin abstractions/base saja sudah cukup. Jangan takut menguji: hapus satu include, jalankan aplikasi di complain mode, dan lihat apa yang benar-benar terjadi.

Jika beberapa aplikasi berbagi kebutuhan yang sama, buat abstractions kustom yang minimal di /etc/apparmor.d/abstractions/, bukan membiarkan setiap aplikasi memakai abstractions besar yang tidak relevan.

LinuxAbstractions kustom minimal
# /etc/apparmor.d/abstractions/myapp-common
#include <tunables/global>
 
owner @{HOME}/.config/myapp/ r,
owner @{HOME}/.cache/myapp/ rw,
@{PROC}/[0-9]*/fd/ r,

Siklus Complain Menuju Enforce

Tidak ada cara yang lebih baik untuk menemukan izin yang tidak terpakai selain mengamati perilaku nyata. Inilah gunanya siklus complain menuju enforce: complain membiarkan aplikasi jalan sambil mencatat semua denial, sedangkan enforce benar-benar memblokir. Dengan membandingkan apa yang "diminta" aplikasi saat complain dengan apa yang sebenarnya dibutuhkannya setelah diuji, kalian bisa menghapus izin dengan percaya diri.

Siklus complain ke enforce
sudo aa-complain /usr/sbin/nginx
sudo systemctl reload nginx
sudo journalctl -k --grep=apparmor
sudo aa-logprof
sudo aa-enforce /usr/sbin/nginx

Urutan di atas menggambarkan siklus yang utuh:

  1. Turunkan profile ke complain dengan aa-complain.
  2. Jalankan workload nyata — jangan hanya menyalakan service, tapi benar-benar kirim request, reload konfigurasi, dan biarkan rotasi log terjadi.
  3. Baca denial lewat journalctl -k --grep=apparmor atau audit log.
  4. Perbaiki profile, baik manual maupun dengan aa-logprof.
  5. Kembalikan ke enforce dengan aa-enforce.

Warning

Profile yang dibiarkan di complain mode di produksi adalah kebohongan keamanan: semua denial tercatat tapi tidak ada yang diblokir. Buat kebijakan bahwa complain mode hanya boleh bertahan selama jendela pengujian, dan selalu diakhiri dengan enforce.

Workflow Update Aplikasi dan Regresi Profile

Profile adalah kontrak antara aplikasi dan sistem. Saat aplikasi naik versi, kontrak itu bisa berubah: binary baru membaca file baru, memakai library baru, atau menuntut capabilitas baru. Jika profile tidak mengikuti, terjadi regresi: denial baru membanjiri log di enforce mode, atau aplikasi kehilangan fungsi secara diam-diam di complain mode.

Workflow yang disiplin menyelamatkan kalian dari keduanya:

Backup dan diff profile saat upgrade
sudo cp -a /etc/apparmor.d/usr.sbin.nginx /etc/apparmor.d/usr.sbin.nginx.bak
sudo apt upgrade nginx
diff -u /etc/apparmor.d/usr.sbin.nginx.bak /etc/apparmor.d/usr.sbin.nginx

Backup profile sebelum upgrade memberi kalian titik pembanding. Setelah upgrade, diff -u menampilkan perubahan yang dibawa distro — misalnya path binary baru atau include baru. Jangan langsung menyalin hasilnya; baca baris demi baris, dan tanyakan apakah setiap perubahan relevan dengan konfigurasi kalian.

Setelah upgrade, uji di complain mode dengan workload yang mewakili fungsi inti: request berhasil, TLS berfungsi, reload tidak error, log ter-rotate. Baru setelah semua hijau, naikkan kembali ke enforce. Jenis regresi yang perlu kalian kenali:

  • Denial baru di enforce — profile terlalu ketat untuk versi baru aplikasi. Biasanya muncul karena path atau file baru.
  • Fungsi hilang diam-diam di complain — aplikasi tidak lagi meminta akses yang dulu diberi, dan tanpa pengujian fungsional hal ini tidak akan terdeteksi.
  • Profile gagal dimuat saat upgrade — sintaks tidak lagi dikenali parser terbaru, atau konflik dengan profile versi baru yang dibawa distro.

Tip

Jadikan profile bagian dari perubahan yang di-versioning, bukan artefak yang lepas dari aplikasi. Simpan profile di git bersama konfigurasi aplikasi, dan catat versi aplikasi yang bersesuaian. Ini akan sangat membantu saat kalian meninjau keputusan hardening berbulan-bulan kemudian.

Penutup

Pada episode ini kalian telah memperkecil permukaan serang dari tiga arah: melarang eksekusi shell dan interpreter dengan aturan deny, menutup jalur escalation lewat binary setuid, dan memangkas abstractions hingga hanya yang dibutuhkan. Kalian juga menguasai siklus complain menuju enforce sebagai metode untuk menguji dan mempersempit policy, plus workflow update aplikasi yang mendeteksi regresi profile sebelum menjadi masalah di produksi.

Inti yang harus kalian bawa:

  • Aturan deny bersifat mutlak dan menang atas allow — pakai untuk menutup akses yang tidak perlu.
  • Binary setuid adalah jalur escalasi; daemon tidak perlu punya akses ke sana.
  • Abstractions yang tidak terpakai adalah permukaan serang yang tidak disengaja.
  • Complain menuju enforce adalah siklus pengujian, bukan mode permanen.
  • Upgrade aplikasi wajib diikuti review profile dan uji fungsional.

Di episode 13 berikutnya kita mengalihkan fokus dari "apa yang tidak boleh dieksekusi" ke "data apa yang tidak boleh dibaca": mengamankan path sensitif seperti /etc/shadow, direktori ~/.ssh, dan key store, serta memastikan daemon yang menyentuh data sensitif tidak bisa menulis ke lokasi yang tidak perlu. Sampai jumpa di episode 13!

Belajar AppArmor - Attack Surface Reduction & Policy Refinement | Belajar AppArmor