2. یک Sandbox Policy ایجاد کنید

هنگامی که یک مجری دارید، احتمالاً می خواهید یک سیاست Sandbox برای Sandboxee تعریف کنید. در غیر این صورت، Sandboxee فقط توسط خط‌مشی Syscall پیش‌فرض محافظت می‌شود.

با سیاست Sandbox، هدف محدود کردن syscals و آرگومان‌هایی است که Sandboxee می‌تواند ایجاد کند، و همچنین فایل‌هایی که می‌تواند به آن دسترسی داشته باشد. شما باید درک دقیقی از syscals های مورد نیاز کدی که قصد دارید sandbox را وارد کنید داشته باشید. یکی از راه های مشاهده syscalls، اجرای کد با خط فرمان خط ابزار لینوکس است.

هنگامی که لیستی از syscalls را دارید، می توانید از PolicyBuilder برای تعریف خط مشی استفاده کنید. PolicyBuilder با بسیاری از توابع راحتی و کمکی ارائه می شود که امکان انجام بسیاری از عملیات های رایج را فراهم می کند. لیست زیر تنها گزیده ای کوچک از توابع موجود است:

  • Allowlist هر syscall برای راه اندازی فرآیند:
    • AllowStaticStartup();
    • AllowDynamicStartup();
  • Allowlist هر سیستم باز /خواندن /نوشتن*:
    • AllowOpen();
    • AllowRead();
    • AllowWrite();
  • Allowlist هر گونه خروجی/دسترسی/وضعیت سیستم مرتبط:
    • AllowExit();
    • AllowStat();
    • AllowAccess();
  • هر گونه پیام‌های مرتبط با خواب/زمان را فهرست کنید:
    • AllowTime();
    • AllowSleep();

این توابع راحتی هر گونه syscall مرتبط را لیست می کنند. این مزیت را دارد که می‌توان از یک خط‌مشی روی معماری‌های مختلف استفاده کرد، جایی که syscals خاصی در دسترس نیست (مثلا ARM64 هیچ syscall OPEN ندارد)، اما با خطر امنیتی جزئی فعال کردن sycsall‌های بیشتر از آنچه لازم است. برای مثال، AllowOpen() Sandboxee را قادر می‌سازد تا هر سیستمی باز مرتبط را فراخوانی کند. اگر می خواهید فقط یک syscall خاص را در لیست allow قرار دهید، می توانید از AllowSyscall(); برای اجازه دادن به چندین syscalls در یک زمان می توانید از AllowSyscalls() استفاده کنید.

تا کنون این خط مشی فقط شناسه syscall را بررسی می کند. اگر نیاز به تقویت بیشتر این خط مشی دارید و می خواهید سیاستی را تعریف کنید که در آن فقط یک syscall با آرگومان های خاص مجاز باشد، باید از AddPolicyOnSyscall() یا AddPolicyOnSyscalls() استفاده کنید. این توابع نه تنها شناسه syscall را به عنوان آرگومان می گیرند، بلکه یک فیلتر خام seccomp-bpf را نیز با استفاده از ماکروهای کمکی bpf از هسته لینوکس می گیرند. برای اطلاعات بیشتر در مورد BPF به اسناد هسته مراجعه کنید. اگر متوجه شدید که کد BPF تکراری می نویسید که فکر می کنید باید دارای قابلیت استفاده باشد، در صورت تمایل یک درخواست ویژگی را ثبت کنید.

جدا از توابع مربوط به syscall، PolicyBuilder همچنین تعدادی توابع مرتبط با فایل سیستم مانند AddFile() یا AddDirectory() را برای متصل کردن یک فایل/دایرکتوری به sandbox ارائه می کند. کمک کننده AddTmpfs() می تواند برای افزودن یک ذخیره سازی موقت فایل در جعبه شنی استفاده شود.

یک تابع بسیار مفید AddLibrariesForBinary() است که کتابخانه ها و پیوند دهنده مورد نیاز یک باینری را اضافه می کند.

متأسفانه ارائه syscalls به لیست مجاز هنوز کمی کار دستی است. یک خط مشی با syscalls که نیازهای باینری خود را می دانید ایجاد کنید و آن را با حجم کاری مشترک اجرا کنید. اگر تخلفی ایجاد شد، syscall را در لیست allow قرار دهید و این فرآیند را تکرار کنید. اگر با تخلفی مواجه شدید که فکر می‌کنید ممکن است در لیست مجوزها خطرناک باشد و برنامه به‌خوبی خطاها را مدیریت می‌کند، می‌توانید به جای آن با 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();
}
،

