এই পৃষ্ঠায়, আপনি Sandbox2-এর মাধ্যমে কীভাবে নিজের স্যান্ডবক্স এনভায়রনমেন্ট তৈরি করবেন তা জানতে পারবেন। আপনি কীভাবে Sandbox Policy নির্ধারণ করবেন এবং কিছু উন্নত, কিন্তু সাধারণ, টুইক সম্পর্কে জানবেন। এখানে দেওয়া তথ্য গাইড হিসেবে ব্যবহার করুন, এর সাথে উদাহরণ এবং হেডার ফাইলে কোড ডকুমেন্টেশন দেখুন।
১. স্যান্ডবক্স এক্সিকিউটর পদ্ধতি বেছে নিন
স্যান্ডবক্সিং একটি এক্সিকিউটর (স্যান্ডবক্স এক্সিকিউটর দেখুন) দিয়ে শুরু হয়, যা স্যান্ডবক্সি রান করার জন্য দায়বদ্ধ। executor.h হেডার ফাইলে এই উদ্দেশ্যে প্রয়োজনীয় API আছে। API-টি খুবই নমনীয় এবং আপনার ব্যবহারের ক্ষেত্রে কোনটি সবচেয়ে ভালো কাজ করে তা বেছে নিতে দেয়। নিম্নলিখিত বিভাগগুলি ৩টি আলাদা আলাদা পদ্ধতি বর্ণনা করে যেগুলি থেকে আপনি বেছে নিতে পারবেন।
পদ্ধতি ১: স্ট্যান্ড-অ্যালোন – স্যান্ডবক্সিং আগে থেকেই চালু আছে এমন বাইনারি এক্সিকিউট করুন
স্যান্ডবক্সিং ব্যবহার করার এটি হল সবচেয়ে সহজ উপায় এবং আপনি যদি এমন কোনও সম্পূর্ণ বাইনারি স্যান্ডবক্স করতে চান যার সোর্স কোড আপনার কাছে নেই, তাহলে এই পদ্ধতিটি ব্যবহার করার জন্য সাজেস্ট করা হয়। এটি স্যান্ডবক্সিং ব্যবহার করার সবচেয়ে নিরাপদ উপায়ও, কারণ এখানে কোনও আনস্যান্ডবক্সড ইনিশিয়ালাইজেশন নেই যা ক্ষতিকর প্রভাব ফেলতে পারে।
নিচে দেওয়া কোড স্নিপেটে, আমরা স্যান্ডবক্স করা বাইনারির পাথ এবং
execve syscall-এ পাস করার জন্য আর্গুমেন্ট উল্লেখ করেছি। আপনি যেমন
executor.h
হেডার ফাইলে দেখতে পাচ্ছেন, আমরা envp-এর জন্য কোনও ভ্যালু নির্দিষ্ট করি না এবং তাই পেরেন্ট প্রসেস থেকে
এনভায়রনমেন্ট কপি করি। মনে রাখবেন, প্রথম আর্গুমেন্টটি সবসময় এক্সিকিউট করা প্রোগ্রামের
নাম হয় এবং আমাদের স্নিপেটে অন্য কোনও
আর্গুমেন্ট সংজ্ঞায়িত করা হয় না।
এই এক্সিকিউটর পদ্ধতির উদাহরণ হল: static এবং tool।
#include "sandboxed_api/sandbox2/executor.h"
std::string path = "path/to/binary";
std::vector<std::string> args = {path}; // args[0] will become the sandboxed
// process' argv[0], typically the
// path to the binary.
auto executor = absl::make_unique<sandbox2::Executor>(path, args);
পদ্ধতি ২: Sandbox2 Forkserver – কখন স্যান্ডবক্স করতে হবে তা এক্সিকিউটরকে বলুন
এই পদ্ধতিতে ইনিশিয়ালাইজেশনের সময় স্যান্ডবক্সিং থেকে বেরিয়ে আসার সুবিধা পাওয়া যায়,
এবং তারপরে ::sandbox2::Client::SandboxMeHere() কল করে কখন স্যান্ডবক্সিংয়ে প্রবেশ করতে হবে তা বেছে নেওয়া যায়।
আপনি কখন স্যান্ডবক্সিং শুরু করতে চান তা কোডে সংজ্ঞায়িত করতে সক্ষম হতে হবে এবং এটি সিঙ্গেল-থ্রেডেড হতে হবে (কেন তা FAQ-তে পড়ুন)।
নিচে দেওয়া কোড স্নিপেটে, আমরা উপরে পদ্ধতি ১-এ উল্লেখ করা একই কোড ব্যবহার করি।
তবে, প্রোগ্রামটি শুরু করার সময় আনস্যান্ডবক্সড পদ্ধতিতে চালানোর অনুমতি দেওয়ার জন্য
আমরা set_enable_sandbox_before_exec(false) কল করি।
#include "sandboxed_api/sandbox2/executor.h"
std::string path = "path/to/binary";
std::vector<std::string> args = {path};
auto executor = absl::make_unique<sandbox2::Executor>(path, args);
executor->set_enable_sandbox_before_exec(false);
যেহেতু এক্সিকিউটরের কাছে এখন একটি বন্ধ করা স্যান্ডবক্স আছে যতক্ষণ না এটি স্যান্ডবক্সির দ্বারা জানানো হয়,
আমাদের একটি ::sandbox2::Client ইনস্ট্যান্স তৈরি করতে হবে, এক্সিকিউটর এবং স্যান্ডবক্সির মধ্যে
যোগাযোগ সেট-আপ করতে হবে এবং তারপরে এক্সিকিউটরকে জানাতে হবে
যে আমাদের ইনিশিয়ালাইজেশন শেষ হয়েছে এবং আমরা এখন sandbox2_client.SandboxMeHere() কল করে
স্যান্ডবক্সিং শুরু করতে চাই।
// main() of sandboxee
int main(int argc, char** argv) {
gflags::ParseCommandLineFlags(&argc, &argv, false);
// Set-up the sandbox2::Client object, using a file descriptor (1023).
sandbox2::Comms comms(sandbox2::Comms::kSandbox2ClientCommsFD);
sandbox2::Client sandbox2_client(&comms);
// Enable sandboxing from here.
sandbox2_client.SandboxMeHere();
…
এই এক্সিকিউটর পদ্ধতির একটি উদাহরণ হল
crc4, যেখানে crc4bin.cc হল
Sandboxee এবং কখন স্যান্ডবক্সে প্রবেশ করতে হবে তা এক্সিকিউটরকে (crc4sandbox.cc)
জানায়।
পদ্ধতি ৩: কাস্টম ফর্কসার্ভার – একটি বাইনারি প্রস্তুত করুন, ফর্ক অনুরোধের জন্য অপেক্ষা করুন এবং আপনার নিজের স্যান্ডবক্স করুন
এই মোড আপনাকে একটি বাইনারি শুরু করতে, স্যান্ডবক্সিংয়ের জন্য এটি প্রস্তুত করতে এবং আপনার বাইনারির লাইফসাইকেলের একটি নির্দিষ্ট মুহূর্তে এটি এক্সিকিউটরের জন্য উপলভ্য করতে দেয়।
এক্সিকিউটর আপনার বাইনারিতে একটি ফর্ক অনুরোধ পাঠাবে, যা fork() (::sandbox2::ForkingClient::EnterForkLoop()-এর মাধ্যমে)। নতুন তৈরি করা প্রসেসটি
::sandbox2::Client::SandboxMeHere()-এর সাথে স্যান্ডবক্স করার জন্য প্রস্তুত হবে।
#include "sandboxed_api/sandbox2/executor.h"
// Start the custom ForkServer
std::string path = "path/to/binary";
std::vector<std::string> args = {path};
auto fork_executor = absl::make_unique<sandbox2::Executor>(path, args);
fork_executor->StartForkServer();
// Initialize Executor with Comms channel to the ForkServer
auto executor = absl::make_unique<sandbox2::Executor>(
fork_executor->ipc()->GetComms());
মনে রাখবেন যে এই মোডটি বেশ জটিল এবং শুধুমাত্র কয়েকটি নির্দিষ্ট ক্ষেত্রে প্রযোজ্য; যেমন, আপনার মেমরির প্রয়োজনীয়তা খুব বেশি হলে। আপনি COW থেকে সুবিধা পাবেন তবে এর নেতিবাচক দিক হল যে কোনও আসল ASLR নেই। আরেকটি সাধারণ ব্যবহারের উদাহরণ হল, স্যান্ডবক্সিতে দীর্ঘ, CPU-ইনটেনসিভ ইনিশিয়ালাইজেশন আছে যা বিশ্বাসযোগ্য নয় এমন ডেটা প্রসেস করার আগে রান করা যেতে পারে।
এই এক্সিকিউটর পদ্ধতির উদাহরণ দেখতে, custom_fork দেখুন।
২. স্যান্ডবক্স নীতি তৈরি করা
আপনার কাছে এক্সিকিউটর থাকলে, আপনি সম্ভবত Sandboxee-এর জন্য একটি Sandbox নীতি সংজ্ঞায়িত করতে চাইবেন। অন্যথায়, Sandboxee শুধুমাত্র Default Syscall Policy দ্বারা সুরক্ষিত থাকে।
স্যান্ডবক্স নীতির মাধ্যমে, স্যান্ডবক্সির করা syscall এবং আর্গুমেন্ট এবং অ্যাক্সেস করা ফাইল সীমিত করাই হল উদ্দেশ্য। আপনি যে কোড স্যান্ডবক্স করার পরিকল্পনা করছেন, তার জন্য প্রয়োজনীয় syscalls সম্পর্কে আপনার বিস্তারিত ধারণা থাকতে হবে। সিসকল পর্যবেক্ষণ করার একটি উপায় হল Linux-এর কমান্ড-লাইন টুল strace-এর মাধ্যমে কোড রান করা।
আপনার কাছে syscall-এর তালিকা থাকলে, আপনি PolicyBuilder ব্যবহার করে নীতি নির্ধারণ করতে পারবেন।
PolicyBuilder-এ অনেক সুবিধা ও সহায়ক ফাংশন আছে যা অনেক
সাধারণ অপারেশন (যার মধ্যে অনেকগুলি আগেই
DefaultPolicyBuilder()-এ অন্তর্ভুক্ত করা হয়েছে) করতে দেয়। নিম্নলিখিত তালিকাটি উপলভ্য ফাংশনের
একটি ছোট অংশ মাত্র:
- প্রসেস স্টার্ট-আপের জন্য যেকোনও syscall-কে সাদা তালিকায় যোগ করুন:
AllowStaticStartup();AllowDynamicStartup();
- যেকোনও open/read/write* syscall-কে শ্বেত তালিকাভুক্ত করুন:
AllowOpen();AllowRead();AllowWrite();
- যেকোনও এক্সিট/অ্যাক্সেস/স্টেট সম্পর্কিত সিসকলকে অনুমতি তালিকায় যোগ করুন:
AllowExit();AllowStat();AllowAccess();
- স্লিপ/সময় সম্পর্কিত যেকোনও syscall অনুমোদিত তালিকায় যোগ করুন:
AllowTime();AllowSleep();
এইসব কনভেনিয়েন্স ফাংশন যেকোনও প্রাসঙ্গিক সিসকলকে হোয়াইটলিস্ট করে। এর সুবিধা হল যে একই নীতি বিভিন্ন আর্কিটেকচারে ব্যবহার করা যেতে পারে যেখানে
কিছু নির্দিষ্ট সিসকল উপলভ্য নেই (যেমন, ARM64-এ কোনও OPEN সিসকল নেই), তবে
প্রয়োজনীয় সিসকলের চেয়ে বেশি সিসকল চালু করার ফলে সামান্য নিরাপত্তা সংক্রান্ত ঝুঁকি থাকে। যেমন, AllowOpen() স্যান্ডবক্সিকে যেকোনও ওপেন সম্পর্কিত সিসকল কল করার অনুমতি দেয়। আপনি
যদি শুধুমাত্র একটি নির্দিষ্ট syscall সাদাতালিকাভুক্ত করতে চান, তাহলে আপনি AllowSyscall(); ব্যবহার করতে পারেন
একসাথে একাধিক syscall-এর অনুমতি দিতে আপনি AllowSyscalls() ব্যবহার করতে পারেন।
এখনও পর্যন্ত নীতিটি শুধুমাত্র syscall শনাক্তকারী চেক করে। আপনি যদি নীতিকে আরও শক্তিশালী করতে চান এবং এমন একটি নীতি নির্ধারণ করতে চান যেখানে আপনি শুধুমাত্র নির্দিষ্ট আর্গুমেন্ট সহ একটি syscall-কে অনুমতি দেন, তাহলে আপনাকে
AddPolicyOnSyscall() বা AddPolicyOnSyscalls() ব্যবহার করতে হবে। এই ফাংশনগুলি শুধু আর্গুমেন্ট হিসেবে
syscall ID নেয় না, তার সাথে Linux কার্নেল থেকে bpf হেল্পার ম্যাক্রো ব্যবহার করে
র seccomp-bpf ফিল্টারও নেয়। BPF সম্পর্কে আরও তথ্যের জন্য কার্নেল
ডকুমেন্টেশন
দেখুন। আপনি যদি দেখেন যে আপনি বারবার BPF কোড লিখছেন
যা আপনার মনে হয় যে একটি ইউজেবিলিটি-র্যাপার থাকা উচিত, তাহলে নির্দ্বিধায় একটি ফিচার
অনুরোধ ফাইল করুন।
syscall-সম্পর্কিত ফাংশন ছাড়াও, PolicyBuilder স্যান্ডবক্সে ফাইল/ডিরেক্টরি বাইন্ড-মাউন্ট করার জন্য
AddFile() বা AddDirectory()-এর মতো
বেশ কিছু ফাইলসিস্টেম-সম্পর্কিত ফাংশন প্রদান করে। AddTmpfs() হেল্পারকে
স্যান্ডবক্সের মধ্যে সাময়িক ফাইল স্টোরেজ যোগ করার জন্য ব্যবহার করা যেতে পারে।
AddLibrariesForBinary() হল একটি বিশেষ উপযোগী ফাংশন যা
লাইব্রেরি ও বাইনারির প্রয়োজনীয় লিঙ্কার যোগ করে।
সাদাতালিকায় syscalls নিয়ে আসা এখনও কিছুটা ম্যানুয়াল কাজ
দুর্ভাগ্যবশত। আপনার বাইনারির প্রয়োজন আছে এমন syscalls দিয়ে একটি নীতি তৈরি করুন এবং
সাধারণ ওয়ার্কলোড দিয়ে এটি রান করুন। কোনও লঙ্ঘন ট্রিগার হলে, syscall
সাদা তালিকায় যোগ করুন এবং প্রসেসটি আবার করুন। আপনি যদি এমন কোনও লঙ্ঘনের সম্মুখীন হন যা আপনি মনে করেন যে শ্বেততালিকাভুক্ত করার জন্য ঝুঁকিপূর্ণ হতে পারে
এবং প্রোগ্রামটি ত্রুটিগুলি সাবলীলভাবে পরিচালনা করে, তাহলে আপনি BlockSyscallWithErrno()-এর মাধ্যমে
এর পরিবর্তে একটি ত্রুটি রিটার্ন করার চেষ্টা করতে পারেন।
নিচের উদাহরণটি sandbox2::PolicyBuilder সরাসরি তৈরি করে
write ও mprotect আর্গুমেন্ট সীমাবদ্ধ করা এবং openat ব্লক করা দেখায়
(যা DefaultPolicyBuilder() অন্যথায় অনুমতি দিত):
#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();
}
৩. সীমা অ্যাডজাস্ট করুন
স্যান্ডবক্স নীতি স্যান্ডবক্সিকে নির্দিষ্ট সিসকল কল করা থেকে আটকায় এবং এর ফলে অ্যাটাক সারফেস কম হয়। তবে, কোনও আক্রমণকারী এখনও অনির্দিষ্টকালের জন্য একটি প্রসেস চালিয়ে বা RAM এবং অন্যান্য রিসোর্স শেষ করে অবাঞ্ছিত প্রভাব ফেলতে পারে।
এই হুমকি মোকাবিলা করতে, Sandboxee ডিফল্ট হিসেবে কঠোর এক্সিকিউশন সীমার মধ্যে
রান করে। এই ডিফল্ট সীমা আপনার প্রোগ্রামের বৈধ এক্সিকিউশনে সমস্যা সৃষ্টি করলে, আপনি এক্সিকিউটর অবজেক্টে limits() কল করে sandbox2::Limits ক্লাস ব্যবহার করে
সেগুলি অ্যাডজাস্ট করতে পারেন।
নিচের কোড স্নিপেটে সীমা অ্যাডজাস্ট করার কিছু উদাহরণ দেখানো হয়েছে। উপলভ্য সব বিকল্প limits.h হেডার ফাইলে ডকুমেন্ট করা আছে।
// Restrict the address space size of the sandboxee to 4 GiB.
executor->limits()->set_rlimit_as(4ULL << 30);
// Kill sandboxee with SIGXFSZ if it writes more than 1 GiB to the filesystem.
executor->limits()->set_rlimit_fsize(1ULL << 30);
// Number of file descriptors which can be used by the sandboxee.
executor->limits()->set_rlimit_nofile(1ULL << 10);
// The sandboxee is not allowed to create core files.
executor->limits()->set_rlimit_core(0);
// Maximum 300s of real CPU time.
executor->limits()->set_rlimit_cpu(300);
// Maximum 120s of wall time.
executor->limits()->set_walltime_limit(absl::Seconds(120));
sandbox2::Limits ক্লাস ব্যবহারের উদাহরণ দেখতে, টুল
উদাহরণ দেখুন।
৪. স্যান্ডবক্স চালান
আগের বিভাগে, আপনি স্যান্ডবক্সড এনভায়রনমেন্ট, নীতি এবং
এক্সিকিউটর ও স্যান্ডবক্সী তৈরি করেছেন। পরবর্তী ধাপ হল Sandbox2 অবজেক্ট তৈরি করা এবং
এটি রান করা।
সিনক্রোনাস মোডে চালানো
স্যান্ডবক্স সিঙ্ক্রোনাসভাবে চলতে পারে, তাই ফলাফল না পাওয়া পর্যন্ত ব্লক করে। নিচে
দেওয়া কোড স্নিপেট থেকে Sandbox2 অবজেক্টের ইনস্ট্যান্টিয়েশন এবং
তার সিঙ্ক্রোনাস এক্সিকিউশন সম্পর্কে জানা যাবে। আরও বিস্তারিত উদাহরণের জন্য, স্ট্যাটিক দেখুন।
#include "sandboxed_api/sandbox2/sandbox2.h"
sandbox2::Sandbox2 s2(std::move(executor), std::move(policy));
sandbox2::Result result = s2.Run(); // Synchronous
LOG(INFO) << "Result of sandbox execution: " << result.ToString();
অ্যাসিঙ্ক্রোনাসভাবে চালান
এছাড়াও, আপনি অ্যাসিঙ্ক্রোনাসভাবে স্যান্ডবক্স চালাতে পারেন, যাতে ফলাফল না পাওয়া পর্যন্ত ব্লক না হয়। এটি স্যান্ডবক্সির সাথে যোগাযোগ করার সময় কাজে লাগে। নিচের কোড স্নিপেট এই ব্যবহারের উদাহরণ দেখায়, আরও বিস্তারিত উদাহরণের জন্য crc4 এবং tool দেখুন।
#include "sandboxed_api/sandbox2/sandbox2.h"
sandbox2::Sandbox2 s2(std::move(executor), std::move(policy));
if (s2.RunAsync()) {
// Communicate with sandboxee, use s2.Kill() to kill it if needed
// ...
}
Sandbox2::Result result = s2.AwaitResult();
LOG(INFO) << "Final execution status: " << result.ToString();
৫. স্যান্ডবক্সির সাথে যোগাযোগ করা
ডিফল্ট হিসেবে, ফাইল ডেসক্রিপ্টরের মাধ্যমে এক্সিকিউটর স্যান্ডবক্সির সাথে যোগাযোগ করতে পারে। আপনার হয়ত শুধু এইটুকুই প্রয়োজন, যেমন, আপনি যদি শুধু Sandboxee-এর সাথে কোনও ফাইল শেয়ার করতে চান অথবা Sandboxee-এর স্ট্যান্ডার্ড আউটপুট পড়তে চান।
তবে, এক্সিকিউটর ও স্যান্ডবক্সির মধ্যে আরও জটিল কমিউনিকেশন লজিকের প্রয়োজন হতে পারে। comms API (comms.h হেডার ফাইল দেখুন) ব্যবহার করে ইন্টিজার, স্ট্রিং, বাইট বাফার, protobuf বা ফাইল ডেসক্রিপটর পাঠানো যেতে পারে।
ফাইল ডেসক্রিপটর শেয়ার করা
ইন্টার-প্রসেস কমিউনিকেশন API (দেখুন
ipc.h)
ব্যবহার করে আপনি MapFd() বা ReceiveFd() ব্যবহার করতে পারবেন:
এক্সিকিউটর থেকে Sandboxee-তে ফাইল ডেসক্রিপটর ম্যাপ করতে
MapFd()ব্যবহার করুন। এক্সিকিউটর থেকে খোলা ফাইল শেয়ার করার জন্য এটি ব্যবহার করা যেতে পারে, যাতে সেটি Sandboxee-তে ব্যবহার করা যায়। এর ব্যবহারের একটি উদাহরণ স্ট্যাটিক-এ দেখা যেতে পারে।// The executor opened /proc/version and passes it to the sandboxee as stdin executor->ipc()->MapFd(proc_version_fd, STDIN_FILENO);socketpair এন্ডপয়েন্ট তৈরি করতে
ReceiveFd()ব্যবহার করুন। এটি Sandboxee-এর স্ট্যান্ডার্ড আউটপুট বা স্ট্যান্ডার্ড এরর পড়ার জন্য ব্যবহার করা যেতে পারে। টুল-এ ব্যবহারের একটি উদাহরণ দেখা যেতে পারে।// The executor receives a file descriptor of the sandboxee stdout int recv_fd1 = executor->ipc())->ReceiveFd(STDOUT_FILENO);
comms API ব্যবহার করা
Sandbox2 একটি সুবিধাজনক comms API প্রদান করে। এটি হল এক্সিকিউটর ও স্যান্ডবক্সির মধ্যে পূর্ণসংখ্যা, স্ট্রিং বা বাইট বাফার শেয়ার করার একটি সহজ ও সরল উপায়। নিচে কিছু কোড স্নিপেট দেওয়া হল যা আপনি crc4 উদাহরণে দেখতে পাবেন।
comms API ব্যবহার শুরু করতে, আপনাকে প্রথমে Sandbox2 অবজেক্ট থেকে comms অবজেক্ট পেতে হবে:
sandbox2::Comms* comms = s2.comms();
comms অবজেক্ট উপলভ্য হলে, Send* ফ্যামিলি অফ ফাংশনের মধ্যে থেকে একটি ব্যবহার করে Sandboxee-তে ডেটা পাঠানো যেতে পারে। crc4 উদাহরণে
আপনি comms API-এর একটি উদাহরণ দেখতে পাবেন। নিচে দেখানো কোড স্নিপেট
সেই উদাহরণের একটি উদ্ধৃতি। অছি SendBytes(buf, size)-এর সাথে unsigned char
buf[size] পাঠান:
if (!(comms->SendBytes(static_cast<const uint8_t*>(buf), sz))) {
/* handle error */
}
Sandboxee থেকে ডেটা পেতে, Recv* ফাংশনগুলির মধ্যে একটি ব্যবহার করুন। নিচের কোড
স্ন্যাপশটটি crc4 উদাহরণের
একটি উদ্ধৃতি। এক্সিকিউটর ৩২-বিট আনসাইনড ইন্টিজারে
চেকসাম পায়: uint32_t crc4;
if (!(comms->RecvUint32(&crc4))) {
/* handle error */
}
Buffer-এর সাথে ডেটা শেয়ার করা
ডেটা শেয়ার করার আরেকটি কার্যকারিতা হল বাফার API ব্যবহার করে প্রচুর পরিমাণে ডেটা শেয়ার করা এবং এক্সিকিউটর ও Sandboxee-এর মধ্যে আদানপ্রদান করা ডেটার কপি তৈরি করার খরচ এড়ানো।
এক্সিকিউটর একটি বাফার তৈরি করে, এটি সাইজ ও পাস করার ডেটা অনুযায়ী অথবা সরাসরি
ফাইল ডেসক্রিপ্টর থেকে তৈরি করা হয় এবং comms->SendFD()
এক্সিকিউটরে ও comms->RecvFD() স্যান্ডবক্সিতে ব্যবহার করে এটি স্যান্ডবক্সিতে পাস করা হয়।
নিচে দেওয়া কোড স্নিপেটে, আপনি এক্সিকিউটরের দিকটি দেখতে পাবেন। স্যান্ডবক্স অ্যাসিঙ্ক্রোনাসভাবে রান করে এবং স্যান্ডবক্সির সাথে বাফারের মাধ্যমে ডেটা শেয়ার করে:
// start the sandbox asynchronously
s2.RunAsync();
// instantiate the comms object
sandbox2::Comms* comms = s2.comms();
// random buffer data we want to send
constexpr unsigned char buffer_data[] = /* random data */;
constexpr unsigned int buffer_dataLen = 34;
// create sandbox2 buffer
absl::StatusOr<std::unique_ptr<sandbox2::Buffer>> buffer =
sandbox2::Buffer::CreateWithSize(1ULL << 20 /* 1Mib */);
std::unique_ptr<sandbox2::Buffer> buffer_ptr = std::move(buffer).value();
// point to the sandbox2 buffer and fill with data
uint8_t* buf = buffer_ptr‑>data();
memcpy(buf, buffer_data, buffer_data_len);
// send the data to the sandboxee
comms‑>SendFd(buffer_ptr‑>fd());
Sandboxee-এর দিকে, আপনাকে একটি বাফার অবজেক্ট তৈরি করতে হবে এবং এক্সিকিউটরের পাঠানো ফাইল ডেসক্রিপটর থেকে ডেটা পড়তে হবে:
// establish the communication with the executor
int fd;
comms.RecvFD(&fd);
// create the buffer
absl::StatusOr<std::unique_ptr<sandbox2::Buffer>> buffer =
sandbox2::Buffer::createFromFd(fd);
// get the data
auto buffer_ptr = std::move(buffer).value();
uint8_t* buf = buffer_ptr‑>data();
/* work with the buf object */
৬. স্যান্ডবক্স থেকে বেরিয়ে আসা
আপনি কীভাবে স্যান্ডবক্স চালান (এই ধাপ দেখুন) তার উপর নির্ভর করে, আপনাকে স্যান্ডবক্স বন্ধ করার পদ্ধতি অ্যাডজাস্ট করতে হবে এবং এর ফলে স্যান্ডবক্সিকেও অ্যাডজাস্ট করতে হবে।
সিনক্রোনাস মোডে চলা স্যান্ডবক্স থেকে বেরিয়ে আসা
স্যান্ডবক্স সিঙ্ক্রোনাসভাবে চলতে থাকলে, স্যান্ডবক্সি শেষ হলেই রান ফিরে আসবে। তাই, বন্ধ করার জন্য কোনও অতিরিক্ত ধাপের প্রয়োজন নেই। নিচে দেওয়া কোড স্নিপেট এই পরিস্থিতি দেখায়:
Sandbox2::Result result = s2.Run();
LOG(INFO) << "Final execution status: " << result.ToString();
অ্যাসিঙ্ক্রোনাসভাবে চলা স্যান্ডবক্স থেকে বেরিয়ে আসা
স্যান্ডবক্স অ্যাসিঙ্ক্রোনাসভাবে চলতে থাকলে, বন্ধ করার জন্য দুটি বিকল্প উপলভ্য থাকে। প্রথমে, আপনি Sandboxee সম্পূর্ণ হওয়ার জন্য অপেক্ষা করতে পারেন এবং এক্সিকিউশনের চূড়ান্ত স্ট্যাটাস পেতে পারেন:
sandbox2::Result result = s2.AwaitResult();
LOG(INFO) << "Final execution status: " << result.ToString();
বিকল্প হিসেবে, আপনি যেকোনও সময় Sandboxee বন্ধ করে দিতে পারেন, তবে এখনও
AwaitResult() কল করার সাজেশন দেওয়া হয় কারণ Sandboxee এর মধ্যে
অন্য কোনও কারণে বন্ধ হয়ে যেতে পারে:
s2.Kill();
sandbox2::Result result = s2.AwaitResult();
LOG(INFO) << "Final execution status: " << result.ToString();
৭. পরীক্ষা
অন্য যেকোনও কোডের মতো, আপনার স্যান্ডবক্স প্রয়োগে টেস্ট থাকতে হবে। স্যান্ডবক্স পরীক্ষা প্রোগ্রামের সঠিকতা পরীক্ষা করার জন্য নয়, বরং স্যান্ডবক্স করা প্রোগ্রামটি স্যান্ডবক্স লঙ্ঘনের মতো সমস্যা ছাড়াই চলতে পারে কিনা তা পরীক্ষা করার জন্য। এটি স্যান্ডবক্স নীতি সঠিক কিনা তাও নিশ্চিত করে।
প্রোডাকশনে আপনি যেভাবে কোনও স্যান্ডবক্সড প্রোগ্রাম রান করান, ঠিক সেইভাবেই সেটি টেস্ট করা হয়, যেসব আর্গুমেন্ট ও ইনপুট ফাইল সাধারণত প্রসেস করা হয় সেগুলি দিয়েই এটি টেস্ট করা হয়।
এই টেস্টগুলি শেল টেস্ট বা সাব-প্রসেস ব্যবহার করে C++ টেস্টের মতো সহজ হতে পারে। অনুপ্রেরণা পেতে উদাহরণগুলি দেখুন।
উপসংহার
এই পর্যন্ত পড়ার জন্য ধন্যবাদ, আমরা আশা করি যে আপনি আমাদের গাইড পছন্দ করেছেন এবং এখন আপনার ব্যবহারকারীদের সুরক্ষিত রাখতে সাহায্য করার জন্য আপনার নিজস্ব স্যান্ডবক্স তৈরি করার জন্য নিজেকে ক্ষমতায়িত বোধ করছেন।
স্যান্ডবক্স ও নীতি তৈরি করা কঠিন কাজ এবং এতে সূক্ষ্ম ভুল হওয়ার সম্ভাবনা থাকে। নিরাপদ থাকতে, আমরা সাজেস্ট করি যে কোনও নিরাপত্তা বিশেষজ্ঞকে দিয়ে আপনার নীতি ও কোড পর্যালোচনা করিয়ে নিন।