सैंडबॉक्सिंग

इस लेख में, Bazel में सैंडबॉक्सिंग और सैंडबॉक्सिंग एनवायरमेंट को डीबग करने के बारे में बताया गया है.

सैंडबॉक्सिंग, अनुमति को सीमित करने की एक रणनीति है. यह प्रोसेस को एक-दूसरे से या सिस्टम के संसाधनों से अलग करती है. Bazel के लिए, इसका मतलब फ़ाइल सिस्टम के ऐक्सेस को सीमित करना है.

Bazel का फ़ाइल सिस्टम सैंडबॉक्स, प्रोसेस को वर्किंग डायरेक्ट्री में चलाता है. इसमें सिर्फ़ जाने-पहचाने इनपुट होते हैं, ताकि कंपाइलर और अन्य टूल उन सोर्स फ़ाइलों को न देख पाएं जिन्हें उन्हें ऐक्सेस नहीं करना चाहिए. हालांकि, ऐसा तब तक होता है, जब तक उन्हें उन फ़ाइलों के पूरे पाथ की जानकारी न हो.

सैंडबॉक्सिंग, होस्ट एनवायरमेंट को किसी भी तरह से नहीं छिपाती है. प्रोसेस, फ़ाइल सिस्टम में मौजूद सभी फ़ाइलों को बिना किसी पाबंदी के ऐक्सेस कर सकती हैं. हालांकि, जिन प्लैटफ़ॉर्म पर उपयोगकर्ता नेमस्पेस काम करते हैं वहां प्रोसेस, अपनी वर्किंग डायरेक्ट्री से बाहर की किसी भी फ़ाइल में बदलाव नहीं कर सकतीं. इससे यह पक्का किया जाता है कि बिल्ड ग्राफ़ में ऐसी छिपी हुई डिपेंडेंसी न हों जो बिल्ड को फिर से बनाने की प्रोसेस पर असर डाल सकती हैं.

खास तौर पर, Bazel हर कार्रवाई के लिए एक execroot/ डायरेक्ट्री बनाता है. यह डायरेक्ट्री, कार्रवाई के एक्ज़ीक्यूशन के समय, कार्रवाई की वर्क डायरेक्ट्री के तौर पर काम करती है. execroot/ इसमें कार्रवाई के लिए सभी इनपुट फ़ाइलें होती हैं. साथ ही, यह जनरेट किए गए किसी भी आउटपुट के लिए कंटेनर के तौर पर काम करता है. इसके बाद, Bazel, ऑपरेटिंग सिस्टम की ओर से उपलब्ध कराई गई तकनीक का इस्तेमाल करता है. जैसे, Linux पर कंटेनर और macOS पर sandbox-exec. इससे कार्रवाई को sandbox-exec के दायरे में सीमित किया जा सकता है.execroot/

सैंडबॉक्सिंग की वजहें

  • ऐक्शन सैंडबॉक्सिंग के बिना, Bazel को यह पता नहीं चलता कि कोई टूल, बिना बताए इनपुट फ़ाइलों का इस्तेमाल करता है या नहीं. ये ऐसी फ़ाइलें होती हैं जिन्हें किसी ऐक्शन की डिपेंडेंसी में साफ़ तौर पर शामिल नहीं किया जाता. जब बिना बताई गई किसी इनपुट फ़ाइल में बदलाव होता है, तब भी Bazel को लगता है कि बिल्ड अप-टू-डेट है. इसलिए, वह कार्रवाई को फिर से नहीं बनाता. इस वजह से, इंक्रीमेंटल बिल्ड गलत हो सकता है.

  • कैश मेमोरी की एंट्री का गलत तरीके से फिर से इस्तेमाल करने पर, रिमोट कैशिंग के दौरान समस्याएं आती हैं. शेयर की गई कैश मेमोरी में मौजूद गलत कैश मेमोरी एंट्री से, प्रोजेक्ट पर काम करने वाले हर डेवलपर पर असर पड़ता है. साथ ही, पूरी रिमोट कैश मेमोरी को मिटाना एक सही समाधान नहीं है.

  • सैंडबॉक्सिंग, रिमोट एक्सीक्यूशन की तरह काम करती है. अगर कोई बिल्ड सैंडबॉक्सिंग के साथ अच्छी तरह से काम करता है, तो वह रिमोट एक्सीक्यूशन के साथ भी काम करेगा. रिमोट एक्ज़ीक्यूशन को सभी ज़रूरी फ़ाइलें (स्थानीय टूल भी शामिल हैं) अपलोड करने की अनुमति देकर, कंपाइल क्लस्टर के रखरखाव की लागत को काफ़ी हद तक कम किया जा सकता है. ऐसा इसलिए, क्योंकि आपको हर बार क्लस्टर में मौजूद हर मशीन पर टूल इंस्टॉल नहीं करने पड़ेंगे. ऐसा तब होता है, जब आपको किसी नए कंपाइलर को आज़माना हो या किसी मौजूदा टूल में बदलाव करना हो.

