GNU/Linux >> Belajar Linux >  >> Linux

Menggunakan fitur systemd untuk mengamankan layanan

Semua versi terbaru dari distribusi Linux paling populer menggunakan systemd untuk mem-boot mesin dan mengelola layanan sistem. Systemd menyediakan beberapa fitur untuk mempermudah memulai layanan dan lebih aman. Ini adalah kombinasi yang langka, dan artikel ini menunjukkan mengapa berguna untuk membiarkan systemd mengelola sumber daya dan sandbox suatu layanan.

Pembenaran

Jadi, mengapa kita harus menggunakan systemd untuk sandbox keamanan? Pertama, orang mungkin berargumen bahwa setiap bit dari fungsi ini sudah diekspos melalui alat yang ada dan terkenal, yang dapat ditulis dan digabungkan dengan cara yang sewenang-wenang. Kedua, terutama dalam hal program yang ditulis dalam C/C++ dan bahasa tingkat rendah lainnya, panggilan sistem yang sesuai dapat digunakan secara langsung, mencapai implementasi lean yang disesuaikan secara hati-hati dengan kebutuhan layanan tertentu.

Ada empat alasan utama:

1. Keamanan sulit. Implementasi terpusat di manajer layanan berarti bahwa layanan yang memanfaatkannya dapat disederhanakan secara signifikan. Tidak diragukan lagi, implementasi terpusat ini rumit, tetapi karena penggunaannya yang luas, itu diuji dengan baik. Jika kami menganggap bahwa itu digunakan kembali di ribuan layanan, keseluruhan kompleksitas sistem berkurang.

2. Dasar keamanan bervariasi antar sistem. Systemd menghaluskan perbedaan antara arsitektur perangkat keras, versi kernel, dan konfigurasi sistem.

Fungsionalitas yang menyediakan pengerasan layanan diimplementasikan sejauh mungkin pada sistem tertentu. Misalnya, systemd unit mungkin berisi konfigurasi AppArmor dan SELinux. Yang pertama digunakan pada sistem Ubuntu/Debian, yang kedua pada Fedora/RHEL/CentoOS, dan tidak juga untuk distribusi yang tidak mengaktifkan sistem MAC apa pun. Sisi lain dari fleksibilitas ini adalah bahwa fitur tersebut tidak dapat diandalkan sebagai hanya mekanisme penahanan (atau bahwa layanan tersebut hanya digunakan pada sistem yang mendukung semua fitur yang diperlukan).

3. Keamanan memerlukan mengutak-atik sistem tingkat rendah. Fitur yang disediakan oleh manajer layanan tidak bergantung pada bahasa implementasi layanan, sehingga mudah untuk menulis layanan dalam bahasa tingkat tinggi, misalnya shell atau Python atau apa pun yang nyaman, dan tetap menguncinya.

4. Keamanan membutuhkan hak istimewa. Ini adalah paradoks, tetapi hak istimewa diperlukan untuk menghilangkan hak istimewa. Misalnya, kita sering kali perlu menjadi root untuk menyiapkan namespace pemasangan khusus untuk membatasi tampilan sistem file. Sebagai contoh lain, daemon HTTP sering dimulai sebagai root hanya untuk dapat membuka port bernomor rendah dan port bernomor rendah dibatasi atas nama keamanan. Manajer layanan harus tetap berjalan dengan hak istimewa tertinggi, tetapi layanan tidak boleh, dan penyiapan pengerasan sering kali menjadi satu-satunya alasan untuk meminta hak istimewa yang lebih tinggi. Setiap bug dalam implementasi layanan pada fase ini bisa berbahaya. Dengan memindahkan penyiapan ke pengelola layanan, layanan dapat dimulai tanpa fase awal peningkatan hak istimewa ini.

