Sandboxing

Laporkan masalah Lihat sumber Nightly · 8.0 7.4 . 7.3 · 7.2 · 7.1 · 7.0 · 6.5

Artikel ini membahas sandboxing di Bazel dan men-debug lingkungan sandboxing Anda.

Sandboxing adalah strategi pembatasan izin yang mengisolasi proses dari satu sama lain atau dari resource dalam sistem. Untuk Bazel, hal ini berarti membatasi akses sistem file.

Sandbox sistem file Bazel menjalankan proses di direktori kerja yang hanya berisi input yang diketahui, sehingga compiler dan alat lainnya tidak melihat file sumber yang tidak boleh diakses, kecuali jika mereka mengetahui jalur absolut ke file tersebut.

Sandboxing tidak menyembunyikan lingkungan host dengan cara apa pun. Proses dapat dengan bebas mengakses semua file di sistem file. Namun, pada platform yang mendukung namespace pengguna, proses tidak dapat mengubah file apa pun di luar direktori kerja. Hal ini memastikan bahwa grafik build tidak memiliki dependensi tersembunyi yang dapat memengaruhi reproduksi build.

Lebih khusus lagi, Bazel membuat direktori execroot/ untuk setiap tindakan, yang berfungsi sebagai direktori kerja tindakan pada waktu eksekusi. execroot/ berisi semua file input ke tindakan dan berfungsi sebagai penampung untuk output yang dihasilkan. Bazel kemudian menggunakan teknik yang disediakan sistem operasi, penampung di Linux dan sandbox-exec di macOS, untuk membatasi tindakan dalam execroot/.

Alasan untuk membuat sandbox

  • Tanpa sandbox tindakan, Bazel tidak tahu apakah alat menggunakan file input yang tidak dideklarasikan (file yang tidak tercantum secara eksplisit dalam dependensi tindakan). Saat salah satu file input yang tidak dideklarasikan berubah, Bazel masih percaya bahwa build sudah yang terbaru dan tidak akan mem-build ulang tindakan. Hal ini dapat menghasilkan build inkremental yang salah.

  • Penggunaan kembali entri cache yang salah akan menimbulkan masalah selama penyimpanan dalam cache jarak jauh. Masuknya cache yang buruk dalam cache bersama memengaruhi setiap developer dalam project, dan menghapus total cache jarak jauh bukanlah solusi yang layak.

  • Sandboxing meniru perilaku eksekusi jarak jauh — jika build berfungsi dengan baik dengan sandboxing, build tersebut kemungkinan juga akan berfungsi dengan eksekusi jarak jauh. Dengan membuat eksekusi jarak jauh mengupload semua file yang diperlukan (termasuk alat lokal), Anda dapat secara signifikan mengurangi biaya pemeliharaan untuk cluster kompilasi dibandingkan dengan harus menginstal alat di setiap mesin dalam cluster setiap kali Anda ingin mencoba compiler baru atau membuat perubahan pada alat yang ada.

Strategi sandbox yang akan digunakan

Anda dapat memilih jenis sandbox yang akan digunakan, jika ada, dengan flag strategi. Dengan menggunakan strategi sandboxed, Bazel akan memilih salah satu implementasi sandbox yang tercantum di bawah, dengan lebih memilih sandbox khusus OS daripada sandbox generik yang kurang hermetis. Pekerja persisten berjalan di sandbox generik jika Anda meneruskan flag --worker_sandboxing.

Strategi local (alias standalone) tidak melakukan sandboxing apa pun. Perintah ini hanya mengeksekusi command line tindakan dengan direktori kerja yang ditetapkan ke execroot ruang kerja Anda.

processwrapper-sandbox adalah strategi sandboxing yang tidak memerlukan fitur "lanjutan" apa pun - strategi ini akan berfungsi di sistem POSIX apa pun secara langsung. Perintah ini mem-build direktori sandbox yang terdiri dari symlink yang mengarah ke file sumber asli, mengeksekusi command line tindakan dengan direktori kerja yang ditetapkan ke direktori ini, bukan execroot, lalu memindahkan artefak output yang diketahui dari sandbox ke execroot dan menghapus sandbox. Hal ini mencegah tindakan menggunakan file input yang tidak dideklarasikan secara tidak sengaja dan membuatkan execroot berantakan dengan file output yang tidak diketahui.

linux-sandbox melangkah lebih jauh dan mem-build di atas processwrapper-sandbox. Mirip dengan yang dilakukan Docker di balik layar, Docker menggunakan Namespace Linux (namespace Pengguna, Mount, PID, Jaringan, dan IPC) untuk mengisolasi tindakan dari host. Artinya, tindakan ini membuat seluruh sistem file bersifat hanya baca kecuali direktori sandbox, sehingga tindakan tersebut tidak dapat mengubah apa pun secara tidak sengaja di sistem file host. Tindakan ini mencegah situasi seperti pengujian yang error yang tidak sengaja menghapus direktori $HOME Anda dengan rm -rf. Secara opsional, Anda juga dapat mencegah tindakan tersebut mengakses jaringan. linux-sandbox menggunakan namespace PID untuk mencegah tindakan melihat proses lain dan untuk menghentikan semua proses (bahkan daemon yang dihasilkan oleh tindakan) dengan andal di bagian akhir.