2. یک Sandbox Policy ایجاد کنید

هنگامی که یک مجری دارید، احتمالاً می خواهید یک سیاست Sandbox برای Sandboxee تعریف کنید. در غیر این صورت، Sandboxee فقط توسط خط‌مشی Syscall پیش‌فرض محافظت می‌شود.

با سیاست Sandbox، هدف محدود کردن syscals و آرگومان‌هایی است که Sandboxee می‌تواند ایجاد کند، و همچنین فایل‌هایی که می‌تواند به آن دسترسی داشته باشد. شما باید درک دقیقی از syscals های مورد نیاز کدی که قصد دارید sandbox را وارد کنید داشته باشید. یکی از راه های مشاهده syscalls، اجرای کد با خط فرمان خط ابزار لینوکس است.

هنگامی که لیستی از syscalls را دارید، می توانید از PolicyBuilder برای تعریف خط مشی استفاده کنید. PolicyBuilder با بسیاری از توابع راحتی و کمکی ارائه می شود که امکان انجام بسیاری از عملیات های رایج را فراهم می کند. لیست زیر تنها گزیده ای کوچک از توابع موجود است:

  • Allowlist هر syscall برای راه اندازی فرآیند:
    • AllowStaticStartup();
    • AllowDynamicStartup();
  • Allowlist هر سیستم باز /خواندن /نوشتن*:
    • AllowOpen();
    • AllowRead();
    • AllowWrite();
  • Allowlist هر گونه خروجی/دسترسی/وضعیت سیستم مرتبط:
    • AllowExit();
    • AllowStat();
    • AllowAccess();
  • هر گونه پیام‌های مرتبط با خواب/زمان را فهرست کنید:
    • AllowTime();
    • AllowSleep();

این توابع راحتی هر گونه syscall مرتبط را لیست می کنند. این مزیت را دارد که می‌توان از یک خط‌مشی روی معماری‌های مختلف استفاده کرد، جایی که syscals خاصی در دسترس نیست (مثلا ARM64 هیچ syscall OPEN ندارد)، اما با خطر امنیتی جزئی فعال کردن sycsall‌های بیشتر از آنچه لازم است. برای مثال، AllowOpen() Sandboxee را قادر می‌سازد تا هر سیستمی باز مرتبط را فراخوانی کند. اگر می خواهید فقط یک syscall خاص را در لیست allow قرار دهید، می توانید از AllowSyscall(); برای اجازه دادن به چندین syscalls در یک زمان می توانید از AllowSyscalls() استفاده کنید.

تا کنون این خط مشی فقط شناسه syscall را بررسی می کند. اگر نیاز به تقویت بیشتر این خط مشی دارید و می خواهید سیاستی را تعریف کنید که در آن فقط یک syscall با آرگومان های خاص مجاز باشد، باید از AddPolicyOnSyscall() یا AddPolicyOnSyscalls() استفاده کنید. این توابع نه تنها شناسه syscall را به عنوان آرگومان می گیرند، بلکه یک فیلتر خام seccomp-bpf را نیز با استفاده از ماکروهای کمکی bpf از هسته لینوکس می گیرند. برای اطلاعات بیشتر در مورد BPF به اسناد هسته مراجعه کنید. اگر متوجه شدید که کد BPF تکراری می نویسید که فکر می کنید باید دارای قابلیت استفاده باشد، در صورت تمایل یک درخواست ویژگی را ثبت کنید.

جدا از توابع مربوط به syscall، PolicyBuilder همچنین تعدادی توابع مرتبط با فایل سیستم مانند AddFile() یا AddDirectory() را برای متصل کردن یک فایل/دایرکتوری به sandbox ارائه می کند. کمک کننده AddTmpfs() می تواند برای افزودن یک ذخیره سازی موقت فایل در جعبه شنی استفاده شود.

یک تابع بسیار مفید AddLibrariesForBinary() است که کتابخانه ها و پیوند دهنده مورد نیاز یک باینری را اضافه می کند.

متأسفانه ارائه syscalls به لیست مجاز هنوز کمی کار دستی است. یک خط مشی با syscalls که نیازهای باینری خود را می دانید ایجاد کنید و آن را با حجم کاری مشترک اجرا کنید. اگر تخلفی ایجاد شد، syscall را در لیست allow قرار دهید و این فرآیند را تکرار کنید. اگر با تخلفی مواجه شدید که فکر می‌کنید ممکن است در لیست مجوزها خطرناک باشد و برنامه به‌خوبی خطاها را مدیریت می‌کند، می‌توانید به جای آن با 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();
}