Untuk menempatkan ini dalam konteks, Fedora 32 yang baru dirilis berisi hampir 1800 file unit berbeda untuk memulai layanan yang ditulis dalam C, C++, Python, Java, Ocaml, Perl, Ruby, Lua, Tcl, Erlang dan seterusnya - dan hanya satu systemd .

[ Butuh lebih banyak tentang systemd? Unduh lembar contekan systemd untuk petunjuk lebih bermanfaat. ]

Beberapa cara yang setara untuk memulai layanan

Paling umum, systemd layanan didefinisikan melalui file unit :file teks dalam format ini yang menyatakan perintah untuk dijalankan dan berbagai setelan. Setelah file unit ini diedit, systemctl daemon-reload harus dipanggil untuk menyodok manajer untuk memuat pengaturan baru. Output dari daemon mendarat di jurnal dan perintah terpisah digunakan untuk melihatnya. Saat menjalankan perintah secara interaktif, semua itu sangat tidak nyaman. systemd-run perintah memberitahu manajer untuk memulai perintah atas nama pengguna dan merupakan alternatif yang bagus untuk penggunaan interaktif. Perintah untuk mengeksekusi ditentukan mirip dengan sudo . Argumen posisi pertama dan semuanya setelahnya adalah perintah yang sebenarnya, dan opsi sebelumnya diinterpretasikan oleh systemd-run diri. systemd-run perintah memiliki opsi untuk menentukan pengaturan tertentu seperti --uid dan --gid untuk pengguna dan grup. -E opsi menyetel variabel lingkungan, sedangkan opsi "tangkap semua" -p menerima pasangan key=value arbitrer yang mirip dengan file unit.

$ systemd-run whoami
Running as unit: run-rbd26afbc67d74371a6d625db78e33acc.service
$ journalctl -u run-rbd26afbc67d74371a6d625db78e33acc.service
journalctl -u run-rbd26afbc67d74371a6d625db78e33acc.service
-- Logs begin at Thu 2020-04-23 19:31:49 CEST, end at Mon 2020-04-27 13:22:35 CEST. --
Apr 27 13:22:18 fedora systemd[1]: Started run-rbd26afbc67d74371a6d625db78e33acc.service.
Apr 27 13:22:18 fedora whoami[520662]: root
Apr 27 13:22:18 fedora systemd[1]: run-rbd26afbc67d74371a6d625db78e33acc.service: Succeeded. 

systemd-run -t menghubungkan input, output, dan aliran kesalahan standar dari perintah ke terminal pemanggilan. Ini bagus untuk menjalankan perintah secara interaktif (perhatikan bahwa proses layanan masih merupakan anak dari manajer).

$ systemd-run -t whoami
Running as unit: run-u53517.service
Press ^] three times within 1s to disconnect TTY.
root 

Lingkungan yang konsisten

Sebuah unit selalu dimulai di lingkungan yang ditentukan dengan cermat. Saat kita memulai unit menggunakan systemctl atau systemd-run , perintah selalu dipanggil sebagai anak dari manajer. Lingkungan shell tidak mempengaruhi lingkungan di mana perintah layanan dijalankan. Tidak semua pengaturan yang dapat ditentukan dalam file unit didukung oleh systemd-run , tetapi sebagian besar, dan selama kita tetap berpegang pada subset itu, pemanggilan melalui file unit dan systemd-run setara. Faktanya, systemd-run membuat file unit sementara dengan cepat.

Misalnya:

$ sudo systemd-run -M rawhide -t /usr/bin/grep PRETTY_NAME= /etc/os-release 

Di sini, sudo berbicara dengan PAM untuk mengizinkan eskalasi hak istimewa, dan kemudian mengeksekusi systemd-run sebagai akar. Selanjutnya, systemd-run membuat koneksi ke mesin bernama rawhide , di mana ia berbicara dengan manajer sistem (PID 1 dalam wadah) melalui dbus. Manajer memanggil grep , yang melakukan tugasnya. grep perintah mencetak ke stdout, yang terhubung ke terminal semu dari mana sudo dipanggil.