darwin-sandbox mirip, tetapi untuk macOS. Library ini menggunakan alat sandbox-exec Apple untuk mencapai hasil yang kurang lebih sama dengan sandbox Linux.

linux-sandbox dan darwin-sandbox tidak berfungsi dalam skenario "bertingkat" karena batasan dalam mekanisme yang disediakan oleh sistem operasi. Karena Docker juga menggunakan namespace Linux untuk keajaiban penampungnya, Anda tidak dapat menjalankan linux-sandbox dengan mudah di dalam penampung Docker, kecuali jika Anda menggunakan docker run --privileged. Di macOS, Anda tidak dapat menjalankan sandbox-exec di dalam proses yang sudah di-sandbox. Jadi, dalam kasus ini, Bazel akan otomatis kembali menggunakan processwrapper-sandbox.

Jika Anda lebih memilih untuk mendapatkan error build — seperti agar tidak sengaja mem-build dengan strategi eksekusi yang kurang ketat — ubah secara eksplisit daftar strategi eksekusi yang coba digunakan Bazel (misalnya, bazel build --spawn_strategy=worker,linux-sandbox).

Eksekusi dinamis biasanya memerlukan sandbox untuk eksekusi lokal. Untuk memilih tidak ikut, teruskan flag --experimental_local_lockfree_output. Eksekusi dinamis secara diam-diam menyandingkan pekerja persisten.

Kekurangan sandbox

  • Sandboxing menimbulkan biaya penyiapan dan pembongkaran tambahan. Besarnya biaya ini bergantung pada banyak faktor, termasuk bentuk build dan performa OS host. Untuk Linux, build dengan sandbox jarang lebih lambat dari beberapa persen. Menetapkan --reuse_sandbox_directories dapat meminimalkan biaya penyiapan dan pembongkaran.

  • Sandboxing secara efektif menonaktifkan cache apa pun yang mungkin dimiliki alat. Anda dapat memitigasi hal ini dengan menggunakan pekerja persisten, dengan biaya jaminan sandbox yang lebih lemah.

  • Pekerja multipleks memerlukan dukungan pekerja eksplisit untuk di-sandbox. Pekerja yang tidak mendukung sandboxing multipleks berjalan sebagai pekerja singleplex dalam eksekusi dinamis, yang dapat menghabiskan memori tambahan.

Proses Debug

Ikuti strategi di bawah untuk men-debug masalah terkait sandboxing.

Namespace yang dinonaktifkan

Di beberapa platform, seperti node cluster Google Kubernetes Engine atau Debian, namespace pengguna dinonaktifkan secara default karena masalah keamanan. Jika file /proc/sys/kernel/unprivileged_userns_clone ada dan berisi 0, Anda dapat mengaktifkan namespace pengguna dengan menjalankan:

   sudo sysctl kernel.unprivileged_userns_clone=1

Kegagalan eksekusi aturan

Sandbox mungkin gagal menjalankan aturan karena penyiapan sistem. Jika Anda melihat pesan seperti namespace-sandbox.c:633: execvp(argv[0], argv): No such file or directory, coba nonaktifkan sandbox dengan --strategy=Genrule=local untuk genrules, dan --spawn_strategy=local untuk aturan lainnya.

Proses debug mendetail untuk kegagalan build

Jika build Anda gagal, gunakan --verbose_failures dan --sandbox_debug untuk membuat Bazel menampilkan perintah persis yang dijalankan saat build Anda gagal, termasuk bagian yang menyiapkan sandbox.

Contoh pesan error:

ERROR: path/to/your/project/BUILD:1:1: compilation of rule
'//path/to/your/project:all' failed:

Sandboxed execution failed, which may be legitimate (such as a compiler error),
or due to missing dependencies. To enter the sandbox environment for easier
debugging, run the following command in parentheses. On command failure, a bash
shell running inside the sandbox will then automatically be spawned

namespace-sandbox failed: error executing command
  (cd /some/path && \
  exec env - \
    LANG=en_US \
    PATH=/some/path/bin:/bin:/usr/bin \
    PYTHONPATH=/usr/local/some/path \
  /some/path/namespace-sandbox @/sandbox/root/path/this-sandbox-name.params --
  /some/path/to/your/some-compiler --some-params some-target)

Sekarang Anda dapat memeriksa direktori sandbox yang dihasilkan dan melihat file yang dibuat Bazel dan menjalankan perintah lagi untuk melihat perilakunya.

Perhatikan bahwa Bazel tidak menghapus direktori sandbox saat Anda menggunakan --sandbox_debug. Kecuali jika Anda secara aktif men-debug, Anda harus menonaktifkan --sandbox_debug karena akan mengisi disk Anda dari waktu ke waktu.