1 puan yazan GN⁺ 3 시간 전 | 1 yorum | WhatsApp'ta paylaş
  • store tabanlı bir paket yöneticisi olan Nix, paketleri /nix/store gibi 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/... ile chroot ve mount namespace birlikte kullanıldığında, mevcut /nix/store derlemeleriyle aynı hash korunabilir; böylece cache.nixos.org gibi ikili önbellekler kullanılmaya devam edebilir
  • Store prefix'i namespace olmadan local?store=/tmp/... ile değiştirilirse hash farklılaşır ve basit bir hello derlemesi bile tüm bağımlılık grafiğinin geçersizleşmesine ve GCC'nin yeniden derlenmesine yol açabilir
  • Önerinin özü, ELF RUNPATH içinde mutlak yol yerine Linux dinamik bağlayıcısının desteklediği $ORIGIN tabanlı 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_INTERP ve betik shebang'lerinde $ORIGIN desteği sunmamasıdır; çözüm yönü olarak çekirdek yaması, statik wrapper, dile özgü göreli yollar ve relocatable = 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
  • 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/bash gibi tam bir store yoluyla değiştirilebilir
  • 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 yapar
    • nix build --store /tmp/fzakaria/store nixpkgs#hello, chroot ve mount namespace kullanarak /tmp/fzakaria/store/nix/store/zi2bj2hlavv8q743li2s9diqbcpmrf9b-hello-2.12.3/ altına kurulum yapar
    • Her iki durumda da zi2bj2hlavv8q743li2s9diqbcpmrf9b hash'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
  • chroot ve 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 hello yolu: /tmp/fzakaria/store/qv3fhi1j9gh27fyds5n5b16yia8i6zn5-hello-2.12.3
    • Hash artık zi2... değil, qv3fhi1j9gh27fyds5n5b16yia8i6zn5 olur
  • 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 biridir
    • hello için mevcut RUNPATH örneği: /nix/store/57iz36553175g3178pvxjij8z5rcsd4n-glibc-2.42-61/lib
    • Linux loader, çalıştırılabilir dosyanın bulunduğu dizini ifade eden $ORIGIN değerini destekler
    • Bu nedenle RUNPATH, $ORIGIN/../../57iz36553175g3178pvxjij8z5rcsd4n-glibc-2.42-61/lib gibi 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_INTERP başlığında saklanır
    • Örnek: /nix/store/57iz36553175g3178pvxjij8z5rcsd4n-glibc-2.42-61/lib/ld-linux-x86-64.so.2
    • Mevcut Linux çekirdeği, PT_INTERP içinde $ORIGIN desteği sunmaz
  • 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 $ORIGIN desteği yoktur
  • 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_INTERP ve shebang içinde $ORIGIN desteğ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
  • 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

 
GN⁺ 3 시간 전
Lobste.rs görüşleri
  • Linux çekirdeğinde PT_INTERP için $ORIGIN desteğ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â hack
    Gü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ın PT_INTERP program header’ı göreli yol içerirse veya $ORIGIN token’ını kullanırsa başarısız olacağı yazıyor

  • Birç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?

    • Bu zaten sık görülen bir durum; özellikle shebang satırlarında böyle. Nix, derleme zamanında bu tür değerleri ikame etmek için pek çok yardımcı araç içeriyor
    • Doğru, ama bu sorun yerel depo ile ilgisiz olarak zaten baştan beri vardı. Muhtemelen alt derivation’lar {foo} üzerinden outPath tüketiyordur; üst bağımlılıklardan birini yerel depoya çevirirsen yeniden derlemek gerekir
  • musl için dcrt1 yaması(rcombs tarafından yazıldı) bu sorunu kullanıcı alanında çözüyor

  • $ORIGIN olarak yorumlanacak şekilde /origin iç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

    • /origin oluşturup mount edebiliyorsan, doğrudan /nix oluşturup nix-daemon çalıştırabilirsin, değil mi?
    • Bence hedef, NixOS dışında da uyumlu olması için ek dosya sistemi veya daemon kurulumuna/yapılandırmasına gerek duymadan ilerlemek
  • Binary göreli bir yol ile, muhtemelen güvenli olmayan ve kendi sağladığı bir loader belirtebiliyorsa bu güvenlik riski olmaz mı?

    • Bu tehdit modelinde neye güveniliyor, neye güvenilmiyor? Sonuçta o binary’yi zaten çalıştıracaksan, mesele sadece onu doğrulanmış bir loader ile çalıştırmak mı?
    • libc.so.6 gibi diğer dinamik bağlantı kütüphanelerinin arama yolunu belirleyen ortam değişkenlerinden neden daha az güvenli sayılsın?