Setelan keamanan

Pengguna dan pengguna dinamis

Tanpa basa-basi lagi, mari kita bicara tentang beberapa pengaturan khusus, dimulai dengan primitif paling sederhana dan paling kuat.

Pertama, mekanisme pemisahan hak istimewa tertua, paling dasar, dan mungkin paling berguna:pengguna. Anda dapat menentukan pengguna dengan User=foobar di [Layanan] bagian dari file unit, atau systemd-run -p User=foobar , atau systemd-run --uid=foobar . Ini mungkin tampak jelas—dan di Android, setiap aplikasi memiliki penggunanya sendiri—tetapi di dunia Linux, kami masih memiliki terlalu banyak layanan yang tidak perlu dijalankan sebagai root.

Systemd menyediakan mekanisme untuk membuat pengguna sesuai permintaan. Saat dipanggil dengan DynamicUser=yes , nomor pengguna unik dialokasikan untuk layanan. Nomor ini memutuskan untuk nama pengguna sementara. Tugas ini tidak disimpan di /etc/passwd , tetapi dihasilkan dengan cepat oleh modul NSS setiap kali nomor atau nama yang sesuai ditanyakan. Setelah layanan dihentikan, nomor tersebut mungkin akan digunakan kembali nanti untuk layanan lain.

Kapan pengguna statis biasa digunakan untuk layanan, dan kapan yang dinamis lebih disukai? Pengguna dinamis sangat bagus ketika identitas pengguna bersifat sementara, dan tidak diperlukan integrasi dengan layanan lain dalam sistem. Tetapi ketika kami memiliki kebijakan dalam database untuk mengizinkan akses pengguna tertentu, direktori yang dibagikan dengan grup tertentu, atau konfigurasi lain di mana kami ingin merujuk ke nama pengguna, pengguna dinamis mungkin bukan pilihan terbaik.

Pasang ruang nama

Secara umum, perlu diperhatikan bahwa systemd seringkali hanya membungkus fungsionalitas yang disediakan oleh kernel. Misalnya, berbagai setelan yang membatasi akses ke hierarki sistem file, membuat bagian-bagiannya menjadi hanya-baca atau tidak dapat diakses, diselesaikan dengan mengatur sistem file yang sesuai di ruang nama pemasangan yang tidak dibagikan.

Beberapa pengaturan yang berguna diterapkan seperti ini. Dua yang paling berguna dan umum adalah ProtectHome= dan ProtectSystem= . Yang pertama menggunakan namespace mount yang tidak dibagikan untuk membuat /home baik hanya-baca atau sama sekali tidak dapat diakses. Yang kedua adalah tentang melindungi /usr , /boot , dan /etc .

Pengaturan ketiga yang juga berguna tetapi sangat spesifik adalah PrivateTmp= . Ia menggunakan ruang nama mount untuk membuat direktori pribadi terlihat sebagai /tmp dan /var/tmp untuk layanan. File sementara layanan disembunyikan dari pengguna lain untuk menghindari masalah apa pun karena tabrakan nama file atau izin yang salah.

Tampilan sistem file dapat dikelola pada tingkat direktori individual melalui InaccessiblePaths= , ReadOnlyPaths= , ReadWritePaths= , BindPaths= , dan ReadOnlyBindPaths= . Dua pengaturan pertama memberikan semua atau hanya akses tulis ke bagian hierarki sistem file. Yang ketiga adalah tentang memulihkan akses, yang berguna ketika kita ingin memberikan akses penuh hanya ke beberapa direktori tertentu jauh di dalam hierarki. Dua yang terakhir memungkinkan direktori bergerak, atau, lebih tepatnya berbicara, secara pribadi mengikat-mount mereka di lokasi yang berbeda.

