Начало работы с Sandboxed API

На этой странице вы узнаете, как создать собственную изолированную библиотеку C/C++ с помощью Sandboxed API (SAPI). Используйте его в качестве руководства вместе с примерами и документацией по коду в заголовочных файлах.

Зависимости сборки

В системе должны быть установлены следующие зависимости:

  • Ядро Linux с поддержкой UTS, IPC, пользователя, PID и сетевых пространств имен.
  • Заголовки API пространства пользователя Linux
  • Для компиляции кода: GCC 6 (рекомендуется версия 7 или более поздняя) или Clang 7 (или более поздняя версия).
  • Для автоматического создания заголовочных файлов: Clang Python Bindings.
  • Python 3.5 или более поздней версии;
  • Bazel версии 2.2.0 или CMake версии 3.12 или более поздней.
    • Только для CMake: GNU Make или версия заголовков библиотеки libcap и инструмент сборки, например Ninja (рекомендуется).

Как использовать Bazel

Bazel – рекомендуемая система сборки, которую проще всего интегрировать.

В нашей документации используется компилятор Clang. Если вам нужна определенная цепочка инструментов (например, компилятор, компоновщик и т. д.), ознакомьтесь с документацией по Bazel, чтобы узнать, как изменить цепочку инструментов компилятора по умолчанию.

Debian 10 (Buster)

Чтобы установить зависимости сборки:

echo "deb http://storage.googleapis.com/bazel-apt stable jdk1.8" | \
  sudo tee /etc/apt/sources.list.d/bazel.list
wget -qO - https://releases.bazel.build/bazel-release.pub.gpg | sudo apt-key add -
sudo apt-get update
sudo apt-get install -qy build-essential linux-libc-dev bazel python3 \
  python3-pip libclang-dev
pip3 install clang

Gentoo

Требуемые параметры ядра:

General setup  --->
-*- Namespaces support
[*]   UTS namespace
[*]   IPC namespace
[*]   User namespace (EXPERIMENTAL)
[*]   PID Namespaces
[*]   Network namespace

Чтобы установить зависимости сборки:

emerge dev-util/bazel dev-python/typing dev-python/clang-python

Использование CMake

CMake – это популярная метасистема сборки с открытым исходным кодом, которая генерирует файлы проекта для таких инструментов сборки, как Ninja или Make.

Debian 10 (Buster)

Чтобы установить зависимости сборки:

sudo apt-get install -qy build-essential linux-libc-dev cmake ninja-build \
  python3 python3-pip libclang-dev libcap-dev
pip3 install absl-py clang

Gentoo

Требуемые параметры ядра:

General setup  --->
-*- Namespaces support
[*]   UTS namespace
[*]   IPC namespace
[*]   User namespace (EXPERIMENTAL)
[*]   PID Namespaces
[*]   Network namespace

Чтобы установить зависимости сборки:

emerge sys-kernel/linux-headers dev-util/cmake dev-util/ninja \
dev-python/clang-python

Процесс разработки

Чтобы изолировать библиотеку C/C++, вам нужно подготовить для проекта два элемента:

Возможно, вы уже знакомы с zlib из примеров Sandbox2. В этом случае в изолированную среду была помещена вся программа (zpipe.c). Ниже описано, как использовать SAPI, чтобы изолировать библиотеку zlib и воспользоваться изолированной библиотекой.

1. Решите, какие функции вам нужны

Если вы посмотрите код хоста zlib (main_zlib.cc), то увидите, что инструмент считывает данные из stdin и использует функцию deflate() из zlib для сжатия данных до тех пор, пока не будет прочитан маркер EOF. Всего в программе используются три функции из zlib:

  • deflateInit_(): инициализация сжатия;
  • deflate() – для выполнения операции сжатия над фрагментом данных.
  • deflateEnd() – завершает сжатие и освобождает динамически выделенные структуры данных.

В реальном примере вы бы изучили библиотеку C/C++ и решили, какие функции вам нужны. Возможная стратегия – начать с кода хоста и использовать библиотеку без изолированной среды. Затем на втором этапе вы можете сгенерировать изолированную библиотеку и адаптировать код хоста для использования изолированных вызовов функций.

