স্যান্ডবক্স 2 দিয়ে শুরু করা

এই পৃষ্ঠায়, আপনি শিখবেন কিভাবে Sandbox2 দিয়ে আপনার নিজস্ব স্যান্ডবক্সযুক্ত পরিবেশ তৈরি করবেন। আপনি শিখবেন কিভাবে একটি Sandbox Policy সংজ্ঞায়িত করতে হয়, এবং কিছু উন্নত, কিন্তু সাধারণ, পরিবর্তন। হেডার ফাইলগুলিতে উদাহরণ এবং কোড ডকুমেন্টেশনের পাশাপাশি এখানে তথ্যটি একটি নির্দেশিকা হিসাবে ব্যবহার করুন।

১. একটি স্যান্ডবক্স এক্সিকিউটর পদ্ধতি বেছে নিন

স্যান্ডবক্সিং একজন এক্সিকিউটর দিয়ে শুরু হয় ( স্যান্ডবক্স এক্সিকিউটর দেখুন), যা স্যান্ডবক্সি চালানোর জন্য দায়ী। executor.h হেডার ফাইলটিতে এই উদ্দেশ্যে প্রয়োজনীয় API রয়েছে। APIটি খুবই নমনীয় এবং আপনার ব্যবহারের ক্ষেত্রে কোনটি সবচেয়ে ভালো কাজ করে তা আপনাকে বেছে নিতে দেয়। নিম্নলিখিত বিভাগগুলিতে আপনি যে 3টি ভিন্ন পদ্ধতি থেকে বেছে নিতে পারেন তার বর্ণনা দেওয়া হয়েছে।

পদ্ধতি ১: স্বতন্ত্র - স্যান্ডবক্সিং ইতিমধ্যেই সক্ষম করে একটি বাইনারি কার্যকর করুন

এটি স্যান্ডবক্সিং ব্যবহারের সবচেয়ে সহজ উপায় এবং যখন আপনি একটি সম্পূর্ণ বাইনারি স্যান্ডবক্স করতে চান যার জন্য আপনার কোনও সোর্স কোড নেই তখন এটি প্রস্তাবিত পদ্ধতি। এটি স্যান্ডবক্সিং ব্যবহারের সবচেয়ে নিরাপদ উপায়, কারণ কোনও আনস্যান্ডবক্সড ইনিশিয়ালাইজেশন নেই যার বিরূপ প্রভাব পড়তে পারে।

নিচের কোড স্নিপেটে, আমরা স্যান্ডবক্স করার জন্য বাইনারিটির পথ এবং execve syscall-এ আমাদের যে আর্গুমেন্টগুলি পাস করতে হবে তা সংজ্ঞায়িত করেছি। executor.h হেডার ফাইলে আপনি দেখতে পাচ্ছেন, আমরা envp জন্য কোনও মান নির্দিষ্ট করি না এবং তাই প্যারেন্ট প্রক্রিয়া থেকে পরিবেশটি অনুলিপি করি। মনে রাখবেন, প্রথম আর্গুমেন্টটি সর্বদা কার্যকর করা প্রোগ্রামের নাম, এবং আমাদের স্নিপেট অন্য কোনও আর্গুমেন্টকে সংজ্ঞায়িত করে না।

এই এক্সিকিউটর পদ্ধতির উদাহরণ হল: স্ট্যাটিক এবং টুল

#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::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 হল স্যান্ডবক্সী এবং এক্সিকিউটর ( crc4sandbox.cc ) কে স্যান্ডবক্সে প্রবেশ করার সময় অবহিত করে।

পদ্ধতি ৩: কাস্টম ফোর্কসার্ভার - একটি বাইনারি প্রস্তুত করুন, ফর্ক অনুরোধের জন্য অপেক্ষা করুন এবং নিজেই স্যান্ডবক্স তৈরি করুন

এই মোড আপনাকে একটি বাইনারি শুরু করতে, স্যান্ডবক্সিংয়ের জন্য প্রস্তুত করতে এবং আপনার বাইনারির জীবনচক্রের একটি নির্দিষ্ট মুহূর্তে এটি নির্বাহকের কাছে উপলব্ধ করতে দেয়।

এক্সিকিউটর আপনার বাইনারিতে একটি ফর্ক রিকোয়েস্ট পাঠাবে, যা fork() ( ::sandbox2::ForkingClient::EnterForkLook() এর মাধ্যমে) করবে। নতুন তৈরি প্রক্রিয়াটি ::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 নেই। আরেকটি সাধারণ ব্যবহারের উদাহরণ হল যখন Sandboxee-তে একটি দীর্ঘ, CPU-নিবিড় ইনিশিয়ালাইজেশন থাকে যা অবিশ্বস্ত ডেটা প্রক্রিয়া করার আগে চালানো যেতে পারে।

