เริ่มต้นใช้งาน Sandbox2

ในหน้านี้ คุณจะได้เรียนรู้วิธีสร้างสภาพแวดล้อมแซนด์บ็อกซ์ของคุณเองด้วย Sandbox2 คุณจะได้เรียนรู้วิธีกำหนดนโยบาย Sandbox และการปรับแต่งขั้นสูง แต่เป็นเรื่องปกติ ใช้ข้อมูลที่นี่เป็นแนวทางควบคู่ไปกับตัวอย่างและเอกสารประกอบโค้ดในไฟล์ส่วนหัว

1. เลือกวิธีการดำเนินการแซนด์บ็อกซ์

การแซนด์บ็อกซ์เริ่มต้นด้วย Executor (ดูSandbox Executor) ซึ่งมีหน้าที่ รับผิดชอบในการเรียกใช้ Sandboxee ไฟล์ส่วนหัว executor.h มี API ที่จำเป็นสำหรับวัตถุประสงค์นี้ API มีความยืดหยุ่นสูง และช่วยให้คุณเลือกสิ่งที่เหมาะกับกรณีการใช้งานของคุณมากที่สุดได้ ส่วนต่อไปนี้ จะอธิบาย 3 วิธีที่แตกต่างกันซึ่งคุณเลือกใช้ได้

วิธีที่ 1: แบบสแตนด์อโลน - เรียกใช้ไบนารีที่เปิดใช้แซนด์บ็อกซ์อยู่แล้ว

นี่เป็นวิธีที่ง่ายที่สุดในการใช้แซนด์บ็อกซ์ และเป็นวิธีที่แนะนำเมื่อ คุณต้องการแซนด์บ็อกซ์ไบนารีทั้งหมดที่คุณไม่มีซอร์สโค้ด นอกจากนี้ยังเป็นวิธีที่ปลอดภัยที่สุดในการใช้แซนด์บ็อกซ์ เนื่องจากไม่มีการเริ่มต้นที่ไม่ใช่แซนด์บ็อกซ์ ซึ่งอาจส่งผลเสีย

ในข้อมูลโค้ดต่อไปนี้ เราจะกำหนดเส้นทางของไบนารีที่จะแซนด์บ็อกซ์ และอาร์กิวเมนต์ที่เราต้องส่งไปยัง Syscall execve ดังที่คุณเห็นในไฟล์ส่วนหัว 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);

วิธีที่ 2: Forkserver ของ Sandbox2 - บอก Executor ว่าเมื่อใดควรอยู่ใน Sandbox

วิธีนี้ช่วยให้มีความยืดหยุ่นในการยกเลิกการแซนด์บ็อกซ์ระหว่างการเริ่มต้น และเลือกเวลาที่จะเข้าสู่แซนด์บ็อกซ์ได้โดยการเรียกใช้ ::sandbox2::Client::SandboxMeHere() คุณต้องกำหนดในโค้ดได้ว่าต้องการเริ่มแซนด์บ็อกซ์เมื่อใด และต้องเป็นแบบ Single-Thread (อ่านเหตุผลในคำถามที่พบบ่อย)

ในข้อมูลโค้ดต่อไปนี้ เราใช้โค้ดเดียวกันกับที่ระบุไว้ในวิธีที่ 1 ด้านบน อย่างไรก็ตาม เพื่อให้โปรแกรมทำงานในลักษณะที่ไม่ได้อยู่ในแซนด์บ็อกซ์ ในระหว่างการเริ่มต้น เราจึงเรียกใช้ 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);

เนื่องจากตอนนี้ตัวดำเนินการมีแซนด์บ็อกซ์ที่ปิดใช้จนกว่าจะได้รับการแจ้งเตือนจาก Sandboxee เราจึงต้องสร้างอินสแตนซ์ ::sandbox2::Client ตั้งค่า การสื่อสารระหว่างตัวดำเนินการกับ Sandboxee แล้วแจ้งให้ตัวดำเนินการทราบ ว่าการเริ่มต้นของเราเสร็จสิ้นแล้ว และเราต้องการเริ่มแซนด์บ็อกซ์ตอนนี้โดย เรียกใช้ 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 และจะแจ้งให้ Executor (crc4sandbox.cc) ทราบเมื่อควรเข้าสู่ แซนด์บ็อกซ์