सैंडबॉक्स की किस रणनीति का इस्तेमाल करना है

रणनीति के फ़्लैग के साथ, आपको किस तरह की सैंडबॉक्सिंग का इस्तेमाल करना है, यह चुना जा सकता है. sandboxed रणनीति का इस्तेमाल करने पर, Bazel नीचे दिए गए सैंडबॉक्स के किसी एक वर्शन को चुनता है. साथ ही, यह ओएस के हिसाब से बने सैंडबॉक्स को, कम सीलबंद सामान्य सैंडबॉक्स के मुकाबले ज़्यादा प्राथमिकता देता है. परसिस्टेंट वर्कर, सामान्य सैंडबॉक्स में चलते हैं. हालांकि, ऐसा तब होता है, जब आपने --worker_sandboxing फ़्लैग पास किया हो.

local (इसे standalone भी कहा जाता है) रणनीति में किसी भी तरह की सैंडबॉक्सिंग नहीं होती है. यह सिर्फ़ कार्रवाई की कमांड लाइन को लागू करता है. इसमें वर्किंग डायरेक्ट्री को आपके वर्कस्पेस के execroot पर सेट किया जाता है.

processwrapper-sandbox एक सैंडबॉक्सिंग रणनीति है. इसके लिए, किसी "ऐडवांस" सुविधा की ज़रूरत नहीं होती. यह किसी भी POSIX सिस्टम पर काम करती है. यह एक सैंडबॉक्स डायरेक्ट्री बनाता है. इसमें ऐसे सिमलंक होते हैं जो ओरिजनल सोर्स फ़ाइलों की ओर ले जाते हैं. इसके बाद, यह execroot के बजाय इस डायरेक्ट्री पर सेट की गई वर्किंग डायरेक्ट्री के साथ, कार्रवाई की कमांड लाइन को एक्ज़ीक्यूट करता है. इसके बाद, यह सैंडबॉक्स से जाने-पहचाने आउटपुट आर्टफ़ैक्ट को execroot में ले जाता है और सैंडबॉक्स को मिटा देता है. इससे, कार्रवाई को ऐसी किसी भी इनपुट फ़ाइल का इस्तेमाल करने से रोका जा सकता है जिसे घोषित नहीं किया गया है. साथ ही, इससे execroot को अनजान आउटपुट फ़ाइलों से भरने से भी रोका जा सकता है.

linux-sandbox, processwrapper-sandbox के ऊपर एक और लेयर बनाता है. यह Docker की तरह ही काम करता है. यह होस्ट से कार्रवाई को अलग करने के लिए, Linux नेमस्पेस (उपयोगकर्ता, माउंट, पीआईडी, नेटवर्क, और आईपीसी नेमस्पेस) का इस्तेमाल करता है. इसका मतलब है कि यह सैंडबॉक्स डायरेक्ट्री को छोड़कर, पूरे फ़ाइल सिस्टम को सिर्फ़ पढ़ने के लिए उपलब्ध कराता है. इसलिए, इस कार्रवाई से होस्ट फ़ाइल सिस्टम में गलती से कोई बदलाव नहीं होता. इससे ऐसी स्थितियों से बचा जा सकता है जहां गड़बड़ी वाला कोई टेस्ट, गलती से आपकी $HOME डायरेक्ट्री को rm -rf कर देता है. आपके पास कार्रवाई को नेटवर्क ऐक्सेस करने से रोकने का विकल्प भी होता है. linux-sandbox, PID नेमस्पेस का इस्तेमाल करता है, ताकि कार्रवाई के दौरान कोई दूसरी प्रोसेस न दिखे. साथ ही, कार्रवाई के आखिर में सभी प्रोसेस (कार्रवाई से शुरू होने वाले डेमॉन भी) को बंद किया जा सके.

darwin-sandbox भी इसी तरह काम करता है, लेकिन यह macOS के लिए है. यह Apple के sandbox-exec टूल का इस्तेमाल करता है, ताकि Linux सैंडबॉक्स की तरह ही काम किया जा सके.

ऑपरेटिंग सिस्टम की ओर से उपलब्ध कराए गए तरीकों में मौजूद पाबंदियों की वजह से, linux-sandbox और darwin-sandbox, दोनों "नेस्ट किए गए" परिदृश्य में काम नहीं करते. Docker भी कंटेनर के लिए Linux नेमस्पेस का इस्तेमाल करता है. इसलिए, linux-sandbox को Docker कंटेनर में आसानी से नहीं चलाया जा सकता. हालांकि, docker run --privileged का इस्तेमाल करके ऐसा किया जा सकता है. macOS पर, sandbox-exec को ऐसी प्रोसेस के अंदर नहीं चलाया जा सकता जिसे पहले से ही सैंडबॉक्स किया जा रहा है. इसलिए, ऐसे मामलों में Bazel अपने-आप processwrapper-sandbox का इस्तेमाल करने लगता है.

