- store tabanlı bir paket yöneticisi olan Nix, paketleri
/nix/storegibi sabit bir prefix altında tutacak şekilde tasarlanmıştır; bu da mevcut bir Nix kurulumu veya root yetkisi olmadan store'u başka bir konuma koymak isteyen rootless Nix ortamlarında ciddi kısıtlar yaratır --store /tmp/...ilechrootve mount namespace birlikte kullanıldığında, mevcut/nix/storederlemeleriyle aynı hash korunabilir; böylececache.nixos.orggibi ikili önbellekler kullanılmaya devam edebilir- Store prefix'i namespace olmadan
local?store=/tmp/...ile değiştirilirse hash farklılaşır ve basit birhelloderlemesi bile tüm bağımlılık grafiğinin geçersizleşmesine ve GCC'nin yeniden derlenmesine yol açabilir - Önerinin özü, ELF
RUNPATHiçinde mutlak yol yerine Linux dinamik bağlayıcısının desteklediği$ORIGINtabanlı göreli yolları kullanarak store konumu değişikliğinin hash değişimine ve yeniden derlemeye yayılmasını önlemektir - Gerçek yeniden konumlandırılabilirliğin önündeki darboğaz, çekirdeğin ELF
PT_INTERPve betik shebang'lerinde$ORIGINdesteği sunmamasıdır; çözüm yönü olarak çekirdek yaması, statik wrapper, dile özgü göreli yollar verelocatable = true;metaverisi önerilmektedir
Sabit store prefix'i ile rootless Nix'in çatışması
- Nix ve Guix gibi store tabanlı sistemler, tüm paketleri belirlenmiş bir prefix altında saklar
- Nix:
/nix/store - Guix:
/gnu/store
- Nix:
- Bu yapıda ikili ve kütüphane yollarını yeniden yazmak kolaydır
- Örneğin
/bin/bash,/nix/store/gik3rh1vz2jlgnifb9dh6vc6sxwwz9jj-bash-5.3p9/bin/bashgibi tam bir store yoluyla değiştirilebilir
- Örneğin
- Store'u başka bir konuma koymak istenebilecek durumlar vardır
- Nix'in zaten kurulu olmadığı ortamlar
- Gerekli yetkilerin olmadığı ortamlar
- Bu tür durumlar “rootless Nix” sorununa yol açar
- Nix şu anda da farklı store yollarını belirlemeye izin verir, ancak yönteme göre hash'in korunup korunmaması değişir
nix build nixpkgs#hello,/nix/store/zi2bj2hlavv8q743li2s9diqbcpmrf9b-hello-2.12.3/altına kurulum yaparnix build --store /tmp/fzakaria/store nixpkgs#hello,chrootve mount namespace kullanarak/tmp/fzakaria/store/nix/store/zi2bj2hlavv8q743li2s9diqbcpmrf9b-hello-2.12.3/altına kurulum yapar- Her iki durumda da
zi2bj2hlavv8q743li2s9diqbcpmrf9bhash'i aynıdır
- Hash aynı kaldığında, https://cache.nixos.org gibi ikili substituter kaynaklarının önceden hesaplanmış derivation'ları kullanılabilir
Namespace olmadan store'u değiştirmenin maliyeti
- Bazel veya Buck2 gibi araçlar, kendi sandboxing'leri için zaten namespace kullanıyor olabilir
- Nix'i bu ekosistemlere entegre etmeye çalışırken iç içe user namespace'ler ve mount kısıtları nedeniyle pratiklik ciddi biçimde düşebilir
chrootve mount namespace olmadan da alternatif bir store prefix'i belirtilebilir, ancak bunun hash'i değiştiren bir kusuru vardır- Örnek komut,
--store 'local?store=/tmp/fzakaria/store&state=/tmp/fzakaria/state&log=/tmp/fzakaria/log'kullanır - Ortaya çıkan
helloyolu:/tmp/fzakaria/store/qv3fhi1j9gh27fyds5n5b16yia8i6zn5-hello-2.12.3 - Hash artık
zi2...değil,qv3fhi1j9gh27fyds5n5b16yia8i6zn5olur
- Örnek komut,
- Sadece store prefix dizesini değiştirmenin kendisi, tüm bağımlılık grafiğini zincirleme biçimde geçersiz kılar
- Başka bir klasörden yalnızca “Hello World” yazdırmak istemek bile GCC'yi 4 saat boyunca derlemenize neden olabilir
- Bu durumda genel açık önbelleklerden yararlanılamaz
- Bu sınırlama şu anda Nix belgelerinde de açıkça belirtilmektedir
$ORIGIN'in çözdüğü kısım ve kalan çekirdek sınırları
- Sorunun kaynağı, store prefix'inin derivation'ın kendisinin bir parçası olması ve bu yüzden hash hesabını etkilemesidir
- Her yerde tam store prefix'i yerine göreli yollar kullanılırsa hash değişiminden kaçınılabilir
- ELF ikili dosyalarındaki
RUNPATH, bunun uygulanabileceği noktalardan biridirhelloiçin mevcutRUNPATHörneği:/nix/store/57iz36553175g3178pvxjij8z5rcsd4n-glibc-2.42-61/lib- Linux loader, çalıştırılabilir dosyanın bulunduğu dizini ifade eden
$ORIGINdeğerini destekler - Bu nedenle
RUNPATH,$ORIGIN/../../57iz36553175g3178pvxjij8z5rcsd4n-glibc-2.42-61/libgibi yazılabilir - Böylece store konumu değişse bile hash değişmez ve yeniden derleme gerekmez
- Ancak dinamik bağlayıcı
RUNPATH'i okumadan önce, Linux çekirdeğinin önce dinamik bağlayıcının kendisini yüklemesi gerekir- Bu yol, ELF'nin
PT_INTERPbaşlığında saklanır - Örnek:
/nix/store/57iz36553175g3178pvxjij8z5rcsd4n-glibc-2.42-61/lib/ld-linux-x86-64.so.2 - Mevcut Linux çekirdeği,
PT_INTERPiçinde$ORIGINdesteği sunmaz
- Bu yol, ELF'nin
- Betik shebang'leri de aynı kısıta sahiptir
- Örnek:
#!/nix/store/gik3rh1vz2jlgnifb9dh6vc6sxwwz9jj-bash-5.3p9/bin/bash - Çekirdek,
#!satırını ayrıştırırken mutlak yol bekler - Shebang içinde de şu anda
$ORIGINdesteği yoktur
- Örnek:
- Geçerli çalışma dizinine göre göreli yollar kullanılabilir, ancak betik başka bir konumdan çalıştırılırsa bozulacağı için güvenilir değildir
Yeniden konumlandırılabilir ikili dosyalara giden öneri
- Gerçekten yeniden konumlandırılabilir ikili dosyalar üretmek için çekirdek kısıtlarını aşmak veya değiştirmek gerekir
- Önerilen yaklaşım üç parçadan oluşur
- Linux çekirdeğini yamalayarak
PT_INTERPve shebang içinde$ORIGINdesteği eklemek - Tüm ikili dosyaları küçük bir statik ikili ile sarmalamak ve wrapper'ın kendi konumunu hesapladıktan sonra dinamik bağlayıcıyı çalıştırmasını sağlamak
- Dosya konumlarını, dillerin göreli yol özelliklerinden yararlanacak şekilde değiştirmek
- Python'da
__file__ile kendi konumuna göre dosyalara erişilebilir
- Python'da
- Linux çekirdeğini yamalayarak
- En uygun yaklaşım olarak Linux çekirdek desteğinin genişletilmesi önerilmektedir
- NixOS makinelerinde Nix kullanılarak çekirdeğe bu desteği ekleyen yamalar uygulanabilir
- Ayrıca, her derivation için yeniden konumlandırılabilir olup olmadığını gösteren
relocatable = true;metaverisinin eklenmesi de önerilmektedir
1 yorum
Lobste.rs görüşleri
Linux çekirdeğinde
PT_INTERPiçin$ORIGINdesteği olsa iyi olurdu. Daha önce statik sarmalayıcı binary ile denemiştim, başka birkaç girişim de gördüm(iyi bir örnek); hepsi harika ve şık hack’ler ama sonuçta hâlâ hackGüvenlik etkilerini henüz tam olarak anlamadım; derli toplu bir açıklama olsa faydalı olurdu
Solaris bunu destekliyor gibi görünüyor; belki güvenli bir şekilde yapmanın yolu vardır. Dayanak bulmak zor ama
execve(2)kılavuzundaki ENOEXEC bölümünde, setuid/setgid süreç image dosyasınınPT_INTERPprogram header’ı göreli yol içerirse veya$ORIGINtoken’ını kullanırsa başarısız olacağı yazıyorBirçok yazılım derleme zamanında gömülmüş yollar ya da sabitler içeriyor; düzgün çalışması için sonuçta yine yeniden derlemek gerekmiyor mu?
{foo}üzerindenoutPathtüketiyordur; üst bağımlılıklardan birini yerel depoya çevirirsen yeniden derlemek gerekirmusl için dcrt1 yaması(rcombs tarafından yazıldı) bu sorunu kullanıcı alanında çözüyor
$ORIGINolarak yorumlanacak şekilde/originiçine mount edilmiş bir dosya sistemi oluşturulamaz mı? O zaman ek sözdizimi olmadan hem shebang’lerde hem de ELF’te çalışır gibi görünüyor/originoluşturup mount edebiliyorsan, doğrudan/nixoluşturupnix-daemonçalıştırabilirsin, değil mi?Binary göreli bir yol ile, muhtemelen güvenli olmayan ve kendi sağladığı bir loader belirtebiliyorsa bu güvenlik riski olmaz mı?
libc.so.6gibi diğer dinamik bağlantı kütüphanelerinin arama yolunu belirleyen ortam değişkenlerinden neden daha az güvenli sayılsın?