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