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

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

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

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

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

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

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

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

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

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

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

रणनीति के फ़्लैग के साथ, आपको किस तरह की सैंडबॉक्सिंग का इस्तेमाल करना है, यह चुना जा सकता है. sandboxed रणनीति का इस्तेमाल करने पर, Bazel नीचे दी गई सैंडबॉक्स की किसी एक सुविधा को चुनता है. इसमें ओएस के हिसाब से सैंडबॉक्स को, कम सीलबंद सामान्य सैंडबॉक्स की तुलना में प्राथमिकता दी जाती है. परसिस्टेंट वर्कर, सामान्य सैंडबॉक्स में चलते हैं. हालांकि, ऐसा तब होता है, जब --worker_sandboxing फ़्लैग पास किया जाता है. सामान्य बूलियन फ़ॉर्म के अलावा, फ़्लैग को --worker_sandboxing=<mnemonic>=<boolean> के साथ किसी खास वर्कर-की नेमोनिक के लिए स्कोप किया जा सकता है. उदाहरण के लिए, Javac को छोड़कर सभी सिंगलप्लेक्स वर्कर को सैंडबॉक्स करने के लिए --worker_sandboxing --worker_sandboxing=Javac=no. बाद में, सामान्य बूलियन वैल्यू फिर से सभी नेमोनिक पर लागू होती है.

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 नेमस्पेस का इस्तेमाल करता है. इसलिए, docker run --privileged का इस्तेमाल किए बिना, Docker कंटेनर में linux-sandbox को आसानी से नहीं चलाया जा सकता. macOS पर, sandbox-exec को ऐसी प्रोसेस में नहीं चलाया जा सकता जिसे पहले से ही सैंडबॉक्स किया जा रहा है. इसलिए, ऐसे मामलों में Bazel अपने-आप processwrapper-sandbox का इस्तेमाल करने लगता है.

अगर आपको बिल्ड से जुड़ी गड़बड़ी का मैसेज चाहिए, तो Bazel की उन रणनीतियों की सूची में साफ़ तौर पर बदलाव करें जिनका इस्तेमाल 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 ने कौनसी फ़ाइलें बनाई हैं. इसके अलावा, यह देखने के लिए कि Bazel कैसे काम करता है, कमांड को फिर से चलाया जा सकता है.

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