1. Выберите метод Sandbox Executor

Песочница начинается с исполнителя (см. Исполняющий объект песочницы ), который отвечает за запуск Sandboxee . Заголовочный файл executor.h содержит API, необходимый для этой цели. API очень гибкий и позволяет вам выбрать наиболее подходящий для вашего случая использования. В следующих разделах описаны три различных метода, которые вы можете выбрать.

Метод 1: Автономный — запуск исполняемого файла с уже включенной песочницей

Это самый простой способ использования песочницы, и он рекомендуется, когда вы хотите изолировать весь исполняемый файл, исходного кода которого у вас нет. Это также самый безопасный способ использования песочницы, поскольку отсутствует инициализация вне песочницы, которая могла бы иметь негативные последствия.

В следующем фрагменте кода мы определяем путь к исполняемому файлу, который необходимо изолировать, и аргументы, которые необходимо передать системному вызову 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: Sandbox2 Forkserver — сообщите исполнителю, когда следует помещать его в песочницу

Этот метод обеспечивает гибкость: выход из песочницы возможен во время инициализации, а затем выбор момента входа в песочницу осуществляется вызовом метода ::sandbox2::Client::SandboxMeHere() . Для этого необходимо указать в коде момент начала песочницы, и метод должен быть однопоточным (подробнее об этом в разделе часто задаваемых вопросов ).

В следующем фрагменте кода мы используем тот же код, что описан в Методе 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 и уведомляет исполнителя ( crc4sandbox.cc ), когда он должен войти в песочницу.

Метод 3: Пользовательский форк-сервер — подготовьте исполняемый файл, дождитесь запросов на форк и создайте песочницу самостоятельно

Этот режим позволяет вам запустить двоичный файл, подготовить его для «песочницы» и в определенный момент жизненного цикла вашего двоичного файла сделать его доступным для исполнителя.

Исполнитель отправит запрос на форк вашему исполняемому файлу, который выполнит fork() (через ::sandbox2::ForkingClient::WaitAndFork() ). Вновь созданный процесс будет готов к изоляции с помощью ::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, требующая значительных ресурсов процессора, которую можно выполнить до обработки ненадёжных данных.

Пример этого метода исполнителя см. в custom_fork .