Nix Memerlukan Binary yang Dapat Direlokasi
(fzakaria.com)- Sebagai manajer paket berbasis store, Nix dirancang untuk menempatkan paket pada prefix tetap seperti
/nix/store, sehingga menjadi sangat membatasi di lingkungan Nix tanpa root yang ingin menaruh store di lokasi lain tanpa instalasi Nix yang sudah ada atau tanpa hak akses root - Jika
--store /tmp/...digunakan bersamachrootdan mount namespace, hash yang sama dengan build/nix/storeyang sudah ada bisa dipertahankan, sehingga cache binary seperticache.nixos.orgtetap bisa dimanfaatkan - Jika prefix store diubah menjadi
local?store=/tmp/...tanpa namespace, hash akan berubah, dan bahkan buildhellosederhana pun bisa membatalkan seluruh grafik dependensi serta memicu kompilasi ulang GCC - Inti usulannya adalah memakai path relatif berbasis
$ORIGINyang didukung dynamic linker Linux, alih-alih path absolut diRUNPATHELF, agar perubahan lokasi store tidak merambat menjadi perubahan hash dan kompilasi ulang - Hambatan utama yang mencegah relokasi nyata adalah kernel tidak mendukung
$ORIGINpada ELFPT_INTERPdan shebang skrip; arah solusi yang diusulkan meliputi patch kernel, wrapper statis, path relatif per bahasa, dan metadatarelocatable = true;
Benturan antara prefix store tetap dan Nix tanpa root
- Sistem berbasis store seperti Nix dan Guix menyimpan semua paket di bawah prefix yang sudah ditentukan
- Nix memakai
/nix/store - Guix memakai
/gnu/store
- Nix memakai
- Struktur ini memudahkan penulisan ulang path binary atau library
- Misalnya
/bin/bashbisa diganti menjadi path store penuh seperti/nix/store/gik3rh1vz2jlgnifb9dh6vc6sxwwz9jj-bash-5.3p9/bin/bash
- Misalnya
- Ada juga situasi ketika store ingin ditempatkan di lokasi lain
- lingkungan yang belum memasang Nix
- lingkungan yang tidak memiliki hak akses yang diperlukan
- kasus seperti ini mengarah pada masalah “Nix tanpa root”
- Nix saat ini memang bisa memakai path store lain, tetapi apakah hash tetap sama atau tidak bergantung pada metodenya
nix build nixpkgs#hellomemasang ke/nix/store/zi2bj2hlavv8q743li2s9diqbcpmrf9b-hello-2.12.3/nix build --store /tmp/fzakaria/store nixpkgs#hellomemakaichrootdan mount namespace untuk memasang ke/tmp/fzakaria/store/nix/store/zi2bj2hlavv8q743li2s9diqbcpmrf9b-hello-2.12.3/- Pada kedua kasus, hash
zi2bj2hlavv8q743li2s9diqbcpmrf9btetap sama
- Jika hash sama, derivation yang sudah dihitung sebelumnya dari binary substituter seperti https://cache.nixos.org bisa dimanfaatkan
Biaya saat mengganti store tanpa namespace
- Alat seperti Bazel atau Buck2 mungkin sudah menggunakan namespace sendiri untuk sandboxing
- Jika mencoba mengintegrasikan Nix ke ekosistem seperti ini, pembatasan nested user namespace dan mount membuatnya jauh kurang praktis
- Prefix store alternatif memang bisa ditentukan tanpa
chrootdan mount namespace, tetapi ada kelemahan berupa perubahan hash- Contoh perintahnya memakai
--store 'local?store=/tmp/fzakaria/store&state=/tmp/fzakaria/state&log=/tmp/fzakaria/log' - Hasil path
hellomenjadi/tmp/fzakaria/store/qv3fhi1j9gh27fyds5n5b16yia8i6zn5-hello-2.12.3 - Hash berubah dari
zi2...menjadiqv3fhi1j9gh27fyds5n5b16yia8i6zn5
- Contoh perintahnya memakai
- Perubahan sederhana pada string prefix store memicu invalidasi berantai pada seluruh grafik dependensi
- Hanya untuk menampilkan “Hello World” dari folder lain, Anda bisa berakhir mengompilasi GCC selama 4 jam
- Dalam kondisi ini, cache publik tidak bisa dimanfaatkan
- Keterbatasan ini juga sudah dicantumkan dalam dokumentasi Nix saat ini
Bagian yang diselesaikan oleh $ORIGIN dan keterbatasan kernel yang tersisa
- Akar masalahnya adalah prefix store merupakan bagian dari derivation itu sendiri, sehingga memengaruhi perhitungan hash
- Jika tidak semua tempat memakai prefix store penuh dan sebagai gantinya menggunakan path relatif, perubahan hash bisa dihindari
RUNPATHpada binary ELF adalah salah satu titik yang memungkinkan untuk itu- Contoh
RUNPATHhellosaat ini adalah/nix/store/57iz36553175g3178pvxjij8z5rcsd4n-glibc-2.42-61/lib - Loader Linux mendukung
$ORIGIN, yang berarti direktori tempat executable berada - Karena itu,
RUNPATHbisa ditulis sebagai$ORIGIN/../../57iz36553175g3178pvxjij8z5rcsd4n-glibc-2.42-61/lib - Dengan cara ini, lokasi store bisa berubah tanpa mengubah hash dan tanpa perlu kompilasi ulang
- Contoh
- Namun sebelum dynamic linker membaca
RUNPATH, kernel Linux lebih dulu harus memuat dynamic linker itu sendiri- Path ini disimpan di header
PT_INTERPpada ELF - Contohnya
/nix/store/57iz36553175g3178pvxjij8z5rcsd4n-glibc-2.42-61/lib/ld-linux-x86-64.so.2 - Saat ini kernel Linux tidak mendukung
$ORIGINdiPT_INTERP
- Path ini disimpan di header
- Shebang skrip juga memiliki batasan yang sama
- Contohnya
#!/nix/store/gik3rh1vz2jlgnifb9dh6vc6sxwwz9jj-bash-5.3p9/bin/bash - Kernel mengharapkan path absolut saat mem-parsing
#! - Saat ini shebang juga belum mendukung
$ORIGIN
- Contohnya
- Path relatif berbasis direktori kerja saat ini memang bisa dipakai, tetapi akan rusak jika skrip dijalankan dari lokasi lain, sehingga kurang dapat diandalkan
Usulan menuju binary yang dapat direlokasi
- Untuk benar-benar membuat binary yang dapat direlokasi, batasan kernel perlu diakali atau diubah
- Ada tiga pendekatan yang diusulkan
- Mem-patch kernel Linux agar mendukung
$ORIGINpadaPT_INTERPdan shebang - Membungkus semua binary dengan binary statis kecil, lalu wrapper menghitung lokasinya sendiri sebelum menjalankan dynamic linker
- Mengubah akses lokasi file agar memanfaatkan fitur path relatif per bahasa
- Di Python, file bisa diakses relatif terhadap dirinya sendiri lewat
__file__
- Di Python, file bisa diakses relatif terhadap dirinya sendiri lewat
- Mem-patch kernel Linux agar mendukung
- Pendekatan yang dinilai paling sesuai adalah memperluas dukungan kernel Linux
- Pada mesin NixOS, kernel bisa di-patch dengan Nix untuk menambahkan dukungan tersebut
- Selain itu, diusulkan juga metadata
relocatable = true;pada setiap derivation untuk menandai apakah paket dapat direlokasi
1 komentar
Komentar Lobste.rs
Akan bagus jika kernel Linux menambahkan dukungan
$ORIGINpadaPT_INTERP. Dulu saya pernah mencoba dengan biner wrapper statis, dan juga melihat beberapa upaya lain(contoh yang bagus); semuanya adalah hack yang hebat dan keren, tapi pada akhirnya tetap hackSaya juga belum benar-benar memahami implikasi keamanannya, jadi akan membantu jika ada penjelasan yang rapi
Solaris tampaknya mendukung ini, jadi mungkin ada cara untuk melakukannya dengan aman. Sulit menemukan rujukannya, tetapi pada ENOEXEC di manual
execve(2)tertulis bahwa jika header programPT_INTERPpada berkas image proses setuid/setgid memiliki path relatif atau memakai token$ORIGIN, maka eksekusi gagalBukankah banyak perangkat lunak memiliki path yang ditanam saat build atau konstanta, sehingga pada akhirnya tetap harus dikompilasi ulang agar bisa berfungsi dengan benar?
outPathmelalui{foo}, tetapi jika salah satu dependensi tingkat atas diubah ke repositori lokal, maka harus dibangun ulangPatch dcrt1 untuk musl (ditulis oleh rcombs) menyelesaikan masalah ini di ruang pengguna
Apakah tidak bisa membuat sistem berkas yang di-mount ke
/originagar ditafsirkan sebagai$ORIGIN? Kalau begitu, ini sepertinya akan bekerja baik untuk shebang maupun ELF tanpa sintaks tambahan/origin, bukankah kita juga bisa langsung membuat/nixdan menjalankannix-daemon?Bukankah akan berisiko dari sisi keamanan jika biner bisa menunjuk loader yang relatif, kemungkinan tidak aman, dan disertakan sendiri?
libc.so.6?