Mengapa saya chroot untuk sandboxing untuk keamanan jika aplikasi saya dapat dari awal berjalan di level yang lebih rendah?

14

Saya menulis daemon server HTTP di C (ada alasan mengapa), mengelolanya dengan file unit systemd.

Saya menulis ulang aplikasi yang dirancang 20 tahun yang lalu, sekitar tahun 1995. Dan sistem yang mereka gunakan adalah mereka chroot dan kemudian setuid, dan prosedur standar.

Sekarang dalam pekerjaan saya sebelumnya, kebijakan yang biasa adalah bahwa Anda tidak pernah menjalankan proses apa pun sebagai root. Anda membuat pengguna / grup untuk itu dan lari dari sana. Tentu saja, sistem menjalankan beberapa hal sebagai root, tetapi kami dapat mencapai semua pemrosesan logika bisnis tanpa menjadi root.

Sekarang untuk daemon HTTP, saya bisa menjalankannya tanpa root jika saya tidak chroot di dalam aplikasi. Jadi bukankah lebih aman bagi aplikasi untuk tidak pernah berjalan sebagai root?

Bukankah lebih aman untuk menjalankannya sebagai pengguna mydaemon dari awal? Alih-alih memulai dengan root, chroot, lalu setuid ke pengguna mydaemon?

mur
sumber
3
Anda perlu dijalankan sebagai root untuk menggunakan port 80 atau 443. jika tidak, Anda dapat melakukan apa yang dilakukan tomcat, dan perangkat lunak webapp / web-server lainnya bekerja dan berjalan pada port yang lebih tinggi (katakanlah, 8080, 9090, dll) dan kemudian gunakan apache / nginx untuk mem-proxy koneksi ke perangkat lunak server web Anda, atau menggunakan firewall sistem untuk NAT / meneruskan lalu lintas ke server web Anda dari port 80. Jika Anda tidak membutuhkan port 80 atau 443, atau dapat mem-proxy atau meneruskan koneksi, maka Anda tidak perlu dijalankan sebagai root, di chroot atau sebaliknya.
SnakeDoc
3
@SnakeDoc di Linux tidak lagi benar. Terima kasih untuk capabilities(7).
0xC0000022L
@SnakeDoc Anda dapat menggunakan authbind juga
Abdul Ahad

Jawaban:

27

Tampaknya orang lain telah melewatkan poin Anda, yang bukan alasan mengapa menggunakan root yang diubah, yang tentu saja Anda sudah tahu, atau apa lagi yang dapat Anda lakukan untuk membatasi dæmons, ketika Anda juga jelas tahu tentang berlari di bawah naungan akun pengguna yang tidak memiliki hak pribadi; tetapi mengapa melakukan hal ini di dalam aplikasi . Sebenarnya ada contoh yang cukup tepat tentang alasannya.

Pertimbangkan desain httpdprogram demon dalam paket publik Daniel J. Bernstein. Hal pertama yang dilakukannya adalah mengubah root ke direktori root yang diperintahkan untuk digunakan dengan argumen perintah, lalu menjatuhkan hak istimewa ke ID pengguna dan ID grup unprivileged yang dilewatkan dalam dua variabel lingkungan.

Toolset manajemen demon memiliki alat khusus untuk hal-hal seperti mengubah direktori root dan beralih ke ID pengguna dan grup yang tidak memiliki hak. Runit Gerrit Pape punya chpst. Toolset nosh saya memiliki chrootdan setuidgid-fromenv. S6 Laurent Bercot memiliki s6-chrootdan s6-setuidgid. Perpecahan Wayne Marshall telah runtooldan runuid. Dan seterusnya. Memang, mereka semua memiliki toolset daemontools M. Bernstein sendiri setuidgidsebagai anteseden.

Orang akan berpikir bahwa seseorang dapat mengekstrak fungsionalitas dari httpddan menggunakan alat khusus tersebut. Kemudian, seperti yang Anda bayangkan, tidak ada bagian dari program server yang pernah berjalan dengan hak pengguna super.

Masalahnya adalah bahwa seseorang sebagai konsekuensi langsung harus melakukan lebih banyak pekerjaan secara signifikan untuk mengatur root yang diubah, dan ini memunculkan masalah baru.