अगर आपको बिल्ड से जुड़ी गड़बड़ी का मैसेज चाहिए, तो Bazel की एक्ज़ीक्यूशन रणनीतियों की सूची में साफ़ तौर पर बदलाव करें. उदाहरण के लिए, bazel build --spawn_strategy=worker,linux-sandbox. ऐसा इसलिए, ताकि गलती से कम सख्त एक्ज़ीक्यूशन रणनीति का इस्तेमाल करके बिल्ड न किया जा सके.

डाइनैमिक एक्ज़ीक्यूशन के लिए, आम तौर पर लोकल एक्ज़ीक्यूशन के लिए सैंडबॉक्सिंग की ज़रूरत होती है. ऑप्ट आउट करने के लिए, --experimental_local_lockfree_output फ़्लैग पास करें. डाइनैमिक एक्ज़ीक्यूशन, पर्सिस्टेंट वर्कर को साइलेंट मोड में सैंडबॉक्स करता है.

सैंडबॉक्सिंग से जुड़ी समस्याएं

  • सैंडबॉक्सिंग के लिए, सेटअप और बंद करने का अतिरिक्त शुल्क लगता है. यह लागत कितनी ज़्यादा है, यह कई बातों पर निर्भर करती है. जैसे, बिल्ड का आकार और होस्ट ओएस का परफ़ॉर्मेंस. Linux के लिए, सैंडबॉक्स किए गए बिल्ड, कुछ प्रतिशत से ज़्यादा धीमे नहीं होते. --reuse_sandbox_directories सेट करने से, सेटअप और बंद करने की लागत कम हो सकती है.

  • सैंडबॉक्स की प्रोसेस, टूल की किसी भी कैश मेमोरी को बंद कर देती है. पर्सिस्टेंट वर्कर का इस्तेमाल करके, इस समस्या को कम किया जा सकता है. हालांकि, इससे सैंडबॉक्स की सुरक्षा से जुड़ी गारंटी कम हो जाती है.

  • मल्टीप्लेक्स वर्कर को सैंडबॉक्स करने के लिए, वर्कर की सहायता की ज़रूरत होती है. मल्टीप्लेक्स सैंडबॉक्सिंग की सुविधा के साथ काम न करने वाले वर्कर, डाइनैमिक एक्ज़ीक्यूशन के तहत सिंगलप्लेक्स वर्कर के तौर पर काम करते हैं. इससे ज़्यादा मेमोरी खर्च हो सकती है.

डीबग करना

सैंडबॉक्सिंग से जुड़ी समस्याओं को डीबग करने के लिए, यहां दी गई रणनीतियों का पालन करें.

बंद किए गए नेमस्पेस

कुछ प्लैटफ़ॉर्म पर, सुरक्षा से जुड़ी समस्याओं की वजह से उपयोगकर्ता नेमस्पेस डिफ़ॉल्ट रूप से बंद होते हैं. जैसे, Google Kubernetes Engine क्लस्टर नोड या Debian. अगर /proc/sys/kernel/unprivileged_userns_clone फ़ाइल मौजूद है और इसमें 0 है, तो उपयोगकर्ता नेमस्पेस चालू करने के लिए, यह कमांड चलाएं:

   sudo sysctl kernel.unprivileged_userns_clone=1

नियम लागू न होने की वजहें

सिस्टम सेटअप की वजह से, सैंडबॉक्स में नियमों को लागू नहीं किया जा सकता. अगर आपको namespace-sandbox.c:633: execvp(argv[0], argv): No such file or directory जैसा कोई मैसेज दिखता है, तो --strategy=Genrule=local की मदद से, सामान्य नियमों के लिए सैंडबॉक्स को बंद करने की कोशिश करें. साथ ही, --spawn_strategy=local की मदद से, अन्य नियमों के लिए सैंडबॉक्स को बंद करने की कोशिश करें.

बिल्ड फ़ेल होने की वजहों को डीबग करने की सुविधा

अगर आपकी बिल्ड प्रोसेस पूरी नहीं हुई है, तो --verbose_failures और --sandbox_debug का इस्तेमाल करें. इससे Bazel, वह कमांड दिखाएगा जिसे बिल्ड प्रोसेस पूरी न होने पर चलाया गया था. इसमें सैंडबॉक्स सेट अप करने वाला हिस्सा भी शामिल होगा.

गड़बड़ी के मैसेज का उदाहरण:

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)

अब जनरेट की गई सैंडबॉक्स डायरेक्ट्री की जांच की जा सकती है. साथ ही, यह देखा जा सकता है कि Bazel ने कौनसी फ़ाइलें बनाई हैं. इसके अलावा, कमांड को फिर से चलाकर यह देखा जा सकता है कि यह कैसे काम करती है.

ध्यान दें कि --sandbox_debug का इस्तेमाल करने पर, Bazel सैंडबॉक्स डायरेक्ट्री को नहीं मिटाता. अगर आपको डिबग नहीं करना है, तो आपको --sandbox_debug को बंद कर देना चाहिए. ऐसा इसलिए, क्योंकि समय के साथ यह आपकी डिस्क को भर देता है.