2. Создайте политику песочницы
Если у вас есть исполнитель, вы, скорее всего, захотите определить политику песочницы для песочницы. В противном случае Sandboxee защищен только политикой системных вызовов по умолчанию .
Целью политики песочницы является ограничение системных вызовов и аргументов, которые может выполнять песочница, а также файлов, к которым она может получить доступ. Вам необходимо иметь детальное представление о системных вызовах, необходимых для кода, который вы планируете поместить в «песочницу». Один из способов наблюдения за системными вызовами — запустить код с помощью инструмента командной строки Linux strace.
Получив список системных вызовов, вы можете использовать PolicyBuilder для определения политики. PolicyBuilder поставляется со множеством удобных и вспомогательных функций, которые позволяют выполнять множество распространенных операций. Следующий список представляет собой лишь небольшую часть доступных функций:
- Разрешить любой системный вызов для запуска процесса:
-
AllowStaticStartup();
-
AllowDynamicStartup();
-
- В список разрешенных системных вызовов открытия /чтения /записи*:
-
AllowOpen();
-
AllowRead();
-
AllowWrite();
-
- Разрешить любые системные вызовы, связанные с выходом/доступом/состоянием:
-
AllowExit();
-
AllowStat();
-
AllowAccess();
-
- В список разрешенных системных вызовов, связанных со сном/временем:
-
AllowTime();
-
AllowSleep();
-
Эти удобные функции позволяют внести в список любые соответствующие системные вызовы. Это имеет то преимущество, что одна и та же политика может использоваться в разных архитектурах, где определенные системные вызовы недоступны (например, в ARM64 нет системного вызова OPEN), но с небольшим риском безопасности, связанным с включением большего количества системных вызовов, чем может быть необходимо. Например, AllowOpen() позволяет Sandboxee вызывать любой системный вызов, связанный с открытием. Если вы хотите внести в список разрешенных только один конкретный системный вызов, вы можете использовать AllowSyscall();
чтобы разрешить несколько системных вызовов одновременно, вы можете использовать AllowSyscalls()
.
Пока что политика проверяет только идентификатор системного вызова. Если вам необходимо еще больше усилить политику и вы хотите определить политику, в которой вы разрешаете системный вызов только с определенными аргументами, вам нужно использовать AddPolicyOnSyscall()
или AddPolicyOnSyscalls()
. Эти функции не только принимают в качестве аргумента идентификатор системного вызова, но и необработанный фильтр seccomp-bpf, использующий вспомогательные макросы bpf из ядра Linux. Дополнительную информацию о BPF смотрите в документации ядра . Если вы обнаружите, что пишете повторяющийся код BPF, который, по вашему мнению, должен иметь оболочку для удобства использования, не стесняйтесь подавать запрос на добавление функции.
Помимо функций, связанных с системными вызовами, 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();
}
,2. Создайте политику песочницы
Если у вас есть исполнитель, вы, скорее всего, захотите определить политику песочницы для песочницы. В противном случае Sandboxee защищен только политикой системных вызовов по умолчанию .
Целью политики песочницы является ограничение системных вызовов и аргументов, которые может выполнять песочница, а также файлов, к которым она может получить доступ. Вам необходимо иметь детальное представление о системных вызовах, необходимых для кода, который вы планируете поместить в «песочницу». Один из способов наблюдения за системными вызовами — запустить код с помощью инструмента командной строки Linux strace.
Получив список системных вызовов, вы можете использовать PolicyBuilder для определения политики. PolicyBuilder поставляется со множеством удобных и вспомогательных функций, которые позволяют выполнять множество распространенных операций. Следующий список представляет собой лишь небольшую часть доступных функций:
- Разрешить любой системный вызов для запуска процесса:
-
AllowStaticStartup();
-
AllowDynamicStartup();
-
- В список разрешенных системных вызовов открытия /чтения /записи*:
-
AllowOpen();
-
AllowRead();
-
AllowWrite();
-
- Разрешить любые системные вызовы, связанные с выходом/доступом/состоянием:
-
AllowExit();
-
AllowStat();
-
AllowAccess();
-
- В список разрешенных системных вызовов, связанных со сном/временем:
-
AllowTime();
-
AllowSleep();
-
Эти удобные функции позволяют внести в список любые соответствующие системные вызовы. Это имеет то преимущество, что одна и та же политика может использоваться в разных архитектурах, где определенные системные вызовы недоступны (например, в ARM64 нет системного вызова OPEN), но с небольшим риском безопасности, связанным с включением большего количества системных вызовов, чем может быть необходимо. Например, AllowOpen() позволяет Sandboxee вызывать любой системный вызов, связанный с открытием. Если вы хотите внести в список разрешенных только один конкретный системный вызов, вы можете использовать AllowSyscall();
чтобы разрешить несколько системных вызовов одновременно, вы можете использовать AllowSyscalls()
.
Пока что политика проверяет только идентификатор системного вызова. Если вам необходимо еще больше усилить политику и вы хотите определить политику, в которой вы разрешаете системный вызов только с определенными аргументами, вам нужно использовать AddPolicyOnSyscall()
или AddPolicyOnSyscalls()
. Эти функции не только принимают в качестве аргумента идентификатор системного вызова, но и необработанный фильтр seccomp-bpf, использующий вспомогательные макросы bpf из ядра Linux. Дополнительную информацию о BPF смотрите в документации ядра . Если вы обнаружите, что пишете повторяющийся код BPF, который, по вашему мнению, должен иметь оболочку для удобства использования, не стесняйтесь подавать запрос на добавление функции.
Помимо функций, связанных с системными вызовами, 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();
}