Dengan httpdberdirinya Bernstein , satu - satunya file dan direktori yang ada di pohon direktori root adalah yang akan dipublikasikan ke dunia. Tidak ada yang lain di pohon itu sama sekali. Selain itu, tidak ada alasan untuk setiap executable file program gambar ada di pohon itu.

Tapi memindahkan perubahan direktori root keluar ke dalam program rantai-loading (atau systemd), dan tiba-tiba file program gambar untuk httpd, setiap shared library yang banyak, dan setiap file khusus di /etc, /run, dan /devbahwa program loader atau C runtime akses perpustakaan selama inisialisasi program (yang mungkin Anda temukan cukup mengejutkan jika Anda truss/ straceprogram C atau C ++), juga harus ada di root yang diubah. Kalau httpdtidak, tidak bisa dirantai ke dan tidak akan memuat / menjalankan.

Ingat bahwa ini adalah server konten HTTP (S). Ini berpotensi melayani file apa pun (yang dapat dibaca dunia) di root yang diubah. Ini sekarang termasuk hal-hal seperti perpustakaan Anda bersama, program loader Anda, dan salinan berbagai file konfigurasi loader / CRTL untuk sistem operasi Anda. Dan jika oleh beberapa (kebetulan) berarti server konten memiliki akses untuk menulis hal-hal, server yang dikompromikan mungkin dapat memperoleh akses tulis ke gambar program untuk httpddirinya sendiri, atau bahkan program loader sistem Anda. (Ingat bahwa Anda sekarang memiliki dua set paralel /usr, /lib, /etc, /run, dan /devdirektori untuk menjaga aman.)

Tidak satu pun dari ini adalah kasus di mana httpdperubahan root dan drop hak istimewa itu sendiri.

Jadi, Anda telah diperdagangkan dengan sejumlah kecil kode istimewa, yang cukup mudah diaudit dan berjalan tepat di awal httpdprogram, berjalan dengan hak pengguna super; karena memiliki permukaan serangan yang sangat luas dari file dan direktori di dalam root yang diubah.

Inilah sebabnya mengapa tidak sesederhana melakukan segala sesuatu secara eksternal pada program layanan.

Perhatikan bahwa ini adalah fungsionalitas minimum di dalamnya httpd. Semua kode yang melakukan hal-hal seperti melihat dalam database akun sistem operasi untuk ID pengguna dan ID grup untuk dimasukkan ke dalam variabel lingkungan di tempat pertama adalah eksternal untuk httpdprogram, dalam perintah sederhana mandiri yang dapat diaudit seperti envuidgid. (Dan tentu saja itu adalah alat ucspi, sehingga tidak mengandung kode untuk mendengarkan pada port TCP yang relevan (s) atau untuk menerima koneksi, mereka menjadi domain dari perintah seperti tcpserver, tcp-socket-listen, tcp-socket-accept, s6-tcpserver4-socketbinder, s6-tcpserver4d, dan sebagainya.)

Bacaan lebih lanjut

JdeBP
sumber
+1, bersalah seperti yang dituduhkan. Saya menemukan judul dan paragraf terakhir yang ambigu, dan jika Anda benar saya tidak mengerti intinya. Jawaban ini memberikan interpretasi yang sangat praktis. Secara pribadi saya akan secara eksplisit mencatat bahwa harus membangun lingkungan chroot seperti ini adalah upaya ekstra, yang ingin dihindari kebanyakan orang. Tapi 2 poin keamanan di sini sudah dibuat dengan baik.
sourcejedi
Hal lain yang perlu diingat adalah bahwa jika server menjatuhkan privilege sebelum memproses lalu lintas jaringan apa pun, maka kode privilege tidak terkena eksploitasi jarak jauh.
kasperd
5

Saya pikir banyak detail pertanyaan Anda dapat berlaku sama avahi-daemon, yang saya lihat baru-baru ini. (Saya mungkin melewatkan detail lain yang berbeda). Menjalankan avahi-daemon dalam chroot memiliki banyak keuntungan, seandainya avahi-daemon terganggu. Ini termasuk:

  1. itu tidak dapat membaca direktori home pengguna dan mengekstrak informasi pribadi.
  2. tidak dapat mengeksploitasi bug di program lain dengan menulis ke / tmp. Setidaknya ada satu kategori bug seperti itu. Misalnya https://www.google.co.id/search?q=tmp+race+security+bug
  3. itu tidak dapat membuka file soket unix yang berada di luar chroot, yang daemon lain mungkin mendengarkan dan membaca pesan.