วิธีที่ 3: Forkserver ที่กำหนดเอง - เตรียมไบนารี รอคำขอ Fork และแซนด์บ็อกซ์ด้วยตนเอง

โหมดนี้ช่วยให้คุณเริ่มไบนารี เตรียมไบนารีสำหรับการแซนด์บ็อกซ์ และทำให้ไบนารีพร้อมใช้งานสำหรับผู้ปฏิบัติการใน ช่วงเวลาที่เฉพาะเจาะจงของวงจรชีวิตของไบนารี

โปรแกรมเรียกใช้จะส่งคำขอ Fork ไปยังไบนารีของคุณ ซึ่งจะ 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 เป็นเวลานาน ซึ่งสามารถเรียกใช้ได้ก่อนที่จะประมวลผลข้อมูลที่ไม่น่าเชื่อถือ

ดูตัวอย่างของเมธอด Executor นี้ได้ที่ custom_fork

2. สร้างนโยบายแซนด์บ็อกซ์

เมื่อมีโปรแกรมเรียกใช้แล้ว คุณอาจต้องกำหนดนโยบาย Sandbox สำหรับ Sandboxee ไม่เช่นนั้น Sandboxee จะได้รับการปกป้องโดยนโยบาย Syscall เริ่มต้นเท่านั้น

นโยบายแซนด์บ็อกซ์มีวัตถุประสงค์เพื่อจำกัด Syscall และอาร์กิวเมนต์ ที่ Sandboxee สามารถทำได้ รวมถึงไฟล์ที่เข้าถึงได้ คุณจะต้องมีความเข้าใจโดยละเอียดเกี่ยวกับ Syscall ที่โค้ดที่คุณวางแผน จะแซนด์บ็อกซ์ต้องการ วิธีหนึ่งในการสังเกต Syscall คือการเรียกใช้โค้ดด้วยเครื่องมือบรรทัดคำสั่ง strace ของ Linux

เมื่อมีรายการ Syscall แล้ว คุณจะใช้ PolicyBuilder เพื่อกำหนดนโยบายได้ PolicyBuilder มาพร้อมฟังก์ชันอำนวยความสะดวกและฟังก์ชันตัวช่วยมากมาย ที่ช่วยให้ดำเนินการทั่วไปได้หลายอย่าง รายการต่อไปนี้เป็นเพียง ตัวอย่างฟังก์ชันที่มีอยู่

  • เพิ่ม Syscall ลงในรายการที่อนุญาตสำหรับการเริ่มต้นกระบวนการ
    • AllowStaticStartup();
    • AllowDynamicStartup();
  • เพิ่ม Syscall ที่เปิด/อ่าน/เขียน* ลงในรายการที่อนุญาต
    • AllowOpen();
    • AllowRead();
    • AllowWrite();
  • อนุญาตรายการ syscall ที่เกี่ยวข้องกับการออก/การเข้าถึง/สถานะ
    • AllowExit();
    • AllowStat();
    • AllowAccess();
  • อนุญาตรายการ syscall ที่เกี่ยวข้องกับการนอนหลับ/เวลา
    • AllowTime();
    • AllowSleep();

ฟังก์ชันอำนวยความสะดวกเหล่านี้จะอนุญาตรายการ Syscall ที่เกี่ยวข้อง ซึ่งมีข้อดีคือสามารถใช้นโยบายเดียวกันกับสถาปัตยกรรมต่างๆ ที่ไม่มี Syscall บางอย่าง (เช่น ARM64 ไม่มี Syscall OPEN) แต่มีความเสี่ยงด้านความปลอดภัยเล็กน้อยจากการเปิดใช้ Syscall มากกว่าที่จำเป็น ตัวอย่างเช่น AllowOpen() ช่วยให้ Sandboxee เรียก syscall ที่เกี่ยวข้องกับการเปิดได้ หากต้องการอนุญาตเฉพาะ Syscall รายการเดียว คุณสามารถใช้ AllowSyscall(); หากต้องการอนุญาต Syscall หลายรายการพร้อมกัน คุณสามารถใช้ AllowSyscalls()

