Terjemahan artikel disiapkan khusus untuk siswa kursus Administrator Linux .
Kemampuan yang digunakan semakin banyak berkat sebagian besar untuk SystemD, Docker, dan orkestra seperti Kubernetes. Tetapi, bagi saya, dokumentasinya agak rumit untuk dipahami, dan beberapa bagian dari implementasi privilege bagi saya ternyata agak membingungkan, jadi saya memutuskan untuk membagikan pengetahuan saya saat ini dalam artikel singkat ini.

Tautan hak istimewa yang paling penting adalah halaman manual
kapabilitas (7) . Tapi dia tidak cocok untuk kenalan awal.
Kemampuan proses
Hak pengguna biasa sangat terbatas, sementara hak pengguna "root" sangat luas. Meskipun proses yang berjalan sebagai "root" seringkali tidak memerlukan semua hak akses root.
Untuk mengurangi hak akses root, izin POSIX menyediakan cara untuk membatasi grup operasi sistem yang diistimewakan sehingga proses dan turunannya diizinkan untuk melakukan. Intinya, mereka membagi semua hak "root" menjadi satu set hak istimewa yang terpisah. Gagasan kapabilitas dijelaskan pada tahun 1997 dalam konsep POSIX 1003.1e.
Di Linux, setiap proses (tugas) memiliki
lima angka 64-bit (set) yang mengandung bit izin (sebelum Linux 2.6.25 jumlahnya 32-bit), yang dapat dilihat dalam
/ proc / <pid> / status
.
CapInh: 00000000000004c0 CapPrm: 00000000000004c0 CapEff: 00000000000004c0 CapBnd: 00000000000004c0 CapAmb: 0000000000000000
Angka-angka ini (ditampilkan di sini dalam notasi heksadesimal) adalah bitmap di mana set izin diwakili. Berikut nama lengkap mereka:
- Warisan - Izin yang bisa diwariskan oleh keturunan
- Diizinkan - Izin yang dapat digunakan oleh tugas.
- Efektif - izin efektif saat ini
- Bounding - Sebelum Linux 2.6.25, set pembatas adalah atribut seluruh sistem yang umum untuk semua utas, dirancang untuk menggambarkan set di luar yang izin tidak dapat diperluas. Saat ini satu set untuk setiap tugas dan hanya bagian dari logika eksekusi, detail di bawah ini.
- Ambient (eksternal sejak Linux 4.3) - ditambahkan untuk memudahkan memberikan izin non-root kepada pengguna, tanpa menggunakan setuid atau izin file (lebih lanjut tentang itu nanti).
Jika suatu tugas meminta operasi istimewa (misalnya, mengikat ke port <1024), kernel memeriksa set batas saat ini untuk
CAP_NET_BIND_SERVICE . Jika sudah diinstal, maka operasi berlanjut. Kalau tidak, operasi ditolak dengan EPERM (operasi tidak diizinkan).
CAP_
dalam kode sumber kernel dan diberi nomor secara berurutan, jadi
CAP_NET_BIND_SERVICE
, sama dengan 10, berarti bit 1 << 10 = 0x400 (ini adalah digit heksadesimal “4” dalam contoh saya sebelumnya).
Daftar hak istimewa yang dapat dibaca oleh manusia yang saat ini didefinisikan dapat ditemukan di halaman manual
kemampuan saat ini
(7) (daftar di sini hanya untuk referensi).
Selain itu, ada perpustakaan libcap untuk menyederhanakan pemeriksaan manajemen dan otorisasi. Selain
API perpustakaan , paket menyertakan utilitas
capsh , yang, di antaranya, memungkinkan Anda untuk menunjukkan kredensial Anda.
Ada beberapa poin membingungkan di sini:
- Current - menampilkan hak istimewa yang efektif, warisan dan tersedia dari proses capsh dalam format cap_to_text (3) . Dalam format ini, hak terdaftar sebagai grup izin
“capability[,capability…]+(e|i|p)”
, di mana “e”
berarti efektif, “i”
diwariskan, dan “p”
tersedia. Daftar ini tidak dipisahkan oleh simbol “,”
, seperti yang mungkin Anda tebak (cap_setgid+eip, cap_setuid+eip)
. Koma membagi izin dalam satu grup tindakan. Daftar grup tindakan yang sebenarnya kemudian dipisahkan oleh spasi. Contoh lain dengan dua grup tindakan adalah “= cap_sys_chroot+ep cap_net_bind_service+eip”
. Dan juga dua grup tindakan berikut “= cap_net_bind_service+e cap_net_bind_service+ip”
akan menyandikan nilai yang sama dengan satu “cap_net_bind_service+eip”
. - Bounding set / Ambient set . Untuk lebih membingungkan, dua baris ini hanya berisi daftar izin yang ditentukan dalam set ini, dipisahkan oleh spasi. Format cap_to_text tidak digunakan di sini, karena tidak mengandung set izin yang tersedia, efektif dan diwariskan, tetapi hanya satu set (terikat / ambient).
- Securebits : menampilkan flag securebits dari tugas dalam format desimal / heksadesimal / dalam Verilog (ya, semua orang mengharapkannya di sini, dan ini sangat jelas dari titik bahwa setiap administrator sistem memprogram
FPGA
dan ASIC
mereka sendiri). Berikut ini adalah status keamanan. Bendera sebenarnya didefinisikan sebagai SECBIT_*
di securebits.h , dan juga dijelaskan dalam kemampuan (7) . - Utilitas ini tidak memiliki tampilan informasi "NoNewPrivs" , yang dapat dilihat di
/ proc / <pid> / status
. Ini disebutkan hanya dalam prctl (2), meskipun secara langsung mempengaruhi hak ketika digunakan bersama dengan izin file (lebih terinci di bawah). NoNewPrivs dideskripsikan sebagai berikut: “Dengan no_new_privs
ke 1, execve (2) berjanji untuk tidak memberikan hak istimewa kepada apa yang tidak dapat dilakukan tanpa memanggil execve (2) (misalnya, memproses bit set-user-ID
, bit set-group-ID
dan menonaktifkan pemrosesan izin file) . Setelah instalasi, atribut no_new_privs
tidak dapat diatur ulang. Nilai dari atribut ini diwarisi oleh keturunan yang dibuat melalui fork (2) dan clone (2) dan disimpan melalui execve (2). " Kubernetes menetapkan flag ini ke 1 ketika allowPrivilegeEscalation salah di dalam konteks securityContext.
Ketika memulai proses baru melalui execve (2), izin untuk proses anak dikonversi menggunakan rumus yang ditentukan dalam
kemampuan (7) :
P'(ambient) = (file is privileged) ? 0 : P(ambient) P'(permitted) = (P(inheritable) & F(inheritable)) | (F(permitted) & P(bounding)) | P'(ambient) P'(effective) = F(effective) ? P'(permitted) : P'(ambient) P'(inheritable) = P(inheritable) [ie, unchanged] P'(bounding) = P(bounding) [ie, unchanged] where: P() denotes the value of a thread capability set before the execve(2) - execve(2) P'() denotes the value of a thread capability set after the execve(2) - execve(2) F() denotes a file capability set -
Aturan-aturan ini menjelaskan tindakan yang dilakukan untuk setiap bit di semua set izin (ambient / diizinkan / efektif / inherit / terikat). Sintaks C standar digunakan (& - untuk logika AND, | - untuk logika OR). P 'adalah proses anak. P adalah proses saat ini memanggil execve (2). F adalah apa yang disebut "izin file" dari file yang diluncurkan melalui execve.
Selain itu, suatu proses dapat secara program mengubah set yang diwarisi, diakses, dan efisien dengan libcap setiap saat sesuai dengan aturan berikut:
- Jika penelepon tidak memiliki
CAP_SETPCAP
, set yang diwarisi baru harus menjadi subset dari P (diwarisi) & P (tersedia) - (dengan Linux 2.6.25) Set yang diwarisi baru harus menjadi subset dari P (diwarisi) & P (membatasi)
- Set yang tersedia baru harus menjadi bagian dari P (tersedia)
- Set efisien baru harus menjadi bagian dari P (efektif)
Izin file
Terkadang pengguna dengan serangkaian hak terbatas perlu menjalankan file yang membutuhkan lebih banyak hak istimewa. Sebelumnya ini dicapai dengan mengatur bit setuid (
chmod + s ./executable
) dalam file biner. File seperti itu, jika itu milik root, akan memiliki hak root penuh ketika dijalankan oleh pengguna mana pun.
Tetapi mekanisme ini memberikan terlalu banyak hak istimewa untuk sebuah file, jadi izin POSIX telah menerapkan konsep yang disebut "izin file". Mereka disimpan sebagai
atribut file yang diperluas yang disebut "security.capability", jadi Anda memerlukan sistem file dengan dukungan untuk atribut yang diperluas (ext *, XFS, Raiserfs, Brtfs, overlay2, ...). Untuk mengubah atribut ini,
CAP_SETFCAP
izin
CAP_SETFCAP
(dalam set izin proses yang tersedia).
$ getfattr -m - -d `which ping`
Kasus dan komentar khusus
Tentu saja, dalam kenyataannya, semuanya tidak begitu sederhana, dan ada beberapa kasus khusus yang dijelaskan dalam halaman manual
kemampuan (7) . Mungkin yang paling penting dari mereka adalah:
- Bit setuid dan izin file diabaikan jika NoNewPrivs diinstal atau sistem file di-mount dengan nosuid atau proses panggilan execve dilacak oleh ptrace. Izin file juga diabaikan ketika kernel melakukan boot dengan opsi
no_file_caps
. - File "bodoh" (kapabilitas-bisu) adalah file biner yang dikonversi dari file setuid ke file dengan izin file, tetapi tanpa mengubah kode sumbernya. File-file semacam itu sering diperoleh dengan menyetel izin + ep padanya, misalnya,
“setcap cap_net_bind_service+ep ./binary”
. Bagian yang penting adalah "e" - efektif. Setelah dieksekusi, izin ini akan ditambahkan ke yang tersedia dan yang sudah ada, sehingga yang dapat dieksekusi akan siap untuk menggunakan operasi istimewa. Sebaliknya, file "kapabilitas-cerdas" yang menggunakan libcap atau fungsi serupa dapat menggunakan cap_set_proc (3) (atau capset ) untuk mengatur bit "efektif" atau "diwariskan" kapan saja jika izin itu sudah ada di " kit ”yang terjangkau. Oleh karena itu, " setcap cap_net_bind_service+p ./binary”
akan cukup untuk file "pintar", karena itu akan dapat mengatur izin yang diperlukan dalam set yang efektif sendiri sebelum menjalankan operasi istimewa. Lihat kode contoh . - File dengan setuid-root terus berfungsi, memberikan semua hak istimewa root saat pengguna memulai sebagai non-root. Tetapi jika mereka memiliki izin file yang ditetapkan, maka hanya mereka yang akan diberikan. Anda juga dapat membuat file setuid dengan set izin kosong, yang akan membuatnya berjalan sebagai pengguna dengan UID 0 tanpa izin apa pun. Ada kasus-kasus khusus untuk pengguna root ketika menjalankan file dengan setuid-root dan pengaturan berbagai bendera keamanan (lihat man).
- Set terikat membatasi izin yang tersedia, tetapi tidak yang diwariskan. Ingat P '(tersedia) = F (tersedia) & P (membatasi). Jika streaming memiliki izin di set yang diwarisi yang tidak di set yang membatasi, maka ia masih bisa mendapatkan izin ini di set yang tersedia dengan menjalankan file yang memiliki izin di set yang diwarisi - P '(tersedia) = P ( diwarisi) & F (diwarisi).
- Menjalankan program yang mengubah UID atau GID melalui bit set-user-ID, set-group-ID, atau menjalankan program yang mengatur izin file apa pun akan menghapus set ambient . Izin ditambahkan ke set sekitarnya menggunakan
PR_CAP_AMBIENT
prctl . Izin ini harus sudah ada dalam set proses yang dapat diakses dan diwariskan . - Jika suatu proses dengan UID selain 0 dijalankan mengeksekusi (2) , maka semua hak dalam set yang tersedia dan aktif akan dihapus.
- Jika
SECBIT_KEEP_CAPS
(atau SECBIT_NO_SETUID_FIXUP
lebih luas) tidak disetel, dan mengubah UID dari 0 menjadi bukan nol menghapus semua izin dari set yang diwarisi, dapat diakses, dan efektif .
Jadi ...
Jika wadah nginx resmi, ingress-nginx atau Anda sendiri berhenti atau memulai kembali dengan kesalahan:
bind() to 0.0.0.0:80 failed (13: Permission denied)
... ini berarti ada upaya untuk mendengarkan pada port 80 sebagai pengguna (bukan 0) yang tidak memiliki hak, dan tidak ada
CAP_NET_BIND_SERVICE
dalam
CAP_NET_BIND_SERVICE
izin saat ini. Untuk mendapatkan hak-hak ini, Anda harus menggunakan xattr dan set (menggunakan
setcap
) untuk izin file nginx setidaknya
cap_net_bind_service+ie
. Izin file ini akan digabungkan dengan set lawas (ditentukan dengan set pembatas dari pod SecurityContext / kapabilitas / tambahkan / NET_BIND_SERVICE), dan juga akan ditempatkan dalam set izin yang tersedia. Hasilnya adalah
cap_net_bind_service+pie
.
Ini semua berfungsi selama securityContext / allowPrivilegeEscalation disetel ke true dan driver penyimpanan buruh pelabuhan / rkt (lihat dokumentasi buruh pelabuhan) mendukung xattrs.
Jika nginx pintar sehubungan dengan izin, maka
cap_net_bind_service+i
akan cukup. Kemudian dia bisa menggunakan libcap untuk memperluas hak dari set yang tersedia menjadi efektif. Setelah menerima
cap_net_bind_service+pie
hasilnya.
Selain menggunakan xattr, satu-satunya cara untuk mendapatkan
cap_net_bind_service
dalam wadah non-root adalah membiarkan Docker mengatur kemampuan eksternal (kemampuan ambient). Tetapi pada April 2019, ini belum
dilaksanakan .
Contoh kode
Berikut adalah contoh kode menggunakan libcap untuk menambahkan
CAP_NET_BIND_SERVICE
ke set izin yang efisien. Dibutuhkan
CAP_BIND_SERVICE+p
izin untuk file biner.
Referensi (bahasa Inggris):