Poin 3 bisa sangat bagus ketika Anda tidak menggunakan dbus atau serupa ... Saya pikir avahi-daemon menggunakan dbus, jadi pastikan untuk menjaga akses ke sistem dbus bahkan dari dalam chroot. Jika Anda tidak memerlukan kemampuan untuk mengirim pesan pada sistem dbus, menyangkal kemampuan itu mungkin fitur keamanan yang cukup bagus.

mengelolanya dengan file unit systemd

Perhatikan bahwa jika avahi-daemon ditulis ulang, itu berpotensi dapat memilih untuk mengandalkan systemd untuk keamanan, dan menggunakan mis ProtectHome. Saya mengusulkan perubahan ke avahi-daemon untuk menambahkan perlindungan ini sebagai lapisan tambahan, bersama dengan beberapa perlindungan tambahan yang tidak dijamin oleh chroot. Anda dapat melihat daftar opsi lengkap yang saya usulkan di sini:

https://github.com/lathiat/avahi/pull/181/commits/67a7b10049c58d6afeebdc64ffd2023c5a93d49a

Sepertinya ada lebih banyak batasan yang bisa saya gunakan jika avahi-daemon tidak menggunakan chroot itu sendiri, beberapa di antaranya disebutkan dalam pesan commit. Saya tidak yakin seberapa banyak ini berlaku.

Catatan, perlindungan yang saya gunakan tidak akan membatasi daemon dari membuka file unix socket (poin 3 di atas).

Pendekatan lain adalah menggunakan SELinux. Namun Anda akan mengikat aplikasi Anda ke sub-set distribusi Linux. Alasan saya memikirkan SELinux secara positif di sini, adalah bahwa SELinux membatasi akses yang dimiliki proses pada dbus, dengan cara yang halus. Sebagai contoh, saya pikir Anda sering berharap bahwa systemdtidak ada dalam daftar nama bus yang Anda butuhkan untuk dapat mengirim pesan ke :-).

"Aku bertanya-tanya, apakah menggunakan systemd sandboxing lebih aman daripada chroot / setuid / umask / ..."

Ringkasan: mengapa tidak keduanya? Mari kita decode sedikit di atas :-).

Jika Anda berpikir tentang poin 3, menggunakan chroot memberikan lebih banyak kurungan. ProtectHome = dan teman-temannya bahkan tidak berusaha seketat chroot. (Misalnya, tidak ada daftar nama sistemd opsi blacklist /run, di mana kita cenderung meletakkan file socket unix).

chroot menunjukkan bahwa membatasi akses sistem file bisa menjadi sangat kuat, tetapi tidak semua Linux adalah file :-). Ada opsi systemd yang dapat membatasi hal-hal lain, yang bukan file. Ini berguna jika program ini dikompromikan, Anda dapat mengurangi fitur kernel yang tersedia untuknya, yang mungkin mencoba untuk mengeksploitasi kerentanan masuk. Misalnya avahi-daemon tidak memerlukan soket bluetooth dan saya kira server web Anda juga tidak :-). Jadi jangan berikan itu akses ke keluarga alamat AF_BLUETOOTH. Hanya daftar putih AF_INET, AF_INET6, dan mungkin AF_UNIX, menggunakan RestrictAddressFamilies=opsi.

Silakan baca dokumen untuk setiap opsi yang Anda gunakan. Beberapa opsi menjadi lebih efektif dalam kombinasi dengan yang lain, dan beberapa tidak tersedia pada semua arsitektur CPU. (Bukan karena CPU buruk, tetapi karena port Linux untuk CPU itu tidak dirancang dengan baik. Saya pikir).

(Ada prinsip umum di sini. Lebih aman jika Anda dapat menulis daftar apa yang ingin Anda izinkan, bukan apa yang ingin Anda tolak. Seperti mendefinisikan chroot memberi Anda daftar file yang diizinkan untuk diakses, dan ini lebih kuat daripada mengatakan Anda ingin memblokir /home).