এই এক্সিকিউটর পদ্ধতির উদাহরণের জন্য, custom_fork দেখুন।

2. একটি স্যান্ডবক্স নীতি তৈরি করুন

একবার আপনার একজন নির্বাহক হয়ে গেলে, আপনি সম্ভবত স্যান্ডবক্সীর জন্য একটি স্যান্ডবক্স নীতি নির্ধারণ করতে চাইবেন। অন্যথায়, স্যান্ডবক্সী কেবল ডিফল্ট সিসকল নীতি দ্বারা সুরক্ষিত থাকে।

স্যান্ডবক্স নীতিমালার উদ্দেশ্য হল স্যান্ডবক্সি যে সিস্টেমকল এবং আর্গুমেন্ট করতে পারে, সেইসাথে এটি যে ফাইলগুলি অ্যাক্সেস করতে পারে তা সীমাবদ্ধ করা। আপনি যে কোডটি স্যান্ডবক্স করার পরিকল্পনা করছেন তার জন্য প্রয়োজনীয় সিস্টেমকল সম্পর্কে আপনার বিস্তারিত ধারণা থাকতে হবে। সিস্টেমকল পর্যবেক্ষণ করার একটি উপায় হল লিনাক্সের কমান্ড-লাইন টুল স্ট্রেস দিয়ে কোডটি চালানো।

একবার আপনার কাছে সিস্টেমকলের তালিকা তৈরি হয়ে গেলে, আপনি পলিসি বিল্ডার ব্যবহার করে পলিসি নির্ধারণ করতে পারেন। পলিসিবিল্ডারে অনেক সুবিধাজনক এবং সহায়ক ফাংশন রয়েছে যা অনেক সাধারণ ক্রিয়াকলাপের অনুমতি দেয়। নিম্নলিখিত তালিকাটি উপলব্ধ ফাংশনগুলির একটি ছোট অংশ মাত্র:

  • প্রক্রিয়া শুরুর জন্য যেকোনো সিস্টেমকল অ্যালাউলিস্ট করুন:
    • AllowStaticStartup();
    • AllowDynamicStartup();
  • যেকোনো খোলা /পড়া / লেখার* সিস্টেমকলের তালিকা তৈরি করুন:
    • AllowOpen();
    • AllowRead();
    • AllowWrite();
  • যেকোনো প্রস্থান/অ্যাক্সেস/স্টেট সম্পর্কিত সিস্টেমকল অ্যালোলিস্ট করুন:
    • AllowExit();
    • AllowStat();
    • AllowAccess();
  • ঘুম/সময় সম্পর্কিত যেকোনো সিস্টেমকলের তালিকা তৈরি করুন:
    • AllowTime();
    • AllowSleep();

এই সুবিধাজনক ফাংশনগুলি যেকোনো প্রাসঙ্গিক syscall allowlist করে। এর সুবিধা হল একই নীতি বিভিন্ন আর্কিটেকচারে ব্যবহার করা যেতে পারে যেখানে নির্দিষ্ট syscall উপলব্ধ নেই (যেমন ARM64-তে কোনও OPEN syscall নেই), তবে প্রয়োজনের চেয়ে বেশি sycsall সক্ষম করার সামান্য নিরাপত্তা ঝুঁকি রয়েছে। উদাহরণস্বরূপ, AllowOpen() Sandboxee-কে যেকোনো open সম্পর্কিত syscall কল করতে সক্ষম করে। আপনি যদি শুধুমাত্র একটি নির্দিষ্ট syscall allowlist করতে চান, তাহলে আপনি AllowSyscall(); ব্যবহার করতে পারেন একসাথে একাধিক syscall অনুমোদন করতে আপনি AllowSyscalls() ব্যবহার করতে পারেন।

