1. Выберите метод исполнения песочницы

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

Способ 1: Автономный: выполнить двоичный файл с уже включенной песочницей.

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

В следующем фрагменте кода мы определяем путь к двоичному файлу, который будет помещен в песочницу, и аргументы, которые мы должны передать системному вызову execve. Как вы можете видеть в заголовочном файле executor.h , мы не указываем значение для envp и поэтому копируем среду из родительского процесса. Помните, что первым аргументом всегда является имя выполняемой программы, и наш фрагмент не определяет никаких других аргументов.

Примерами этого метода исполнителя являются: статический и инструментальный .

#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() . Он требует, чтобы вы могли определить в коде, когда вы хотите запустить песочницу, и он должен быть однопоточным (почему читайте в FAQ ).

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

,

1. Выберите метод исполнения песочницы

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

Способ 1: Автономный: выполнить двоичный файл с уже включенной песочницей.

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

В следующем фрагменте кода мы определяем путь к двоичному файлу, который будет помещен в песочницу, и аргументы, которые мы должны передать системному вызову execve. Как вы можете видеть в заголовочном файле executor.h , мы не указываем значение для envp и поэтому копируем среду из родительского процесса. Помните, что первым аргументом всегда является имя выполняемой программы, и наш фрагмент не определяет никаких других аргументов.

Примерами этого метода исполнителя являются: статический и инструментальный .

#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() . Он требует, чтобы вы могли определить в коде, когда вы хотите запустить песочницу, и он должен быть однопоточным (почему читайте в FAQ ).

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