Pada prinsipnya, Anda bisa menerapkan sendiri semua pembatasan yang sama sebelum setuid (). Itu semua hanya kode yang dapat Anda salin dari systemd. Namun, opsi unit systemd harus secara signifikan lebih mudah untuk ditulis, dan karena mereka berada dalam format standar, mereka harus lebih mudah dibaca dan ditinjau.

Jadi saya bisa sangat merekomendasikan hanya membaca bagian sandboxing man systemd.execpada platform target Anda. Tetapi jika Anda ingin desain yang paling aman mungkin, saya tidak akan takut untuk mencoba chroot(dan kemudian menjatuhkan roothak istimewa) dalam program Anda juga . Ada pengorbanan di sini. Penggunaan chrootmemberi beberapa kendala pada desain keseluruhan Anda. Jika Anda sudah memiliki desain yang menggunakan chroot, dan tampaknya melakukan apa yang Anda butuhkan, itu terdengar sangat hebat.

sourcejedi
sumber
+1 terutama untuk saran sistemd.
mattdm
Saya belajar sedikit dari jawaban Anda, jika stack overflow diperbolehkan beberapa jawaban saya akan menerima Anda juga. Saya bertanya-tanya, apakah menggunakan systemd sandboxing lebih aman daripada chroot / setuid / umask / ...
mur
@ Ya senang Anda menyukainya :). Itu adalah respons yang sangat alami untuk jawaban saya. Jadi saya telah memperbaruinya lagi, untuk mencoba dan menjawab pertanyaan Anda.
sourcejedi
1

Jika Anda dapat mengandalkan systemd, maka memang lebih aman (dan lebih sederhana!) Untuk meninggalkan sandboxing ke systemd. (Tentu saja, aplikasi juga dapat mendeteksi apakah telah diluncurkan sandbox oleh systemd atau tidak, dan sandbox itu sendiri jika masih root.) Setara dengan layanan yang Anda gambarkan adalah:

[Service]
ExecStart=/usr/local/bin/mydaemon
User=mydaemon-user
RootDirectory=...

Tapi kita tidak harus berhenti di situ. systemd juga dapat melakukan banyak sandboxing lain untuk Anda - berikut adalah beberapa contoh:

[Service]
# allocate separate /tmp and /var/tmp for the service
PrivateTmp=yes
# mount / (except for some subdirectories) read-only
ProtectSystem=strict
# empty /home, /root
ProtectHome=yes
# disable setuid and other privilege escalation mechanisms
NoNewPrivileges=yes
# separate network namespace with only loopback device
PrivateNetwork=yes
# only unix domain sockets (no inet, inet6, netlink, …)
RestrictAddressFamilies=AF_UNIX

Lihat man 5 systemd.execlebih banyak arahan dan deskripsi lebih rinci. Jika Anda membuat soket daemon Anda dapat diaktifkan ( man 5 systemd.socket), Anda bahkan dapat menggunakan opsi yang berhubungan dengan jaringan: satu-satunya tautan layanan ke dunia luar adalah soket jaringan yang diterima dari systemd, itu tidak akan dapat terhubung ke hal lain. Jika ini adalah server sederhana yang hanya mendengarkan pada beberapa port dan tidak perlu terhubung ke server lain, ini bisa bermanfaat. (Opsi terkait sistem file juga dapat membuat RootDirectoryusang, menurut saya, jadi mungkin Anda tidak perlu repot mengatur direktori root baru dengan semua biner dan perpustakaan yang diperlukan lagi.)

Versi systemd yang lebih baru (sejak v232) juga mendukung DynamicUser=yes, di mana systemd akan secara otomatis mengalokasikan pengguna layanan untuk Anda hanya untuk runtime layanan. Ini berarti Anda tidak perlu mendaftarkan pengguna permanen untuk layanan, dan bekerja dengan baik selama layanan tidak menulis ke lokasi sistem file lain dibandingkan StateDirectory, LogsDirectorydan CacheDirectory(yang Anda juga dapat mendeklarasikan dalam file Unit - lihat man 5 systemd.exec, lagi - dan systemd yang kemudian akan mengatur, berhati-hati untuk menempatkan mereka dengan benar ke pengguna dinamis).

Lucas Werkmeister
sumber