এখন পর্যন্ত নীতিটি শুধুমাত্র syscall শনাক্তকারী পরীক্ষা করে। যদি আপনার নীতিটিকে আরও শক্তিশালী করার প্রয়োজন হয় এবং এমন একটি নীতি সংজ্ঞায়িত করতে চান যেখানে আপনি শুধুমাত্র নির্দিষ্ট আর্গুমেন্ট সহ একটি syscall অনুমোদন করেন, তাহলে আপনাকে AddPolicyOnSyscall() অথবা AddPolicyOnSyscalls() ব্যবহার করতে হবে। এই ফাংশনগুলি কেবল syscall ID কে একটি আর্গুমেন্ট হিসাবে গ্রহণ করে না, বরং লিনাক্স কার্নেল থেকে bpf হেল্পার ম্যাক্রো ব্যবহার করে একটি raw seccomp-bpf ফিল্টারও গ্রহণ করে। BPF সম্পর্কে আরও তথ্যের জন্য কার্নেল ডকুমেন্টেশন দেখুন। যদি আপনি নিজেকে পুনরাবৃত্তিমূলক BPF কোড লিখতে দেখেন যা আপনার মনে হয় একটি ব্যবহারযোগ্যতা-র‍্যাপার থাকা উচিত, তাহলে একটি বৈশিষ্ট্য অনুরোধ দায়ের করতে দ্বিধা করবেন না।

syscall-সম্পর্কিত ফাংশন ছাড়াও, PolicyBuilder ফাইল/ডিরেক্টরিকে স্যান্ডবক্সে বাইন্ড-মাউন্ট করার জন্য AddFile() বা AddDirectory() এর মতো ফাইল সিস্টেম-সম্পর্কিত ফাংশনও প্রদান করে। AddTmpfs() হেল্পারটি স্যান্ডবক্সের মধ্যে একটি অস্থায়ী ফাইল স্টোরেজ যোগ করতে ব্যবহার করা যেতে পারে।

একটি বিশেষভাবে কার্যকর ফাংশন হল AddLibrariesForBinary() যা একটি বাইনারি দ্বারা প্রয়োজনীয় লাইব্রেরি এবং লিঙ্কার যোগ করে।

দুর্ভাগ্যবশত, অ্যালওয়ালিস্টে সিস্টেমকল তৈরি করা এখনও কিছুটা ম্যানুয়াল কাজ। আপনার বাইনারি চাহিদা সম্পর্কে জানা সিসকল দিয়ে একটি নীতি তৈরি করুন এবং একটি সাধারণ ওয়ার্কলোড দিয়ে এটি চালান। যদি কোনও লঙ্ঘন শুরু হয়, তাহলে সিস্টেমকলটি অ্যালওয়ালিস্ট করুন এবং প্রক্রিয়াটি পুনরাবৃত্তি করুন। যদি আপনি এমন কোনও লঙ্ঘনের সম্মুখীন হন যা আপনার মনে হয় অ্যালওয়ালিস্টের জন্য ঝুঁকিপূর্ণ হতে পারে এবং প্রোগ্রামটি ত্রুটিগুলি সুন্দরভাবে পরিচালনা করে, তাহলে আপনি 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();
}

3. সীমা সামঞ্জস্য করুন

স্যান্ডবক্স নীতি স্যান্ডবক্সিকে নির্দিষ্ট সিস্টেম কল কল করতে বাধা দেয় এবং এইভাবে আক্রমণের পৃষ্ঠকে হ্রাস করে। যাইহোক, একজন আক্রমণকারী এখনও অনির্দিষ্টকালের জন্য একটি প্রক্রিয়া চালিয়ে বা RAM এবং অন্যান্য সংস্থানগুলি ক্লান্ত করে অবাঞ্ছিত প্রভাব ফেলতে সক্ষম হতে পারে।

এই হুমকি মোকাবেলা করার জন্য, Sandboxee ডিফল্টভাবে কঠোর কার্যকর সীমার মধ্যে চলে। যদি এই ডিফল্ট সীমাগুলি আপনার প্রোগ্রামের বৈধ কার্যকরকরণে সমস্যা সৃষ্টি করে, তাহলে আপনি executor অবজেক্টে 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 ক্লাস ব্যবহারের উদাহরণের জন্য, example tool দেখুন।

৪. স্যান্ডবক্স চালান

পূর্ববর্তী বিভাগগুলিতে, আপনি স্যান্ডবক্সযুক্ত পরিবেশ, নীতি, এবং নির্বাহক এবং স্যান্ডবক্সি প্রস্তুত করেছেন। পরবর্তী ধাপ হল 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();

৫. স্যান্ডবক্সির সাথে যোগাযোগ করা

ডিফল্টরূপে, এক্সিকিউটর ফাইল বর্ণনাকারীর মাধ্যমে স্যান্ডবক্সির সাথে যোগাযোগ করতে পারে। আপনার প্রয়োজন হতে পারে এটিই, উদাহরণস্বরূপ, যদি আপনি কেবল স্যান্ডবক্সির সাথে একটি ফাইল ভাগ করতে চান, অথবা স্যান্ডবক্সির স্ট্যান্ডার্ড আউটপুট পড়তে চান।