2. Напишите правило сборки sapi_library

Определив три функции zlib, которые нужны из изолированной библиотеки zlib, вы можете определить правило сборки в файле BUILD. Документацию по правилу сборки sapi_library можно найти на странице Правила сборки.

В приведенном ниже фрагменте кода показано определение sapi_library для примера SAPI zlib. С помощью атрибута lib Bazel получает указание искать библиотеку zlib в файле WORKSPACE.

sapi_library(
    name = "zlib-sapi",
    srcs = [],
    hdrs = [],
    functions = [
        "deflateInit_",
        "deflate",
        "deflateEnd",
    ],
    lib = "@net_zlib//:zlib",
    lib_name = "Zlib",
    namespace = "sapi::zlib",
)

В результате будет создана изолированная библиотека zlib. На выходе получается объект SAPI, который можно включить в код хоста и использовать для связи с библиотекой в изолированной среде с помощью вызовов RPC.

В этом примере используется конфигурация изолированной среды по умолчанию, предоставленная функцией DefaultConfig (sapi::SandboxConfig::DefaultConfig()).

3. Как написать или изменить код хоста

Теперь пришло время включить сгенерированную библиотеку SAPI в код хоста.

Как создать изолированную среду

Используйте sapi::zlib::ZlibSandbox sandbox;, чтобы создать объект песочницы.

Используйте sapi::zlib::ZlibApi api(&sandbox);, чтобы создать экземпляр объекта SAPI и сделать функции изолированной среды доступными для использования.

Чтобы настроить правила, флаги, переменные среды или ограничения изолированной среды, начните с sapi::SandboxConfig::DefaultConfig(), измените нужные поля (используя sapi::Sandbox2Config::DefaultPolicyBuilder(), sapi::SandboxConfig::DefaultFlags() или sapi::SandboxConfig::DefaultEnvironmentVariables(), чтобы сохранить базовые настройки) и передайте sapi::SandboxConfig конструктору изолированной среды:

sapi::SandboxConfig config = sapi::SandboxConfig::DefaultConfig();
config.sandbox2.policy = sapi::Sandbox2Config::DefaultPolicyBuilder()
                             .AddFile("/path/to/file")
                             .BuildOrDie();
auto flags = sapi::SandboxConfig::DefaultFlags();
flags["my_flag"] = "value";
config.command_line_flags = std::move(flags);

sapi::zlib::ZlibSandbox sandbox(std::move(config));

Использовать типы SAPI

Типы SAPI – это специальные типы в виде классов C++, которые предоставляет SAPI, поскольку обычные типы C иногда не работают.

Первое использование типа SAPI можно увидеть в объявлении strm, где используется структура SAPI: sapi::v::Struct<sapi::zlib::z_stream> strm;.

Тип шаблона (sapi::zlib::z_stream) – хороший пример кода, автоматически созданного правилом сборки.

Подробную информацию вы найдете на странице "Переменные".

Как выполнять вызовы API

Чтобы позвонить на номер defalteInit_, deflate или deflateEnd, используйте объект SAPI. Если вы решите использовать подход "изменить", убедитесь, что параметры функции соответствуют ожидаемым значениям.

Пример каждого вызова в примере zlib:

  • api.deflateInit_(strm.PtrBoth(), Z_DEFAULT_COMPRESSION,
        version.PtrBefore(), sizeof(sapi::zlib::z_stream));
  • api.deflate(strm.PtrBoth(), flush);
  • api.deflateEnd(strm.PtrBoth()).IgnoreError();

Как использовать транзакции SAPI

SAPI изолирует код хоста от библиотеки в изолированной среде и позволяет вызывающей стороне перезапускать или прерывать запросы на обработку данных, которые вызывают проблемы. SAPI Transaction автоматически повторяет неудачные процессы.

Подробную информацию можно найти на странице транзакций SAPI.

Примеры

В разделе Examples (Примеры) вы найдете несколько библиотек, подготовленных командой SAPI.