Sandboxing

Artikel ini membahas sandbox di Bazel, menginstal sandboxfs, dan men-debug lingkungan sandbox Anda.

Sandboxing adalah strategi pembatasan izin yang mengisolasi proses dari satu sama lain atau dari resource dalam suatu sistem. Untuk Bazel, 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 mengetahui jalur absolut ke sana.

Sandboxing tidak menyembunyikan lingkungan host dengan cara apa pun. Proses dapat dengan bebas mengakses semua file pada sistem file. Namun, pada platform yang mendukung namespace pengguna, proses tidak dapat mengubah file apa pun di luar direktori kerjanya. 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 bertindak sebagai direktori kerja tindakan pada waktu eksekusi. execroot/ berisi semua file input untuk tindakan dan berfungsi sebagai penampung untuk setiap output yang dihasilkan. Bazel kemudian menggunakan teknik yang disediakan sistem operasi, container di Linux dan sandbox-exec di macOS, untuk membatasi tindakan dalam execroot/.

Alasan sandboxing

  • Tanpa sandbox tindakan, Bazel tidak tahu apakah alat menggunakan file input yang tidak dideklarasikan (file yang tidak secara eksplisit tercantum dalam dependensi suatu tindakan). Saat salah satu file input yang tidak dideklarasikan berubah, Bazel masih yakin 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 cache jarak jauh. Entri cache yang buruk dalam cache bersama akan memengaruhi setiap developer di project, dan menghapus seluruh cache jarak jauh bukanlah solusi yang tepat.

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

Strategi sandbox yang akan digunakan

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

Strategi local (alias standalone) tidak melakukan jenis sandbox apa pun. Cukup menjalankan command line tindakan dengan direktori kerja yang ditetapkan ke execroot ruang kerja Anda.

processwrapper-sandbox adalah strategi sandbox yang tidak memerlukan fitur "lanjutan" dan dapat langsung berfungsi di sistem POSIX. Fitur ini membuat direktori sandbox yang terdiri dari symlink yang mengarah ke file sumber asli, menjalankan command line tindakan dengan direktori kerja yang disetel ke direktori ini, bukan execroot, lalu memindahkan artefak output yang diketahui dari sandbox ke dalam execroot dan menghapus sandbox. Hal ini mencegah tindakan secara tidak sengaja menggunakan file input yang tidak dideklarasikan dan mengotori execroot dengan file output yang tidak dikenal.

linux-sandbox selangkah lebih maju dan di-build di atas processwrapper-sandbox. Serupa dengan apa yang dilakukan Docker pada dasarnya, Docker menggunakan Namespace Linux (namespace User, Mount, PID, Network, dan IPC) untuk memisahkan tindakan dari host. Artinya, tindakan ini membuat seluruh sistem file menjadi hanya-baca kecuali untuk direktori sandbox, sehingga tindakan tersebut tidak dapat mengubah apa pun secara tidak sengaja pada sistem file host. Hal ini mencegah situasi seperti pengujian yang berisi bug secara tidak sengaja melakukan rm -rf'ing direktori $HOME Anda. Secara opsional, Anda juga dapat mencegah tindakan tersebut mengakses jaringan. linux-sandbox menggunakan namespace PID untuk mencegah tindakan melihat proses lain dan menghentikan semua proses dengan andal (bahkan daemon yang dihasilkan oleh tindakan) di akhir.

darwin-sandbox serupa, tetapi untuk macOS. Framework ini menggunakan alat sandbox-exec Apple untuk mencapai hal yang kurang lebih sama dengan sandbox Linux.

Baik linux-sandbox maupun darwin-sandbox tidak berfungsi dalam skenario "tumpang-tindih" karena batasan dalam mekanisme yang disediakan oleh sistem operasi. Karena Docker juga menggunakan namespace Linux untuk keajaiban container-nya, Anda tidak dapat menjalankan linux-sandbox dengan mudah di dalam container Docker, kecuali jika Anda menggunakan docker run --privileged. Di macOS, Anda tidak dapat menjalankan sandbox-exec di dalam proses yang sudah di-sandbox. Dengan demikian, dalam kasus ini, Bazel otomatis kembali menggunakan processwrapper-sandbox.

Jika Anda lebih suka mendapatkan error build — seperti tidak mem-build secara tidak sengaja 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 akan otomatis melakukan sandbox pekerja persisten.

Kelemahan sandboxing

  • Sandbox menimbulkan biaya penyiapan dan pembongkaran tambahan. Seberapa besar biaya ini bergantung pada banyak faktor, termasuk bentuk build dan performa OS host. Untuk Linux, build dengan sandbox jarang lebih lambat beberapa persen. Menetapkan --reuse_sandbox_directories dapat mengurangi biaya penyiapan dan pemutusan.

  • Sandboxing secara efektif menonaktifkan semua cache yang mungkin dimiliki alat ini. Anda dapat mengurangi hal ini dengan menggunakan pekerja persisten, dengan mengorbankan jaminan sandbox yang lebih lemah.

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

sandboxf

sandboxfs adalah sistem file FUSE yang mengekspos tampilan arbitrer dari sistem file yang mendasarinya tanpa penalti waktu. Bazel menggunakan sandboxfs untuk menghasilkan execroot/ secara instan untuk setiap tindakan, sehingga menghindari biaya pembuatan ribuan panggilan sistem. Perhatikan bahwa I/O lebih lanjut dalam execroot/ mungkin lebih lambat karena overhead FUSE.

Menginstal sandboxf

Gunakan langkah-langkah berikut untuk menginstal sandboxfs dan menjalankan build Bazel dengannya:

Download

Download dan instal sandboxfs sehingga biner sandboxfs berakhir di PATH.

Jalankan sandboxfs

  1. (khusus macOS) Instal OSXFUSE.
  2. (khusus macOS) Jalankan:

    sudo sysctl -w vfs.generic.osxfuse.tunables.allow_other=1
    

    Anda harus melakukannya setelah penginstalan dan setiap kali memulai ulang untuk memastikan layanan sistem macOS inti berfungsi melalui sandboxfs.

  3. Menjalankan build Bazel dengan --experimental_use_sandboxfs.

    bazel build target --experimental_use_sandboxfs
    

Pemecahan masalah

Jika Anda melihat local, bukan darwin-sandbox atau linux-sandbox, sebagai anotasi untuk tindakan yang dijalankan, ini dapat berarti bahwa sandbox dinonaktifkan. Teruskan --genrule_strategy=sandboxed --spawn_strategy=sandboxed untuk mengaktifkannya.

Proses debug

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

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 yang tepat 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, melihat file mana yang dibuat oleh Bazel, dan menjalankan kembali perintah tersebut untuk melihat perilakunya.

Perlu diperhatikan bahwa Bazel tidak menghapus direktori sandbox saat Anda menggunakan --sandbox_debug. Sebaiknya nonaktifkan --sandbox_debug, kecuali jika sedang melakukan proses debug, karena disk akan terisi penuh seiring waktu.