2. सैंडबॉक्स की नीति बनाना
एक्ज़ीक्यूटर तय करने के बाद, आपको सैंडबॉक्स के लिए सैंडबॉक्स की नीति तय करनी होगी. ऐसा न होने पर, सैंडबॉक्सी को सिर्फ़ डिफ़ॉल्ट सिसकॉल नीति के तहत सुरक्षित किया जाता है.
सैंडबॉक्स नीति का मकसद, उन सिसकॉल और तर्कों पर पाबंदी लगाना है जिन्हें सैंडबॉक्सी बना सकता है. साथ ही, उन फ़ाइलों पर पाबंदी लगाना है जिन्हें वह ऐक्सेस कर सकता है. आपको उस कोड के लिए ज़रूरी सिस्टम कॉल के बारे में पूरी जानकारी होनी चाहिए जिसे आपको सैंडबॉक्स करना है. सिस्टम कॉल को मॉनिटर करने का एक तरीका यह है कि कोड को Linux के कमांड-लाइन टूल strace के साथ चलाया जाए.
सिस्टम कॉल की सूची मिलने के बाद, नीति तय करने के लिए PolicyBuilder का इस्तेमाल किया जा सकता है. PolicyBuilder में कई सुविधाजनक और हेल्पर फ़ंक्शन होते हैं. इनकी मदद से, कई सामान्य कार्रवाइयां की जा सकती हैं. यहां दी गई सूची में, उपलब्ध फ़ंक्शन का सिर्फ़ एक छोटा सा हिस्सा दिया गया है:
- प्रोसेस शुरू करने के लिए, किसी भी syscall को अनुमति वाली सूची में शामिल करें:
AllowStaticStartup();
AllowDynamicStartup();
- किसी भी ओपन/रीड/राइट* सिसकॉल को अनुमति वाली सूची में जोड़ें:
AllowOpen();
AllowRead();
AllowWrite();
- निकास/ऐक्सेस/स्टेट से जुड़े किसी भी syscall को अनुमति वाली सूची में शामिल करें:
AllowExit();
AllowStat();
AllowAccess();
- नींद/समय से जुड़े किसी भी syscall को अनुमति वाली सूची में शामिल करें:
AllowTime();
AllowSleep();
ये सुविधा फ़ंक्शन, काम के किसी भी syscall को अनुमति देते हैं. इसका फ़ायदा यह है कि एक ही नीति का इस्तेमाल अलग-अलग आर्किटेक्चर पर किया जा सकता है. हालांकि, कुछ सिस्टम कॉल उपलब्ध नहीं होते. उदाहरण के लिए, ARM64 में OPEN सिस्टम कॉल नहीं होता. हालांकि, इसमें सुरक्षा से जुड़ा थोड़ा जोखिम होता है. ऐसा इसलिए, क्योंकि इसमें ज़रूरत से ज़्यादा सिस्टम कॉल चालू किए जा सकते हैं. उदाहरण के लिए, AllowOpen() की मदद से, सैंडबॉक्सी किसी भी ओपन से जुड़े syscall को कॉल कर सकता है. अगर आपको सिर्फ़ एक खास सिसकॉल को अनुमति देनी है, तो AllowSyscall();
का इस्तेमाल करें. एक साथ कई सिसकॉल को अनुमति देने के लिए, AllowSyscalls()
का इस्तेमाल करें.
अब तक यह नीति, सिर्फ़ syscall आइडेंटिफ़ायर की जाँच करती है. अगर आपको नीति को और मज़बूत करना है और ऐसी नीति तय करनी है जिसमें सिर्फ़ खास आर्ग्युमेंट वाले सिसकॉल की अनुमति हो, तो आपको AddPolicyOnSyscall()
या AddPolicyOnSyscalls()
का इस्तेमाल करना होगा. ये फ़ंक्शन, सिर्फ़ syscall आईडी को आर्ग्युमेंट के तौर पर नहीं लेते. इसके अलावा, ये Linux कर्नल से bpf हेल्पर मैक्रो का इस्तेमाल करके, रॉ seccomp-bpf फ़िल्टर को भी आर्ग्युमेंट के तौर पर लेते हैं. BPF के बारे में ज़्यादा जानने के लिए, कर्नेल के दस्तावेज़ देखें. अगर आपको बार-बार BPF कोड लिखना पड़ रहा है और आपको लगता है कि इसमें यूज़ेबिलिटी-रैपर होना चाहिए, तो बेझिझक तौर पर सुविधा के लिए अनुरोध करें.
सिस्टम कॉल से जुड़े फ़ंक्शन के अलावा, PolicyBuilder, फ़ाइल सिस्टम से जुड़े कई फ़ंक्शन भी उपलब्ध कराता है. जैसे, सैंडबॉक्स में किसी फ़ाइल/डायरेक्ट्री को बाइंड-माउंट करने के लिए AddFile()
या AddDirectory()
. AddTmpfs()
हेल्पर का इस्तेमाल, सैंडबॉक्स में अस्थायी फ़ाइल स्टोरेज जोड़ने के लिए किया जा सकता है.
खास तौर पर, AddLibrariesForBinary()
फ़ंक्शन बहुत काम का है. यह किसी बाइनरी के लिए ज़रूरी लाइब्रेरी और लिंकर जोड़ता है.
जिन सिस्टम कॉल को अनुमति देनी है उन्हें तय करने के लिए, अब भी कुछ काम मैन्युअल तरीके से करना पड़ता है. उन सिस्टम कॉल के साथ एक नीति बनाएं जिनकी आपकी बाइनरी को ज़रूरत है. इसके बाद, इसे सामान्य वर्कलोड के साथ चलाएं. अगर उल्लंघन का पता चलता है, तो syscall को अनुमति वाली सूची में शामिल करें और प्रोसेस को दोहराएं. अगर आपको लगता है कि कोई उल्लंघन हुआ है और उसे अनुमति वाली सूची में शामिल करना जोखिम भरा हो सकता है, तो BlockSyscallWithErrno()
का इस्तेमाल करके, उसे गड़बड़ी के तौर पर मार्क किया जा सकता है.
#include "sandboxed_api/sandbox2/policy.h"
#include "sandboxed_api/sandbox2/policybuilder.h"
#include "sandboxed_api/sandbox2/util/bpf_helper.h"
std::unique_ptr<sandbox2::Policy> CreatePolicy() {
return sandbox2::PolicyBuilder()
.AllowSyscall(__NR_read) // See also AllowRead()
.AllowTime() // Allow time, gettimeofday and clock_gettime
.AddPolicyOnSyscall(__NR_write, {
ARG(0), // fd is the first argument of write (argument #0)
JEQ(1, ALLOW), // allow write only on fd 1
KILL, // kill if not fd 1
})
.AddPolicyOnSyscall(__NR_mprotect, {
ARG_32(2), // prot is a 32-bit wide argument, so it's OK to use *_32
// macro here
JNE32(PROT_READ | PROT_WRITE, KILL), // prot must be the RW, otherwise
// kill the process
ARG(1), // len is a 64-bit argument
JNE(0x1000, KILL), // Allow single page syscalls only, otherwise kill
// the process
ALLOW, // Allow for the syscall to proceed, if prot and
// size match
})
// Allow the openat() syscall but always return "not found".
.BlockSyscallWithErrno(__NR_openat, ENOENT)
.BuildOrDie();
}