Kembali ke subjek DynamicUser=yes , pengguna sementara tersebut hanya mungkin jika layanan tidak diizinkan untuk membuat file permanen pada disk. Jika file tersebut terlihat oleh pengguna lain, mereka akan ditampilkan sebagai tidak memiliki pemilik, atau lebih buruk lagi, mereka dapat diakses oleh pengguna sementara baru dengan nomor yang sama, yang menyebabkan kebocoran informasi atau eskalasi hak istimewa yang tidak diinginkan. Systemd menggunakan mount namespaces untuk membuat sebagian besar pohon sistem file tidak dapat ditulis ke layanan. Untuk mengizinkan penyimpanan permanen, direktori pribadi dipasang ke pohon sistem file yang terlihat oleh layanan.

Perhatikan bahwa perlindungan tersebut tidak tergantung pada mekanisme kontrol akses file dasar menggunakan kepemilikan file dan topeng izin. Jika sistem file dipasang hanya-baca, bahkan pengguna yang dapat memodifikasi file tertentu berdasarkan izin standar tidak dapat melakukannya sampai sistem file di-remount baca-tulis. Ini memberikan perlindungan terhadap kesalahan dalam manajemen file (bagaimanapun juga, tidak jarang bagi pengguna untuk mengatur topeng izin yang salah sesekali) dan merupakan lapisan strategi pertahanan yang mendalam.

Sumber daya yang diimplementasikan menggunakan ruang nama mount umumnya sangat efisien karena implementasi kernelnya efisien. Overhead saat menyiapkannya biasanya juga dapat diabaikan.

Pembuatan direktori otomatis untuk layanan

Fitur yang relatif baru yang systemd menyediakan layanan adalah manajemen otomatis direktori. Jalur yang berbeda dari sistem file memiliki karakteristik penyimpanan yang berbeda dan tujuan penggunaan, tetapi mereka termasuk dalam beberapa kategori standar. FHS menetapkan bahwa /etc adalah untuk file konfigurasi, /var/cache adalah untuk penyimpanan tidak permanen, /var/lib/ adalah untuk penyimpanan semi permanen, /var/log untuk log, dan /run untuk file yang mudah menguap. Sebuah layanan sering membutuhkan subdirektori di setiap lokasi tersebut. Systemd menyetelnya secara otomatis, seperti yang dikontrol oleh ConfigurationDirectory= , CacheDirectory= , StateDirectory= , LogsDirectory= , dan RuntimeDirectory= pengaturan. Pengguna memiliki direktori tersebut. Direktori runtime dihapus oleh manajer saat layanan berhenti. Ide umumnya adalah untuk mengikat keberadaan aset sistem file tersebut dengan masa pakai layanan. Mereka tidak perlu dibuat sebelumnya dan dibersihkan dengan tepat setelah layanan berhenti berjalan.

$ sudo systemd-run -t -p User=user -p CacheDirectory=foo -p StateDirectory=foo -p RuntimeDirectory=foo -p PrivateTmp=yes ls -ld /run/foo /var/cache/foo /var/lib/foo /etc/foo /tmp/
Running as unit: run-u45882.service
Press ^] three times within 1s to disconnect TTY.
drwxr-xr-x  2 user    user   40 Apr 26 08:21 /run/foo           ← automatically created and removed
drwxr-xr-x  2 user    user 4096 Apr 26 08:20 /var/cache/foo     ← automatically created
drwxr-xr-x. 2 user    user 4096 Nov 13 21:50 /var/lib/foo       ← automatically created
drwxr-xr-x. 2 root    root    4096 Nov 13 21:50 /etc/foo           ← automatically created, but not owned by the user, since the service (usually) shall not modify its own configuration
drwxrwxrwt  2 root    root      40 Apr 26 08:21 /tmp/              ← "sticky bit" is set, but this directory is not the one everyone else sees 

