2. सैंडबॉक्स की नीति बनाना
एक्ज़ीक्यूटर मिलने के बाद, आपको सैंडबॉक्सी के लिए सैंडबॉक्स नीति तय करनी होगी. ऐसा न करने पर, सैंडबॉक्सी को सिर्फ़ डिफ़ॉल्ट Syscall नीति से सुरक्षा मिलती है.
सैंडबॉक्स की नीति का मकसद, उन syscall और आर्ग्युमेंट को सीमित करना है जिन्हें सैंडबॉक्सी इस्तेमाल कर सकता है. साथ ही, उन फ़ाइलों को भी सीमित करना है जिन्हें वह ऐक्सेस कर सकता है. आपको उस कोड के लिए ज़रूरी syscall के बारे में पूरी जानकारी होनी चाहिए जिसे सैंडबॉक्स में चलाने की योजना है. syscall को देखने का एक तरीका है कि कोड को Linux के कमांड-लाइन टूल strace के साथ चलाया जाए.
syscall की सूची मिलने के बाद, नीति तय करने के लिए PolicyBuilder का इस्तेमाल किया जा सकता है.
PolicyBuilder में कई काम के और हेल्पर फ़ंक्शन होते हैं. इनकी मदद से, कई
सामान्य कार्रवाइयां की जा सकती हैं. इनमें से कई फ़ंक्शन,
DefaultPolicyBuilder() में पहले से शामिल होते हैं. यहां दी गई सूची, उपलब्ध फ़ंक्शन का सिर्फ़ एक छोटा अंश है:
- प्रोसेस शुरू करने के लिए, किसी भी syscall को अनुमति वाली सूची में शामिल करना:
AllowStaticStartup();AllowDynamicStartup();
- किसी भी open/read/write* syscall को अनुमति वाली सूची में शामिल करना:
AllowOpen();AllowRead();AllowWrite();
- exit/access/state से जुड़े किसी भी syscall को अनुमति वाली सूची में शामिल करना:
AllowExit();AllowStat();AllowAccess();
- sleep/time से जुड़े किसी भी syscall को अनुमति वाली सूची में शामिल करना:
AllowTime();AllowSleep();
इन काम के फ़ंक्शन की मदद से, किसी भी काम के syscall को अनुमति वाली सूची में शामिल किया जा सकता है. इसका फ़ायदा यह है कि एक ही नीति का इस्तेमाल अलग-अलग आर्किटेक्चर पर किया जा सकता है. हालांकि, कुछ syscall उपलब्ध नहीं होते हैं. जैसे, ARM64 में OPEN syscall नहीं होता. हालांकि, इससे सुरक्षा से जुड़ा थोड़ा जोखिम होता है, क्योंकि इसमें ज़रूरत से ज़्यादा syscall चालू हो सकते हैं. उदाहरण के लिए, AllowOpen() की मदद से, सैंडबॉक्सी, open से जुड़े किसी भी syscall को कॉल कर सकता है. अगर आपको सिर्फ़ एक खास syscall को अनुमति वाली सूची में शामिल करना है, तो AllowSyscall(); का इस्तेमाल करें. एक साथ कई syscall को अनुमति वाली सूची में शामिल करने के लिए, AllowSyscalls() का इस्तेमाल करें.
अब तक, नीति सिर्फ़ syscall आइडेंटिफ़ायर की जांच करती है. अगर आपको नीति को और मज़बूत करना है और ऐसी नीति तय करनी है जिसमें सिर्फ़ खास आर्ग्युमेंट वाले syscall को अनुमति दी जाए, तो आपको AddPolicyOnSyscall() या AddPolicyOnSyscalls() का इस्तेमाल करना होगा. इन फ़ंक्शन में, syscall आईडी को आर्ग्युमेंट के तौर पर इस्तेमाल किया जाता है. साथ ही, Linux कर्नल से bpf हेल्पर मैक्रो का इस्तेमाल करके, रॉ seccomp-bpf फ़िल्टर का भी इस्तेमाल किया जाता है. BPF के बारे में ज़्यादा जानकारी के लिए, कर्नल
दस्तावेज़
देखें. अगर आपको बार-बार BPF कोड लिखना पड़ता है और आपको लगता है कि इसके लिए यूज़ेबिलिटी-रैपर होना चाहिए, तो बेझिझक सुविधा के लिए अनुरोध सबमिट करें.
syscall से जुड़े फ़ंक्शन के अलावा, PolicyBuilder में फ़ाइल सिस्टम से जुड़े कई फ़ंक्शन भी होते हैं. जैसे, AddFile() या AddDirectory(). इनकी मदद से, किसी फ़ाइल/डायरेक्ट्री को सैंडबॉक्स में बाइंड-माउंट किया जा सकता है. AddTmpfs() हेल्पर का इस्तेमाल करके, सैंडबॉक्स में अस्थायी फ़ाइल स्टोरेज जोड़ा जा सकता है.
AddLibrariesForBinary() एक खास काम का फ़ंक्शन है. इसकी मदद से, बाइनरी के लिए ज़रूरी लाइब्रेरी और लिंकर जोड़े जा सकते हैं.
दुर्भाग्य से, अनुमति वाली सूची में शामिल करने के लिए syscall तय करने का काम अब भी मैन्युअल तरीके से करना पड़ता है. उन syscall के साथ नीति बनाएं जिनकी ज़रूरत आपकी बाइनरी को है. इसके बाद, इसे सामान्य वर्कलोड के साथ चलाएं. अगर कोई उल्लंघन ट्रिगर होता है, तो syscall को अनुमति वाली सूची में शामिल करें और इस प्रोसेस को दोहराएं. अगर आपको कोई ऐसा उल्लंघन दिखता है जिसे अनुमति वाली सूची में शामिल करना जोखिम भरा हो सकता है और प्रोग्राम, गड़बड़ियों को आसानी से हैंडल करता है, तो BlockSyscallWithErrno() की मदद से, उसे गड़बड़ी दिखाने के लिए सेट किया जा सकता है.
यहां दिए गए उदाहरण में, sandbox2::PolicyBuilder को सीधे तौर पर बनाया गया है. इससे, write और mprotect आर्ग्युमेंट को सीमित करने और openat को ब्लॉक करने का तरीका दिखाया गया है. DefaultPolicyBuilder() की मदद से, openat को अनुमति दी जा सकती है:
#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();
}