2. สร้างนโยบายแซนด์บ็อกซ์
เมื่อมีผู้ดำเนินการแล้ว คุณอาจต้องการกำหนดนโยบายแซนด์บ็อกซ์สำหรับแซนด์บ็อกซ์ ไม่เช่นนั้น แซนด์บ็อกซ์จะได้รับการปกป้องโดยนโยบายเริ่มต้นของ Syscall เท่านั้น
วัตถุประสงค์ของนโยบายแซนด์บ็อกซ์คือการจำกัดการเรียกใช้ syscall และอาร์กิวเมนต์ที่แซนด์บ็อกซ์สามารถสร้างได้ รวมถึงไฟล์ที่แซนด์บ็อกซ์สามารถเข้าถึงได้ คุณจะต้องเข้าใจ Syscall อย่างละเอียดที่จำเป็นสำหรับโค้ดที่วางแผนจะใช้แซนด์บ็อกซ์ วิธีหนึ่งในการสังเกตการณ์ Syscall คือการเรียกใช้โค้ดด้วยสแต็กของเครื่องมือบรรทัดคำสั่งของ Linux
เมื่อคุณมีรายการ Syscall แล้ว คุณสามารถใช้ PolicyBuilder เพื่อกำหนดนโยบาย PolicyBuilder มาพร้อมกับฟังก์ชันที่ช่วยอำนวยความสะดวกและความช่วยเหลือมากมายที่อนุญาตการดำเนินการทั่วไปหลายอย่าง รายการต่อไปนี้เป็นเพียงตัวอย่างบางส่วนของฟังก์ชันที่ใช้ได้
- เพิ่ม syscall ไว้ในรายการที่อนุญาตสำหรับการเริ่มต้นกระบวนการ:
AllowStaticStartup();
AllowDynamicStartup();
- เพิ่ม syscalls ที่เปิด/read/write* ไว้ในรายการที่อนุญาต ดังนี้
AllowOpen();
AllowRead();
AllowWrite();
- เพิ่ม syscalls ที่เกี่ยวข้องกับการออก/การเข้าถึง/สถานะไปยังรายการที่อนุญาต
AllowExit();
AllowStat();
AllowAccess();
- เพิ่ม syscalls ที่เกี่ยวข้องกับการนอนหลับ/เวลาลงในรายการที่อนุญาต
AllowTime();
AllowSleep();
ฟังก์ชันอำนวยความสะดวกเหล่านี้จะเพิ่มการเรียกใช้ Syscall ที่เกี่ยวข้องลงในรายการได้ทั้งหมด ซึ่งมีข้อดีตรงที่นโยบายเดียวกันสามารถใช้กับสถาปัตยกรรมต่างๆ ที่ syscall ไม่พร้อมใช้งาน (เช่น ARM64 ไม่มี OPEN syscall) แต่มีความเสี่ยงด้านความปลอดภัยเล็กน้อยที่จะเปิดใช้ Sycsall มากเกินกว่าที่จำเป็น ตัวอย่างเช่น AllowOpen() ช่วยให้แซนด์บ็อกซ์สามารถเรียก syscall ที่เกี่ยวข้องแบบเปิด หากต้องการอนุญาต Sycall ที่ระบุอยู่ในรายการที่อนุญาตเพียง 1 รายการ คุณใช้ AllowSyscall();
เพื่ออนุญาต syscall ได้หลายรายการพร้อมกัน คุณสามารถใช้ AllowSyscalls()
ได้
จนถึงขณะนี้ นโยบายจะตรวจสอบเฉพาะตัวระบุ Syscall เท่านั้น หากคุณต้องการปรับปรุงนโยบายเพิ่มเติมและต้องการกำหนดนโยบายที่คุณอนุญาตให้ใช้เฉพาะ syscall ที่มีอาร์กิวเมนต์ที่ระบุ คุณจะต้องใช้ AddPolicyOnSyscall()
หรือ AddPolicyOnSyscalls()
ฟังก์ชันเหล่านี้ไม่เพียงนำรหัส syscall เป็นอาร์กิวเมนต์เท่านั้น แต่ยังใช้ตัวกรอง seccomp-bpf ดิบโดยใช้มาโครตัวช่วย bpf จากเคอร์เนลของ Linux ด้วย โปรดดูข้อมูลเพิ่มเติมเกี่ยวกับ BPF ในเอกสารประกอบของเคอร์เนล หากคุณพบว่าตนเองกำลังเขียนโค้ด BPF ซ้ำๆ และคิดว่าควรมีข้อมูลสรุปความสามารถในการใช้งาน โปรดยื่นคำขอฟีเจอร์
นอกเหนือจากฟังก์ชันที่เกี่ยวข้องกับ syscall แล้ว PolicyBuilder ยังมีฟังก์ชันเกี่ยวกับระบบไฟล์จำนวนมาก เช่น AddFile()
หรือ AddDirectory()
เพื่อเชื่อมโยงไฟล์/ไดเรกทอรีเข้ากับแซนด์บ็อกซ์ คุณจะใช้ตัวช่วยของ AddTmpfs()
เพื่อเพิ่มพื้นที่เก็บไฟล์ชั่วคราวในแซนด์บ็อกซ์ได้
ฟังก์ชันที่มีประโยชน์เป็นพิเศษคือ AddLibrariesForBinary()
ซึ่งจะเพิ่มไลบรารีและ Linker ที่ไบนารีจำเป็นต้องใช้
น่าเสียดายที่การเพิ่ม syscalls ไปยังรายการที่อนุญาตยังคงเป็นงานที่ต้องดำเนินการด้วยตนเอง สร้างนโยบายด้วย syscalls ที่คุณทราบว่าจำเป็นต้องใช้ไบนารีของคุณ และเรียกใช้ด้วยภาระงานทั่วไป หากมีการละเมิดเกิดขึ้น ให้เพิ่มการเรียกใช้ 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();
}