Premiers pas avec l'API Sandboxed

Cette page explique comment créer votre propre bibliothèque C/C++ en bac à sable avec l'API en bac à sable (SAPI). Utilisez-la comme guide, en complément des exemples et de la documentation du code dans les fichiers d'en-tête.

Créer des dépendances

Les dépendances suivantes doivent être installées sur le système :

  • Noyau Linux compatible avec les espaces de noms UTS, IPC, utilisateur, PID et réseau
  • En-têtes de l'API de l'espace utilisateur Linux
  • Pour compiler votre code : GCC 6 (version 7 ou ultérieure de préférence) ou Clang 7 (ou version ultérieure)
  • Pour générer automatiquement des fichiers d'en-tête : liaisons Python Clang
  • Python 3.5 ou version ultérieure
  • Bazel version 2.2.0 ou CMake version 3.12 ou ultérieure.
    • CMake uniquement : GNU Make ou une version des en-têtes de la bibliothèque libcap et un outil de compilation tel que Ninja (recommandé).

Utiliser Bazel

Bazel est le système de compilation recommandé et le plus facile à intégrer.

Notre documentation utilise le compilateur Clang. Si vous avez besoin d'une chaîne d'outils spécifique (compilateur, éditeur de liens, etc.), consultez la documentation Bazel pour savoir comment modifier la chaîne d'outils du compilateur par défaut.

Debian 10 (Buster)

Pour installer les dépendances de compilation :

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

Options de noyau requises :

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

Pour installer les dépendances de compilation :

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

Utiliser CMake

CMake est un système de méta-compilation Open Source populaire qui génère des fichiers de projet pour des outils de compilation tels que Ninja ou Make.

Debian 10 (Buster)

Pour installer les dépendances de compilation :

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

Options de noyau requises :

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

Pour installer les dépendances de compilation :

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

Processus de développement

Pour mettre en bac à sable une bibliothèque C/C++, vous devez préparer deux éléments pour votre projet :

Vous connaissez peut-être zlib grâce aux exemples Sandbox2 où un programme complet (zpipe.c) a été mis en bac à sable. Dans les étapes suivantes, vous apprendrez à utiliser SAPI pour mettre en bac à sable la bibliothèque zlib et à utiliser la bibliothèque en bac à sable.

1. Déterminer les fonctions nécessaires

Si vous examinez le code hôte zlib (main_zlib.cc), vous pouvez constater que la fonctionnalité de l'outil consiste à lire les données à partir de stdin et à utiliser la fonction deflate() de zlib pour compresser les données jusqu'à ce qu'un marqueur EOF soit lu. Au total, le programme utilise trois fonctions de zlib :

  • deflateInit_() : pour initialiser la compression
  • deflate() : pour effectuer l'opération de compression sur le bloc de données
  • deflateEnd(): pour mettre fin à la compression et libérer les structures de données allouées dynamiquement

Dans un exemple concret, vous examinerez la bibliothèque C/C++ et déterminerez les fonctions nécessaires. Une stratégie possible consiste à commencer par le code hôte et à utiliser la bibliothèque sans bac à sable. Ensuite, dans une deuxième étape, vous pouvez générer la bibliothèque en bac à sable et adapter le code hôte pour utiliser les appels de fonction en bac à sable.

2. Écrire la règle de compilation sapi_library

Une fois que vous avez identifié les trois fonctions zlib nécessaires à partir de la bibliothèque zlib en bac à sable, vous pouvez définir la règle de compilation dans le fichier BUILD. La documentation de la règle de compilation sapi_library est disponible sur la page Règles de compilation.

L'extrait de code ci-dessous montre la définition sapi_library pour l'exemple SAPI zlib. À l'aide de l'attribut lib, Bazel est invité à rechercher la bibliothèque zlib dans le fichier WORKSPACE.

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

La bibliothèque zlib en bac à sable est alors générée. La sortie est l'objet SAPI qui peut être inclus dans le code hôte et utilisé pour communiquer avec la bibliothèque en bac à sable à l'aide d'appels RPC.

Cet exemple utilise la configuration de bac à sable par défaut fournie par DefaultConfig (sapi::SandboxConfig::DefaultConfig()).

3. Écrire ou modifier le code hôte

Il est maintenant temps d'intégrer la bibliothèque SAPI générée dans le code hôte.

Créer le bac à sable

Utilisez sapi::zlib::ZlibSandbox sandbox; pour créer un objet de bac à sable.

Utilisez sapi::zlib::ZlibApi api(&sandbox); pour instancier l'objet SAPI et ainsi rendre les fonctions en bac à sable disponibles.

Pour personnaliser la stratégie, les flags, les variables d'environnement ou les limites du bac à sable, commencez par sapi::SandboxConfig::DefaultConfig(), modifiez les champs pertinents (à l'aide de sapi::Sandbox2Config::DefaultPolicyBuilder(), sapi::SandboxConfig::DefaultFlags(), ou sapi::SandboxConfig::DefaultEnvironmentVariables() afin de conserver les paramètres de référence), puis transmettez le sapi::SandboxConfig au constructeur du bac à sable :

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));

Utiliser les types SAPI

Les types SAPI sont des types spéciaux sous la forme de classes C++ que SAPI fournit, car parfois les types C standards ne fonctionnent pas.

La première utilisation d'un type SAPI peut être observée dans la déclaration de strm, où une structure SAPI est utilisée : sapi::v::Struct<sapi::zlib::z_stream> strm;

Le type de modèle (sapi::zlib::z_stream) est un bon exemple de code généré automatiquement par la règle de compilation.

Pour en savoir plus, consultez la page Variables.

Effectuer des appels d'API

Pour effectuer des appels à defalteInit_, deflate ou deflateEnd, utilisez l'objet SAPI. Si vous décidez d'utiliser l'approche "modifier", vous devez vous assurer que les paramètres de la fonction correspondent aux valeurs attendues.

Voici un exemple de chacun des appels dans l'exemple 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();

Utiliser les transactions SAPI

SAPI isole le code hôte de la bibliothèque en bac à sable et permet à l'appelant de redémarrer ou d'abandonner les requêtes de traitement de données problématiques. La transaction SAPI va encore plus loin et répète automatiquement les processus ayant échoué.

Pour en savoir plus, consultez la page Transactions SAPI.

Exemples

Sous "Exemples", vous trouverez quelques bibliothèques déjà préparées par l'équipe SAPI.