Tentu saja, ketujuh lokasi tersebut (menghitung PrivateTmp= sebagai dua) tidak mencakup kebutuhan setiap layanan, tetapi mereka harus cukup untuk sebagian besar situasi. Untuk kasus lain, pengaturan manual atau konfigurasi yang sesuai di tmpfiles.d selalu menjadi pilihan.

Manajemen direktori otomatis terikat baik dengan DynamicUser= pengaturan dan pengguna yang dibuat secara otomatis, dengan menyediakan layanan yang berjalan sebagai pengguna terpisah dan tidak diizinkan untuk mengubah sebagian besar hierarki sistem file (meskipun izin akses file mengizinkannya). Layanan masih dapat mengakses direktori tertentu dan menyimpan data di sana, tanpa pengaturan apa pun selain konfigurasi file unit.

Misalnya, layanan web Python dapat dijalankan sebagai:

$ systemd-run -p DynamicUser=yes -p ProtectHome=yes -p StateDirectory=webserver --working-directory=/srv/www/content python3 -m http.server 8000 

atau melalui file unit yang setara:

[Service]
DynamicUser=yes
ProtectHome=yes
StateDirectory=webserver
WorkingDirectory=/srv/www/content
ExecStart=python3 -m http.server 8000 

Kami memastikan bahwa layanan berjalan sebagai pengguna sementara tanpa kemampuan untuk mengubah sistem file atau memiliki akses apa pun ke data pengguna.

Pengaturan yang dijelaskan di sini dapat dianggap "tingkat tinggi". Meskipun implementasinya mungkin rumit, konsepnya sendiri mudah dipahami, dan efeknya pada layanan jelas. Ada banyak setelan lain untuk menghilangkan berbagai izin dan kemampuan, mengunci protokol jaringan dan pengaturan kernel, dan bahkan menonaktifkan panggilan sistem individual. Ini berada di luar cakupan artikel singkat ini. Lihat dokumentasi referensi ekstensif.

Memanfaatkan semua ini

Ketika kita memiliki pemahaman yang baik tentang apa yang dilakukan dan dibutuhkan oleh layanan, kita dapat mempertimbangkan hak istimewa apa yang diperlukan dan apa yang dapat kita ambil. Kandidat yang jelas berjalan sebagai pengguna yang tidak memiliki hak istimewa dan membatasi akses ke data pengguna di bawah /home . Semakin kami mengizinkan systemd untuk mengatur segalanya bagi kita (misalnya, dengan menggunakan StateDirectory= dan teman-teman), semakin besar kemungkinan layanan dapat berhasil dijalankan sebagai pengguna yang tidak memiliki hak istimewa. Seringkali layanan membutuhkan akses ke subdirektori tertentu, dan kami dapat mencapainya dengan menggunakan ReadWritePaths= dan pengaturan serupa.

Menambahkan langkah-langkah keamanan dengan cara otomatis apa pun tidak mungkin. Tanpa pemahaman yang baik tentang apa yang dibutuhkan layanan dalam skenario konfigurasi yang berbeda dan untuk operasi yang berbeda, kami tidak dapat mendefinisikan kotak pasir yang berguna. Artinya, sandbox layanan paling baik dilakukan oleh penulis atau pengelolanya.

Evaluasi dan status quo

Jumlah kemungkinan pengaturan besar, dan yang baru ditambahkan dengan setiap rilis systemd . Menjaga dengan itu sulit. Systemd menyediakan alat untuk mengevaluasi penggunaan arahan sandboxing dalam file unit. Hasilnya harus dianggap sebagai petunjuk — lagi pula, seperti yang disebutkan di atas, pembuatan kebijakan keamanan secara otomatis itu sulit, dan evaluasi apa pun hanya menghitung apa yang digunakan dan apa yang tidak, tanpa pemahaman mendalam tentang apa yang penting untuk layanan tertentu.