ปัจจุบันนโยบายจะตรวจสอบเฉพาะตัวระบุ Syscall หากต้องการเสริมความแข็งแกร่งของนโยบายเพิ่มเติมและต้องการกำหนดนโยบายที่อนุญาตเฉพาะ Syscall ที่มีอาร์กิวเมนต์ที่เฉพาะเจาะจง คุณต้องใช้ AddPolicyOnSyscall() หรือ AddPolicyOnSyscalls() ฟังก์ชันเหล่านี้ไม่เพียงใช้รหัส syscall เป็นอาร์กิวเมนต์เท่านั้น แต่ยังใช้ตัวกรอง seccomp-bpf ดิบโดยใช้มาโครตัวช่วย bpf จากเคอร์เนล Linux ด้วย ดูข้อมูลเพิ่มเติมเกี่ยวกับ BPF ได้ที่เอกสารประกอบ เคอร์เนล หากคุณพบว่าตัวเองเขียนโค้ด BPF ซ้ำๆ ซึ่งคุณคิดว่าควรมี Wrapper ที่ใช้งานง่าย โปรดส่งคำขอฟีเจอร์

นอกเหนือจากฟังก์ชันที่เกี่ยวข้องกับ Syscall แล้ว PolicyBuilder ยังมีฟังก์ชันที่เกี่ยวข้องกับระบบไฟล์อีกหลายฟังก์ชัน เช่น AddFile() หรือ AddDirectory() เพื่อ ผูกเมานต์ไฟล์/ไดเรกทอรีลงในแซนด์บ็อกซ์ คุณใช้โปรแกรมช่วย AddTmpfs() เพื่อเพิ่มพื้นที่เก็บข้อมูลไฟล์ชั่วคราวภายในแซนด์บ็อกซ์ได้

ฟังก์ชันที่มีประโยชน์อย่างยิ่งคือ AddLibrariesForBinary() ซึ่งจะเพิ่มไลบรารีและลิงก์เกอร์ที่ไบนารีต้องการ

การคิดค้น Syscall เพื่อเพิ่มในรายการที่อนุญาตยังคงต้องดำเนินการด้วยตนเองอยู่บ้าง ขออภัย สร้างนโยบายด้วย Syscall ที่คุณทราบว่าไบนารีของคุณต้องการ และ เรียกใช้นโยบายดังกล่าวกับภาระงานทั่วไป หากมีการละเมิด ให้เพิ่ม 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();
}

3. ปรับขีดจำกัด

นโยบายแซนด์บ็อกซ์จะป้องกันไม่ให้ Sandboxee เรียก Syscall ที่เฉพาะเจาะจงและ จึงช่วยลดพื้นที่ในการโจมตี อย่างไรก็ตาม ผู้โจมตียังคงอาจทำให้เกิดผลกระทบที่ไม่พึงประสงค์ได้โดยการเรียกใช้กระบวนการอย่างไม่มีกำหนด หรือใช้ RAM และ ทรัพยากรอื่นๆ จนหมด

Sandboxee จะทำงานภายใต้ขีดจำกัดการดำเนินการที่เข้มงวดโดยค่าเริ่มต้นเพื่อรับมือกับภัยคุกคามนี้ หากขีดจํากัดเริ่มต้นเหล่านี้ทําให้เกิดปัญหาในการเรียกใช้โปรแกรมของคุณอย่างถูกต้อง คุณสามารถปรับขีดจํากัดได้โดยใช้คลาส sandbox2::Limits โดยการเรียกใช้ 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 ได้ที่ตัวอย่าง เครื่องมือ

4. เรียกใช้แซนด์บ็อกซ์

ในส่วนก่อนหน้า คุณได้เตรียมสภาพแวดล้อมแซนด์บ็อกซ์ นโยบาย และ Executor และ Sandboxee ขั้นตอนถัดไปคือการสร้างออบเจ็กต์ Sandbox2 และเรียกใช้

เรียกใช้แบบพร้อมกัน

แซนด์บ็อกซ์สามารถทำงานแบบซิงโครนัสได้ จึงจะบล็อกจนกว่าจะมีผลลัพธ์ ข้อมูลโค้ดด้านล่างแสดงการเริ่มต้นออบเจ็กต์ Sandbox2 และการดำเนินการแบบซิงโครนัส ดูตัวอย่างโดยละเอียดเพิ่มเติมได้ที่ static

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

