Setiap proses di Linux berjalan dengan identitas tertentu, dan seluruh izin yang dimiliki proses itu mengikuti identitas tersebut. Topik ini membahas prinsip hak istimewa minimum (least privilege), yaitu layanan dijalankan oleh pengguna khusus tanpa kemampuan login, kemudian sisa haknya dipangkas sampai batas yang benar-benar dibutuhkan. Setelah membacanya, pengguna layanan dapat diperiksa di sistem, unit systemd dapat disetel berjalan tanpa hak istimewa berlebih, dan kapabilitas dapat dipahami sebagai pembagian wewenang root yang lebih halus.
Pengguna layanan#
Pengguna layanan (service user) adalah akun yang dibuat untuk menjalankan satu layanan dan tidak dimaksudkan untuk dipakai oleh orang. Cirinya terlihat pada berkas /etc/passwd: UID-nya berada pada rentang akun sistem, direktori home menunjuk ke lokasi aplikasi atau data, dan shell-nya adalah /usr/sbin/nologin, yaitu shell yang menolak sesi interaktif. Pengguna www-data adalah contohnya, dibuat otomatis oleh paket web server sebagai identitas untuk proses penyajian halaman.
# identitas pengguna layanan www-data
id www-data
# baris www-data pada berkas /etc/passwd
grep www-data /etc/passwduid=33(www-data) gid=33(www-data) groups=33(www-data)
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologinKeluaran tersebut menunjukkan UID 33, grup utama yang sama, direktori home /var/www, dan shell nologin. Shell nologin hanya menutup sesi interaktif: proses layanan tetap dapat dijalankan oleh sistem atas nama akun itu, tetapi seseorang tidak dapat duduk di terminal sebagai www-data. Kolom kata sandi akun ini pada /etc/shadow juga berisi nilai yang tidak cocok dengan kata sandi apa pun, sehingga jalur autentikasi kata sandi tertutup sejak akun dibuat. Pembatasan ini meredam dampak pembajakan proses, karena pelaku tidak otomatis mendapatkan shell untuk berpindah-pindah di sistem.
Pembuatan pengguna layanan baru memerlukan hak root, sehingga tidak dapat dieksekusi di sandbox.
# tidak-dijalankan
# pembuatan pengguna memerlukan hak root
# opsi -r membuat akun sistem dan -s menetapkan shell nologin
sudo useradd -r -s /usr/sbin/nologin app-catatanLayanan tanpa hak istimewa berlebih#
Layanan yang berjalan sebagai root memindahkan seluruh wewenang root kepada program itu sendiri. Satu lubang keamanan pada aplikasi, misalnya kesalahan penanganan masukan, berarti pengambilalihan penuh atas mesin, karena proses yang dibajak mewarisi identitas root. Prinsip hak istimewa minimum membalik keadaan tersebut: layanan dijalankan sebagai pengguna khusus dan hanya boleh menulis pada direktori yang memang dipakainya.
systemd menampung ketentuan tersebut pada berkas unit melalui arahan User= beserta sejumlah arahan pengerasan lain. Unit berikut dijadikan contoh untuk aplikasi catatan sederhana yang mendengarkan pada port tinggi.
# /etc/systemd/system/app-catatan.service
[Unit]
Description=Layanan aplikasi catatan
After=network.target
[Service]
User=app-catatan
Group=app-catatan
WorkingDirectory=/opt/app-catatan
StateDirectory=app-catatan
ExecStart=/usr/bin/python3 /opt/app-catatan/server.py
Restart=on-failure
# Pembatasan hak istimewa
NoNewPrivileges=yes
CapabilityBoundingSet=
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
[Install]
WantedBy=multi-user.targetArahan pada bagian [Service] saling melengkapi. User= dan Group= menurunkan proses ke identitas app-catatan, sedangkan StateDirectory=app-catatan membuat /var/lib/app-catatan milik pengguna tersebut sekaligus menandainya sebagai lokasi yang boleh ditulis. NoNewPrivileges=yes melarang proses dan turunannya memperoleh hak baru, termasuk melalui biner setuid yang berjalan sebagai pemiliknya.
CapabilityBoundingSet= yang kosong mengosongkan himpunan kapabilitas yang boleh dipertahankan oleh proses. ProtectSystem=strict menjadikan seluruh hierarki sistem berkas hanya-baca bagi layanan, ProtectHome=yes menyembunyikan direktori home, dan PrivateTmp=yes memberikan /tmp yang terpisah. Aplikasi contoh mendengarkan pada port tinggi sehingga tidak membutuhkan wewenang apa pun untuk berjalan, sebab hanya port di bawah 1024 yang menuntut wewenang khusus.
Penerapan unit tersebut pada server berjalan sebagai prosedur berikut.
# tidak-dijalankan
# systemd tidak berjalan di sandbox dan langkahnya memerlukan root
# kode aplikasi diserahkan kepada pengguna layanan
sudo chown -R app-catatan:app-catatan /opt/app-catatan
# unit dipasang lalu daftar unit systemd diperbarui
sudo cp app-layanan.service /etc/systemd/system/
sudo systemctl daemon-reload
# layanan diaktifkan dan dijalankan
sudo systemctl enable --now app-catatan.serviceKapabilitas: memecah wewenang root#
Wewenang root pada kenyataannya adalah kumpulan kapabilitas terpisah, seperti CAP_NET_BIND_SERVICE untuk mendengarkan port di bawah 1024 dan CAP_NET_RAW untuk membuat soket mentah. Model ini menggantikan pertanyaan tumpul “root atau bukan root” dengan pembagian yang lebih rinci, misalnya sebuah proses yang hanya boleh mengikat port rendah tanpa mewarisi wewenang lain. Kapabilitas tersebut dapat dilekatkan pada berkas eksekusi lewat atribut diperluas (extended attribute) sistem berkas, dan getcap membacanya kembali.
# menghitung berkas berkapabilitas pada direktori biner sandbox
getcap -r /usr/bin /usr/sbin 2>/dev/null | wc -l0Hasil nol berarti tidak ada satu pun berkas di dua direktori tersebut yang membawa kapabilitas berkas pada sandbox latihan ini. Pada pemasangan Debian standar, daftar yang sama biasanya memuat /usr/bin/ping dengan cap_net_raw, karena utilitas ping membutuhkan soket mentah tanpa harus berjalan sebagai root. Pemberiannya menggunakan setcap, yang memerlukan hak root dan karenanya tidak dieksekusi di sandbox.
# tidak-dijalankan
# setcap memerlukan hak root
# aplikasi contoh diberi wewenang mengikat port rendah saja
sudo setcap cap_net_bind_service=+ep /opt/app-catatan/serverAkhiran =+ep memberi kapabilitas pada himpunan efektif dan diizinkan, sehingga berkas tersebut dapat langsung memakai wewenangnya saat dieksekusi. Hubungan setcap dengan unit pada bagian sebelumnya bersifat saling melengkapi: CapabilityBoundingSet= yang kosong memastikan proses layanan tidak dapat mempertahankan kapabilitas apa pun, sekalipun berkas yang dieksekusi oleh proses tersebut membawa kapabilitas berkas.
Mengaudit hak istimewa yang tersebar#
Bit setuid adalah mekanisme eskalasi tertua di Unix: biner yang membawa bit ini berjalan dengan identitas pemilik berkasnya, dan pemilik itu hampir selalu root. Mekanisme ini sah dan diperlukan untuk perintah tertentu, tetapi setiap entri pada daftar berikut adalah permukaan eskalasi lokal yang layak diperiksa secara berkala.
# berkas setuid atau setgid pada direktori biner sistem
find /usr/bin /usr/sbin -xdev -perm /6000 -type f 2>/dev/null | sort/usr/bin/chage
/usr/bin/chfn
/usr/bin/chsh
/usr/bin/expiry
/usr/bin/gpasswd
/usr/bin/mount
/usr/bin/newgrp
/usr/bin/passwd
/usr/bin/su
/usr/bin/sudo
/usr/bin/umount
/usr/sbin/unix_chkpwdDaftar pada sandbox ini seluruhnya berasal dari paket dasar sistem, dan perannya mudah dijelaskan. passwd membutuhkan setuid karena menulis ke /etc/shadow milik root, sedangkan su dan sudo memang perintah eskalasi itu sendiri. Bagi layanan yang sudah dikeraskan pada bagian sebelumnya, biner-biner pada daftar ini tidak terjangkau, karena NoNewPrivileges=yes menutup jalan proses layanan untuk memanfaatkan biner setuid apa pun. Audit sederhananya sebagai berikut: daftar ini dibaca berkala, setiap entri dihubungkan dengan paket yang memasangnya melalui dpkg -S, dan biner asing yang muncul tanpa penjelasan diselidiki terlebih dahulu. Aturan yang sama berlaku untuk getcap, karena daftar yang kosong hari ini bukan jaminan untuk selamanya, dan satu paket baru yang membawa kapabilitas cukup untuk mengubah keadaannya.
Kesalahan umum#
Kesalahan berikut paling sering terjadi saat menerapkan prinsip ini.
- Layanan berhenti setelah pengerasan diterapkan.
ProtectSystem=strictmenjadikan sistem berkas hanya-baca, dan lokasi yang memang perlu ditulis belum dimasukkan, sehinggaStateDirectory=atauReadWritePaths=perlu dilengkapi. Pesan error padajournalctl -u app-catatanbiasanya menunjuk langsung jalur yang ditolak. - Aplikasi gagal mengikat port rendah meskipun sudah bukan root. Port di bawah 1024 membutuhkan
CAP_NET_BIND_SERVICE, dan wewenang itu diberikan melaluisetcapatau kapabilitas pada unit, bukan dengan mengembalikan layanan ke root. Cara menghindarinya yang paling sederhana adalah menjaga aplikasi pada port tinggi seperti contoh unit pada bagian sebelumnya, sehinggasetcaptersisa hanya untuk kasus yang benar-benar membutuhkannya. - Pengguna layanan dibuat tanpa shell nologin. Akun dengan shell biasa membuka pintu login interaktif yang justru ingin ditutup, dan
-s /usr/sbin/nologinpadauseraddmenutupnya sejak awal. - Kapabilitas menghilang setelah pembaruan aplikasi. Berkas biner diganti berkas baru oleh paket, sedangkan atribut kapabilitasnya tidak ikut terbawa, sehingga
setcapperlu diulang setelah setiap pembaruan berkas tersebut.
Ringkasan#
- Pengguna layanan adalah akun tanpa login interaktif, ditandai shell
/usr/sbin/nologinpada/etc/passwd, yang menjadi identitas proses layanan. - Unit systemd menurunkan layanan ke akun tersebut lewat
User=dan memangkas sisanya denganNoNewPrivileges=,CapabilityBoundingSet=, serta arahanProtect*. - Kapabilitas memecah wewenang root menjadi wewenang yang lebih kecil, dan dapat dilekatkan pada berkas dengan
setcaplalu dibaca dengangetcap. - Daftar biner setuid hasil
find -perm /6000adalah permukaan eskalasi lokal yang diaudit secara berkala.
Referensi#
- capabilities(7) — Linux man-pages — daftar kapabilitas Linux beserta aturan pewarisan dan pemeriksaannya.
- systemd.exec — freedesktop.org
— arahan
User=,NoNewPrivileges=,CapabilityBoundingSet=, dan keluargaProtect*pada berkas unit.