Salah satu pertanyaan yang paling sering saya tanyakan tentang Podman rootless adalah bagaimana men-debug masalah dengan volume yang dipasang ke dalam wadah. Pertanyaan ini sangat sulit. Dalam banyak hal, menjalankan Podman tanpa root hampir identik dengan menjalankannya sebagai root . Sayangnya, ini tidak selalu benar, dan volume adalah salah satu area dengan perbedaan paling signifikan. Di sini, saya akan menjelaskan secara rinci apa perbedaan ini, jenis kesalahan apa yang dapat ditimbulkannya, dan bagaimana Anda dapat mengatasinya. Untuk memulai, kita memerlukan beberapa informasi latar belakang tentang cara kerja container tanpa root, dimulai dengan salah satu fitur paling mendasar dari Podman tanpa root:Ruang nama pengguna .
Ruang nama pengguna
Salah satu fitur keamanan dasar container adalah ruang nama kernel Linux. Namespace adalah cara mengisolasi proses (atau kelompok proses) dari sistem lainnya dengan membatasi apa yang dapat dilihatnya. Ada banyak ruang nama yang berbeda, masing-masing dengan efek yang berbeda. Misalnya, ruang nama PID membatasi PID yang dapat dilihat oleh suatu proses—hanya PID dalam ruang nama yang terlihat dan diberi nomor secara independen dari host. Proses masih memiliki PID di host juga, jadi PID 2000 di host bisa menjadi PID 1 di namespace. Pada tulisan ini, namespace yang disediakan kernel adalah Mount, PID, Network, IPC, UTS, User, cgroup, dan time, masing-masing mengisolasi aspek yang berbeda dari sistem; yang paling kami pedulikan untuk blog ini adalah ruang nama pengguna.
Ruang nama pengguna mengisolasi pengguna dan grup yang tersedia dalam wadah dari yang tersedia untuk sistem host. Ruang nama pengguna bekerja dengan memetakan pengguna di wadah ke pengguna di host. Misalnya, kami dapat memetakan pengguna 0 hingga 1000 dalam wadah ke pengguna 100000 hingga 101000 pada host (grup juga dipetakan dengan cara yang sama, tetapi kami akan fokus pada pengguna untuk kesederhanaan). Pemetaan ini bekerja sangat mirip dengan namespace PID yang kami jelaskan di atas, tetapi dengan pengguna.
Dari host, semua akses dari root di penampung (UID 0) akan muncul dari UID 100000. Di dalam penampung, file apa pun yang dimiliki oleh pengguna 100000 di host akan muncul sebagai milik UID 0 (root ). Pertanyaan yang menarik adalah apa yang terjadi pada pengguna yang tidak dipetakan ke dalam wadah—bagaimana jika saya memasang volume yang dimiliki oleh pengguna 1001 di host ke dalam wadah menggunakan ruang nama pengguna yang saya jelaskan? Kernel, dalam hal ini, akan memetakan UID atau GID yang tidak valid di namespace ke UID/GID 65534, pengguna dan grup yang disebut nobody , yang bukan grup normal.
Masih dimungkinkan untuk berinteraksi dengan file yang dimiliki oleh tidak seorang pun jika izin mengizinkan (mis., Anda dapat membaca file yang dimiliki oleh tidak seorang pun yang dapat dibaca dunia dan menulis ke file itu jika dapat ditulis dunia), tetapi Anda tidak dapat mengubah kepemilikannya. Ruang nama pengguna juga memberikan versi terbatas dari kemampuan khusus yang biasanya hanya tersedia untuk root —contoh tipikalnya adalah bahwa ruang nama pengguna dapat memasang jenis sistem file tertentu, seperti tmpfs.
Ruang nama pengguna sangat berguna karena memungkinkan kita bertindak sebagai root dalam wadah tanpa benar-benar menjadi root pada sistem. Kita dapat menggunakan ruang nama pengguna untuk memisahkan wadah dari pengguna yang berbeda pada sistem multi-penyewa— wadah satu pengguna akan dijalankan sebagai UID 10000 hingga 10999, yang lain sebagai 11000 hingga 11999, dan seterusnya. Dalam setiap wadah, tampaknya aplikasi tersebut root , dan melalui kemampuan terbatas yang diberikan, ia dapat melakukan operasi yang paling umum (misalnya, menginstal paket).
Namun, misalkan aplikasi berhasil keluar dari wadah. Dalam hal ini, itu tidak berjalan sebagai root pada sistem dan tidak berjalan sebagai UID dan GID yang sama dengan container milik pengguna lain—kemampuan untuk menyerang bagian berbeda dari sistem sangat terbatas.
Namun, adopsi mereka dibatasi oleh batasan teknis—terutama, fakta bahwa hingga saat ini, tidak ada cara untuk memetakan kembali UID dan GID di tingkat sistem file. Untuk memiliki wadah yang menggunakan UID 10000 hingga 10999, kami harus membuat salinan gambarnya dan kemudian chown
setiap UID di gambar tersebut dengan menambahkan 10.000 ke UID yang ada. Ini chown
bisa sangat lambat dan (dalam banyak sistem file) secara dramatis meningkatkan jumlah ruang yang dibutuhkan.
[ Memulai container? Lihat kursus gratis ini. Menyebarkan aplikasi kemas:Tinjauan teknis. ]
Kontainer tanpa root
Di mana ruang nama pengguna menjadi sangat berguna dan populer adalah wadah tanpa akar. Pengguna non-root di Linux hanya memiliki akses ke satu UID dan GID (milik mereka sendiri). Namun, container berharap untuk mengakses lebih dari satu pengguna dan grup—banyak file dalam gambar container tidak dimiliki oleh root , dan aplikasi akan sering dijalankan sebagai pengguna non-root dalam container untuk menerapkan pemisahan hak istimewa di dalam container.
Untuk beberapa lingkungan (komputasi kinerja tinggi, HPC, menjadi salah satu yang terkenal), hanya memiliki satu pengguna dan grup dalam wadah dapat diterima (bahkan diinginkan). Untuk sebagian besar lingkungan lain, satu pengguna dan grup merupakan batasan parah untuk kegunaan wadah. Kami dapat menggunakan ruang nama pengguna untuk mendapatkan pengguna dan grup tambahan yang kami perlukan untuk bertindak seperti wadah biasa.
Namun, kami membutuhkan hak istimewa yang lebih tinggi untuk mencapai pengguna dan grup tambahan ini. Inilah yang newuidmap
dan newgidmap
executable (dan /etc/subuid
dan /etc/subgid
config yang mereka baca) lakukan—mereka memberi kami akses ke blok pengguna dan grup, yang kemudian dipetakan ke dalam ruang nama pengguna untuk digunakan wadah tanpa akar. Keterbatasan ruang nama pengguna terkait dukungan sistem file masih berlaku sampai tingkat tertentu tetapi dikurangi dengan hanya memiliki satu set UID dan GID untuk digunakan setiap pengguna.
Yang juga perlu diperhatikan adalah fakta bahwa kernel akan secara otomatis menangani chown
operasi untuk kami jika kami membongkar gambar di dalam ruang nama pengguna. Pergeseran namespace oleh pengguna memastikan bahwa UID yang benar telah ditetapkan saat pembuatan alih-alih mengharuskan runtime container untuk menyetelnya secara manual.
Kemampuan tambahan dari ruang nama pengguna juga penting untuk beberapa hal yang dibutuhkan wadah tanpa root—tanpa kemampuan untuk memasang sistem file FUSE dan tmpfs, wadah tanpa akar akan jauh lebih terbatas (sampai-sampai hampir tidak dapat digunakan).
Ruang nama pengguna di Podman
Sekarang setelah kita memahami cara kerja ruang nama pengguna secara umum, mari kita bahas bagaimana penerapannya di Podman tanpa root.
Semua container Podman tanpa root dijalankan di ruang nama pengguna, meskipun pengguna tidak memiliki lebih dari satu UID dan GID yang tersedia. Semua wadah pengguna berbagi ruang nama pengguna tunggal, dibuka oleh proses jeda tanpa akar. Namespace biasanya dipangkas oleh kernel ketika tidak ada lagi proses di dalamnya, jadi menjaga proses di sekitar yang tidak melakukan apa-apa selain tidur dan tidak pernah keluar akan membuat namespace pengguna tetap hidup.
Hal pertama yang dilakukan proses Podman rootless adalah bergabung dengan namespace pengguna rootless (atau membuat namespace baru dan menjeda proses jika belum ada). Sebagai bagian dari pembuatan ruang nama pengguna, Podman akan menjalankan newuidmap
dan newgidmap
executable untuk memberikan UID dan GID tambahan yang telah dialokasikan pengguna di /etc/subuid
dan /etc/subgid
(jumlah default yang diberikan pada pembuatan pengguna masing-masing adalah 65536). Anda dapat melihat pemetaan pengguna yang tersedia di podman info
perintah, di idMappings bidang:
mheon@podman-rhel8-test $ podman info
idMappings:
gidmap:
- container_id: 0
host_id: 1000
size: 1
- container_id: 1
host_id: 100000
size: 65536
uidmap:
- container_id: 0
host_id: 1000
size: 1
- container_id: 1
host_id: 100000
size: 65536
Harap dicatat bahwa namespace pengguna rootless tidak dibuat ulang secara default jika /etc/subuid
dan /etc/subgid
file diubah; ini dilakukan dengan menjalankan podman system migrate
memerintah. Jika Anda telah mengedit file-file ini dan Podman tampaknya tidak mengenali perubahan Anda, jalankan perintah ini.
Semua perintah Podman rootless dijalankan dalam ruang nama pengguna rootless yang dibuat untuk memastikan bahwa kami memiliki pemetaan dan hak istimewa pengguna yang benar. Bahkan perintah informasi sederhana, seperti podman info
, memerlukan ruang nama pengguna tanpa akar. Dengan demikian, ruang nama pengguna yang tidak berfungsi adalah penghalang untuk Podman tanpa akar. Untungnya, ini tidak sering terjadi. Ketika ya, kami biasanya menemukan bahwa itu disebabkan oleh izin file yang tidak memadai di newuidmap
dan/atau newgidmap
binari pada sistem (biasanya tidak memiliki kemampuan file). Menginstal ulang paket yang berisi mereka (disebut shadow-utils pada RHEL, CentOS, dan Fedora) biasanya akan menyelesaikan ini.
Setelah namespace pengguna rootless dibuat, kita dapat mulai menjalankan container. Menggunakan volume dengan wadah ini adalah titik pertama di mana sebagian besar pengguna akan menemukan perbedaan praktis antara Podman root dan rootless. Pengguna akan menjalankan wadah dengan volume terpasang dan segera menemukan bahwa wadah tidak dapat mengakses file dalam volume, meskipun semuanya tampaknya diatur dengan benar.
Sebagai contoh, mari kita periksa perintah Podman sederhana:
podman run --user 1000:1000 -v /home/mheon/data:/data:Z ubi8 sh
Perintah ini dijalankan oleh pengguna saya, mheon , dengan UID dan GID disetel ke 1000 (pengguna yang sama dengan wadah yang diinstruksikan untuk digunakan). Pengguna saya telah dialokasikan 65536 UID dan GID mulai dari UID/GID 100000 melalui /etc/subuid
dan /etc/subgid
. Konteks SELinux telah diatur sehingga wadah dapat mengakses direktori melalui :Z
pilihan pada volume mount.
Namun, akses ke /data
folder dalam penampung akan ditolak, dan satu-satunya pesan kesalahan yang akan dikembalikan oleh sistem adalah izin ditolak generik dari kernel. Setiap pengguna yang tidak tahu apa itu ruang nama pengguna tidak akan tahu apa yang salah.
Namun, sekarang kita tahu, penyebabnya harus jelas:Pemetaan pengguna berarti bahwa UID 1000 dalam wadah sebenarnya bukan UID 1000 pada host. Anda dapat melihat pengguna di wadah dan pengguna sebenarnya di host melalui podman top
perintah:
mheon@podman-rhel8-test $ podman top -l user,huser
USER HUSER
1000 100999
Di sini, USER adalah pengguna dalam penampung, sedangkan HUSER adalah pengguna di host.
Namun, mengetahui mengapa sesuatu terjadi tidak berarti kita tahu cara memperbaikinya. Kami masih ingin menjalankan wadah Podman tanpa akar kami dengan volume tertentu yang terpasang di dalamnya. Bagaimana kita melakukannya? Untungnya, ada banyak cara untuk memperbaikinya, yang akan saya bahas di bawah.
Solusi pertama
Yang pertama sederhana:--user
opsi dapat dihilangkan dari wadah, menjalankan perintah wadah sebagai root . Seperti disebutkan di atas, secara default, Podman memetakan pengguna yang menjalankan container ke root dalam wadah—jadi sekarang kita akan mengakses volume sebagai UID/GID 1000 di host, meskipun root dalam wadah. Berjalan sebagai root dalam wadah rootful adalah masalah keamanan potensial karena Anda menjalankan sebagai root . sistem pengguna—jika penyerang keluar dari penampung, mereka dapat bertindak sebagai root pada sistem. Inti dari penampung tanpa akar adalah bahwa ini tidak pernah benar—masalah keamanan pada dasarnya bukan faktor.
Sayangnya, solusi menjalankan container sebagai root jatuh datar ketika gambar secara khusus ditulis untuk menggunakan pengguna non-root. Beberapa gambar kontainer menampilkan skrip titik masuk yang kompleks untuk menghapus izin yang tidak dapat dimodifikasi dengan mudah. Ini akan membutuhkan solusi alternatif.
Solusi kedua
Opsi kedua adalah memberikan izin kepada pengguna yang berjalan di wadah untuk membaca dan menulis folder yang dipasang dari Host. Pada Podman v3.1.0, ini dapat dilakukan secara otomatis melalui :U
opsi volume ke -v
bendera (mis. -v /home/mheon/data:/data:Z,U
).
Selanjutnya masukkan podman unshare chown 1000:1000 /home/mheon/data
. Opsi volume ini akan secara otomatis menyesuaikan kepemilikan direktori, sehingga pengguna yang berjalan di penampung—pengguna UID 1000 apa pun dalam penampung yang dipetakan di host—akan memiliki direktori tersebut. Dalam versi tanpa tanda ini, podman unshare
perintah dapat digunakan untuk memasuki ruang nama pengguna tanpa root dan kemudian chown
direktori yang akan dimiliki oleh pengguna yang menjalankan container.
Dalam hal ini, podman unshare chown 1000:1000 /home/mheon/data
akan mengubah kepemilikan direktori pada Host menjadi pengguna dan grup yang dipetakan ke UID/GID 1000 di ruang nama pengguna. Harap dicatat bahwa, jika kepemilikan diubah, semua direktori induk pada host juga akan memerlukan izin eksekusi untuk semua pengguna (chmod a+x…
) izin untuk memastikan direktori yang dimaksud dapat diakses.
Sayangnya, chown
pendekatan memang datang dengan serangkaian kelemahannya sendiri. /home/mheon/data
direktori ada di direktori home pengguna saya, tetapi tidak lagi dimiliki oleh pengguna saya (dalam hal ini, itu dimiliki oleh pengguna dan grup 100999 ). Di ruang nama pengguna tanpa akar, mheon pengguna dapat bertindak sebagai root dan membaca, menulis, dan memodifikasi file yang dimiliki oleh pengguna tersebut; tetapi ia tidak dapat melakukan hal-hal ini di luarnya. Podman memang menyediakan perintah untuk memasukkan shell di dalam ruang nama pengguna rootless (podman unshare
) yang dapat digunakan untuk mengubah atau menghapus file tersebut, tetapi ketidakmampuan untuk mengelola file ini akan merepotkan.
Solusi ketiga
Opsi ketiga adalah menggunakan --userns=keep-id
opsi untuk podman run
. Bendera ini memberi tahu Podman untuk melakukan dua hal:Pertama, untuk menyetel pengguna, wadah berjalan ke UID dan GID pengguna yang menjalankan Podman (kecuali secara eksplisit diganti oleh --user
flag), dan kedua, untuk menyusun ulang pengguna yang dipetakan ke dalam wadah sehingga pengguna yang menjalankan Podman dipetakan ke UID dan GID mereka sendiri, bukan root (ini dilakukan melalui ruang nama pengguna kedua, bersarang di dalam ruang nama pengguna tanpa akar, dibuat hanya untuk wadah ini). Pengguna di ruang nama pengguna tempat penampung dijalankan bukan root tetapi tetap memetakan ke pengguna yang menjalankan Podman (mheon ) pada host dan dengan demikian dapat mengakses direktori yang dipasang pada contoh /home/mheon/data
.
Namun, ini tidak akan menyelesaikan semua kesalahan akses. Masalah umum lainnya adalah mencoba memasang di file atau perangkat yang dapat diakses oleh pengguna yang menjalankan Podman, tetapi hanya oleh grup tambahan. Misalnya, katakanlah pengguna saya, mheon , adalah bagian dari kvm grup yang memiliki /dev/kvm
, dan saya memilih untuk memasang /dev/kvm
ke dalam wadah menggunakan podman run -t -i -v /dev/kvm:/dev/kvm fedora bash
. Kontainer tidak akan dapat mengakses /dev/kvm
, terlepas dari kenyataan bahwa itu berjalan sebagai pengguna saya di host (yang seharusnya memiliki akses).
Alasannya adalah, untuk tujuan keamanan, container akan (secara default) menghapus semua grup tambahan sebagai bagian dari pembuatannya. Perilaku ini dapat dinonaktifkan, tetapi hanya menggunakan crun
OCI runtime (ini harus menjadi default pada Podman 3.0 pada semua distribusi kecuali RHEL) dengan meneruskan anotasi khusus (--annotation run.oci.keep_original_groups=1
).
Di Podman v3.2.0 mendatang, ini akan tersedia melalui argumen khusus ke group-add
tandai (--group-add keep-groups
). Harap dicatat bahwa, meskipun kami dapat mempertahankan akses ke grup ini, kami tidak memiliki izin untuk menambahkannya ke ruang nama pengguna tanpa akar—kami hanya dapat melakukannya untuk pengguna dan grup yang dialokasikan kepada kami di /etc/subuid
dan /etc/subgid
. Sebuah ls -al
di /dev/kvm
dalam wadah akan menemukannya dimiliki oleh nobody:nobody (sebagai pemilik dan grup sebenarnya, root:kvm , tidak dipetakan ke dalam wadah).
Namun, itu dapat diakses karena, terlepas dari kenyataan bahwa kvm grup bukan bagian dari ruang nama pengguna, proses wadah masih merupakan bagian dari grup di mata kernel. Ini agak membatasi karena tidak mungkin membuat file secara eksplisit sebagai salah satu grup tambahan ini (sebagai tidak ada bukan grup nyata yang dapat berinteraksi dengan kami), tetapi itu cukup untuk memberikan wadah akses ke konten di Host yang tidak dapat dijangkau, dan direktori dengan bit SUID yang dimiliki oleh grup tambahan akan tetap menetapkan pemilik yang benar .
Jenis kesalahan umum lainnya ditemui saat menarik gambar dengan file atau folder yang dimiliki oleh pengguna UID tinggi. File atau folder apa pun yang dimiliki oleh UID atau GID yang terlalu besar untuk dimasukkan ke dalam ruang nama pengguna akan menghasilkan kesalahan. Saya sebelumnya menulis blog tentang ini dan solusi potensial, yang dapat ditemukan di sini.
Kesimpulan
Salah satu fitur terkuat Podman adalah dukungan kuat kami untuk container tanpa root, dan tidak sulit untuk melihat mengapa orang-orang bersemangat. Wadah tanpa root mudah disiapkan, lebih aman daripada root container, dan dapat melakukan hampir semua hal yang dijalankan container sebagai root bisa lakukan. Tentu saja, kata kuncinya adalah hampir —karena keseluruhan pengalaman dengan root dan rootless sangat mirip, perbedaan dapat membingungkan dan seringkali tidak mudah untuk dijelaskan. Setelah membaca blog ini, Anda akan memiliki pemahaman yang kuat tentang salah satu perbedaan terbesar ini dan cara bekerja dengan Podman agar container Anda berjalan seperti yang Anda inginkan.