$ systemd-analyze security systemd-resolved.service   NAME                    DESCRIPTION                                                       EXPOSURE ... ✓ User=/DynamicUser=      Service runs under a static non-root user identity                        ✗ DeviceAllow=            Service has a device ACL with some special devices                0.1 ✓ PrivateDevices=         Service has no access to hardware devices                                 ✓ PrivateMounts=          Service cannot install system mounts                                        PrivateTmp=             Service runs in special boot phase, option does not apply                 ✗ PrivateUsers=           Service has access to other users                                 0.2   ProtectHome=            Service runs in special boot phase, option does not apply                 ✓ ProtectKernelLogs=      Service cannot read from or write to the kernel log ring buffer           ✓ ProtectKernelModules=   Service cannot load or read kernel modules                                ✓ ProtectKernelTunables=  Service cannot alter kernel tunables (/proc/sys, …)                         ProtectSystem=          Service runs in special boot phase, option does not apply                 ✓ SupplementaryGroups=    Service has no supplementary groups                                       ... → Overall exposure level for systemd-resolved.service: 2.1 OK 🙂 $ systemd-analyze security httpd.service   NAME                    DESCRIPTION                                                        EXPOSURE ... ✗ User=/DynamicUser=      Service runs as root user                                          0.4 ✗ DeviceAllow=            Service has no device ACL                                          0.2 ✗ PrivateDevices=         Service potentially has access to hardware devices                 0.2 ✓ PrivateMounts=          Service cannot install system mounts                                   ✓ PrivateTmp=             Service has no access to other software's temporary files              ✗ PrivateUsers=           Service has access to other users                                  0.2 ✗ ProtectHome=            Service has full access to home directories                        0.2 ✗ ProtectKernelLogs=      Service may read from or write to the kernel log ring buffer       0.2 ✗ ProtectKernelModules=   Service may load or read kernel modules                            0.2 ✗ ProtectKernelTunables=  Service may alter kernel tunables                                  0.2 ✗ ProtectSystem=          Service has full access to the OS file hierarchy                   0.2   SupplementaryGroups=    Service runs as root, option does not matter                           ... → Overall exposure level for httpd.service: 9.2 UNSAFE 😨

Sekali lagi, ini tidak berarti bahwa layanan tidak aman, tetapi tidak menggunakan systemd primitif keamanan.

Melihat tingkat distribusi secara keseluruhan:

$ systemd-analyze security '*' 

Kami melihat bahwa sebagian besar layanan mendapat skor sangat tinggi (mis., Buruk). Kami tidak dapat mengumpulkan statistik semacam itu tentang berbagai layanan internal, tetapi tampaknya masuk akal untuk berasumsi bahwa mereka serupa. Tentu saja ada banyak buah yang menggantung rendah, dan menerapkan beberapa sandbox yang relatif sederhana akan membuat sistem kami lebih aman.

Selesai

Membiarkan systemd manage services dan sandboxing bisa menjadi cara yang bagus untuk menambahkan lapisan keamanan ke server Linux Anda. Pertimbangkan untuk menguji konfigurasi di atas untuk melihat apa yang mungkin bermanfaat bagi organisasi Anda.

Dalam artikel ini, kami sengaja menghindari penyebutan jaringan. Ini karena angsuran kedua akan berbicara tentang aktivasi soket dan sandboxing layanan menggunakan jaringan.

[ Jangan lupa untuk memeriksa lembar contekan systemd untuk petunjuk lebih bermanfaat. ]


Linux
  1. Kelola startup menggunakan systemd

  2. Cara Mengelola Layanan Systemd dengan Systemctl di Linux

  3. 10 perintah systemd yang berguna:Referensi

  1. Cara membuat layanan Systemd di Linux

  2. Menambahkan layanan baru ke sistem Linuxd

  3. Gunakan Systemctl untuk mengelola layanan

  1. Cara Mendaftar Layanan Systemd di Linux

  2. Bagaimana menghentikan layanan systemd

  3. Menggunakan variabel di jalur perintah untuk ExecStart di layanan systemd