เรียกใช้แบบไม่พร้อมกัน

นอกจากนี้ คุณยังเรียกใช้แซนด์บ็อกซ์แบบไม่พร้อมกันได้ด้วย ซึ่งจะไม่บล็อกจนกว่าจะมี ผลลัพธ์ ซึ่งจะเป็นประโยชน์ เช่น เมื่อสื่อสารกับ Sandboxee ข้อมูลโค้ดด้านล่างแสดงกรณีการใช้งานนี้ ดูตัวอย่างโดยละเอียดเพิ่มเติมได้ที่ 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();

5. การสื่อสารกับ Sandboxee

โดยค่าเริ่มต้น ตัวดำเนินการจะสื่อสารกับ Sandboxee ผ่านตัวอธิบายไฟล์ ซึ่งอาจเป็นสิ่งที่คุณต้องการทั้งหมด เช่น หากคุณเพียงต้องการแชร์ ไฟล์กับ Sandboxee หรืออ่านเอาต์พุตมาตรฐานของ Sandboxee

อย่างไรก็ตาม คุณอาจต้องใช้ตรรกะการสื่อสารที่ซับซ้อนมากขึ้น ระหว่าง Executor กับ Sandboxee คุณใช้ Comms API (ดูไฟล์ส่วนหัว comms.h ) เพื่อส่งจำนวนเต็ม สตริง บัฟเฟอร์ไบต์ Protobuf หรือ ตัวอธิบายไฟล์ได้

การแชร์ตัวบอกไฟล์

การใช้ Inter-Process Communication API (ดู ipc.h) คุณสามารถใช้ MapFd() หรือ ReceiveFd() ได้

  • ใช้ MapFd() เพื่อแมปตัวอธิบายไฟล์จากตัวดำเนินการไปยัง Sandboxee ซึ่งใช้เพื่อแชร์ไฟล์ที่เปิดจากโปรแกรมเรียกใช้เพื่อใช้ใน Sandboxee ได้ ดูตัวอย่างการใช้งานได้ใน แบบคงที่

    // The executor opened /proc/version and passes it to the sandboxee as stdin
    executor->ipc()->MapFd(proc_version_fd, STDIN_FILENO);
    
  • ใช้ReceiveFd()เพื่อสร้างปลายทาง socketpair ซึ่งใช้เพื่ออ่านเอาต์พุตมาตรฐานหรือข้อผิดพลาดมาตรฐานของ Sandboxee ได้ ตัวอย่างการใช้งานดูได้ในเครื่องมือ

    // The executor receives a file descriptor of the sandboxee stdout
    int recv_fd1 = executor->ipc())->ReceiveFd(STDOUT_FILENO);
    

การใช้ Comms API

Sandbox2 มี comms API ที่สะดวก นี่เป็นวิธีที่ง่ายและสะดวกในการแชร์จำนวนเต็ม สตริง หรือบัฟเฟอร์ไบต์ ระหว่าง Executor กับ Sandboxee ด้านล่างนี้คือข้อมูลโค้ดบางส่วนที่คุณดูได้ในตัวอย่าง crc4

หากต้องการเริ่มต้นใช้งาน Comms API คุณต้องรับออบเจ็กต์ Comms จากออบเจ็กต์ Sandbox2 ก่อน โดยทำดังนี้

sandbox2::Comms* comms = s2.comms();

เมื่อออบเจ็กต์ comms พร้อมใช้งานแล้ว คุณจะส่งข้อมูลไปยัง Sandboxee ได้โดยใช้ฟังก์ชันตระกูล Send* อย่างใดอย่างหนึ่ง คุณดูตัวอย่างการใช้ Comms API ได้ในตัวอย่าง crc4 ข้อมูลโค้ด ด้านล่างแสดงตัวอย่างจากตัวอย่างนั้น ผู้ดำเนินการจะส่ง unsigned char buf[size] พร้อม SendBytes(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 */
}

การแชร์ข้อมูลกับบัฟเฟอร์