তবে, সম্ভবত আপনার এক্সিকিউটর এবং স্যান্ডবক্সির মধ্যে আরও জটিল যোগাযোগের যুক্তির প্রয়োজন হবে। comms API ( comms.h হেডার ফাইলটি দেখুন) পূর্ণসংখ্যা, স্ট্রিং, বাইট বাফার, প্রোটোবাফ বা ফাইল বর্ণনাকারী পাঠাতে ব্যবহার করা যেতে পারে।

ফাইল বর্ণনাকারী ভাগ করে নেওয়া

ইন্টার-প্রসেস কমিউনিকেশন API ব্যবহার করে ( ipc.h দেখুন), আপনি MapFd() অথবা ReceiveFd() ব্যবহার করতে পারেন:

  • এক্সিকিউটর থেকে স্যান্ডবক্সিতে ফাইল বর্ণনাকারী ম্যাপ করতে MapFd() ব্যবহার করুন। স্যান্ডবক্সিতে ব্যবহারের জন্য এক্সিকিউটর থেকে খোলা ফাইল শেয়ার করতে এটি ব্যবহার করা যেতে পারে। স্ট্যাটিক তে ব্যবহারের একটি উদাহরণ দেখা যেতে পারে।

    // 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);
    

কমস এপিআই ব্যবহার করে

Sandbox2 একটি সুবিধাজনক comms API প্রদান করে। এটি এক্সিকিউটর এবং Sandboxee এর মধ্যে পূর্ণসংখ্যা, স্ট্রিং, বা বাইট বাফার শেয়ার করার একটি সহজ এবং সহজ উপায়। নিচে কিছু কোড স্নিপেট দেওয়া হল যা আপনি 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 উদাহরণ থেকে একটি উদ্ধৃতাংশ। এক্সিকিউটর একটি 32-বিট স্বাক্ষরবিহীন পূর্ণসংখ্যায় চেকসাম গ্রহণ করে: uint32_t crc4;

if (!(comms->RecvUint32(&crc4))) {
  /* handle error */
}

বাফারের সাথে ডেটা শেয়ার করা

আরেকটি ডেটা শেয়ারিং কার্যকারিতা হল বাফার API ব্যবহার করে প্রচুর পরিমাণে ডেটা শেয়ার করা এবং এক্সিকিউটর এবং স্যান্ডবক্সির মধ্যে বারবার পাঠানো ব্যয়বহুল কপি এড়ানো।

এক্সিকিউটর একটি বাফার তৈরি করে, হয় আকার এবং পাস করার জন্য ডেটা অনুসারে, অথবা সরাসরি একটি ফাইল বর্ণনাকারী থেকে, এবং এক্সিকিউটরে 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());

স্যান্ডবক্সির দিকে, আপনাকে একটি বাফার অবজেক্ট তৈরি করতে হবে এবং এক্সিকিউটরের পাঠানো ফাইল বর্ণনাকারী থেকে ডেটা পড়তে হবে:

// 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();

অ্যাসিঙ্ক্রোনাসভাবে চলমান একটি স্যান্ডবক্স থেকে বেরিয়ে আসা

যদি স্যান্ডবক্সটি অ্যাসিঙ্ক্রোনাসভাবে চলমান থাকে, তাহলে বন্ধ করার জন্য দুটি বিকল্প রয়েছে। প্রথমত, আপনি স্যান্ডবক্সি সম্পূর্ণ হওয়ার জন্য অপেক্ষা করতে পারেন এবং চূড়ান্ত কার্যকরকরণের অবস্থা পেতে পারেন:

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++ পরীক্ষার মতোই সহজ হতে পারে। অনুপ্রেরণার জন্য উদাহরণগুলি দেখুন।

উপসংহার

এতদূর পড়ার জন্য ধন্যবাদ, আমরা আশা করি আপনি আমাদের নির্দেশিকাটি পছন্দ করেছেন এবং এখন আপনার ব্যবহারকারীদের সুরক্ষিত রাখতে আপনার নিজস্ব স্যান্ডবক্স তৈরি করার ক্ষমতা অনুভব করছেন।

স্যান্ডবক্স এবং নীতি তৈরি করা একটি কঠিন কাজ এবং এতে সূক্ষ্ম ত্রুটি হওয়ার সম্ভাবনা থাকে। নিরাপদ থাকার জন্য, আমরা আপনাকে একজন নিরাপত্তা বিশেষজ্ঞের কাছ থেকে আপনার নীতি এবং কোড পর্যালোচনা করার পরামর্শ দিচ্ছি।