ฟังก์ชันการแชร์ข้อมูลอีกอย่างคือการใช้ buffer API เพื่อแชร์ข้อมูลจำนวนมากและหลีกเลี่ยงการคัดลอกที่มีค่าใช้จ่ายสูงซึ่งส่งกลับไปกลับมาระหว่างตัวดำเนินการและ Sandboxee

โดย Executor จะสร้างบัฟเฟอร์ตามขนาดและข้อมูลที่จะส่ง หรือสร้างจากตัวอธิบายไฟล์โดยตรง แล้วส่งไปยัง Sandboxee โดยใช้ comms->SendFD() ใน Executor และ comms->RecvFD() ใน Sandboxee

ในข้อมูลโค้ดด้านล่าง คุณจะเห็นฝั่งของผู้ดำเนินการ แซนด์บ็อกซ์จะทำงานแบบอะซิงโครนัสและแชร์ข้อมูลผ่านบัฟเฟอร์กับ Sandboxee ดังนี้

// 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 */

6. การออกจากแซนด์บ็อกซ์

คุณต้องปรับวิธีสิ้นสุดแซนด์บ็อกซ์และ Sandboxee ด้วย ทั้งนี้ขึ้นอยู่กับวิธีเรียกใช้แซนด์บ็อกซ์ (ดูขั้นตอนนี้)

การออกจากแซนด์บ็อกซ์ที่ทำงานแบบพร้อมกัน

หากแซนด์บ็อกซ์ทำงานแบบซิงโครนัส ฟังก์ชัน Run จะแสดงผลเมื่อ Sandboxee เสร็จสิ้นเท่านั้น ดังนั้นจึงไม่จำเป็นต้องดำเนินการเพิ่มเติมเพื่อสิ้นสุดการใช้งาน ข้อมูลโค้ดด้านล่างแสดงสถานการณ์นี้

Sandbox2::Result result = s2.Run();
LOG(INFO) << "Final execution status: " << result.ToString();

การออกจากแซนด์บ็อกซ์ที่ทำงานแบบไม่พร้อมกัน

หากแซนด์บ็อกซ์ทำงานแบบไม่พร้อมกัน คุณจะมีตัวเลือก 2 อย่างในการสิ้นสุด ก่อนอื่น คุณสามารถรอให้ 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();

7. ทดสอบ

การติดตั้งใช้งาน Sandbox ควรมีการทดสอบเช่นเดียวกับโค้ดอื่นๆ การทดสอบแซนด์บ็อกซ์ ไม่ได้มีไว้เพื่อทดสอบความถูกต้องของโปรแกรม แต่มีไว้เพื่อตรวจสอบ ว่าโปรแกรมที่อยู่ในแซนด์บ็อกซ์สามารถทำงานได้โดยไม่มีปัญหา เช่น การละเมิดแซนด์บ็อกซ์ และยังช่วยให้มั่นใจได้ว่านโยบายแซนด์บ็อกซ์ถูกต้อง

ระบบจะทดสอบโปรแกรมแซนด์บ็อกซ์ในลักษณะเดียวกับที่คุณจะเรียกใช้ในเวอร์ชันที่ใช้งานจริง โดยมีอาร์กิวเมนต์และไฟล์อินพุตที่โปรแกรมจะประมวลผลตามปกติ

การทดสอบเหล่านี้อาจเป็นเพียงการทดสอบเชลล์หรือการทดสอบ C++ โดยใช้กระบวนการย่อย ดูตัวอย่างเพื่อหาแรงบันดาลใจ

บทสรุป

ขอขอบคุณที่อ่านมาจนถึงตรงนี้ เราหวังว่าคุณจะชอบคู่มือนี้และรู้สึกพร้อม ที่จะสร้างแซนด์บ็อกซ์ของคุณเองเพื่อช่วยรักษาความปลอดภัยให้ผู้ใช้

การสร้างแซนด์บ็อกซ์และนโยบายเป็นงานที่ยากและมีแนวโน้มที่จะเกิดข้อผิดพลาดเล็กๆ น้อยๆ เราขอแนะนำให้คุณปรึกษาผู้เชี่ยวชาญด้านความปลอดภัยเพื่อตรวจสอบนโยบายและโค้ดของคุณเพื่อความปลอดภัย