Introduzione all'API Sandboxed

In questa pagina scoprirai come creare la tua libreria C/C++ in sandbox con Sandboxed API (SAPI). Utilizzala come guida insieme agli esempi e alla documentazione del codice nei file di intestazione.

Dipendenze build

Le seguenti dipendenze devono essere installate sul sistema:

  • Kernel Linux con supporto per gli spazi dei nomi UTS, IPC, utente, PID e di rete
  • Intestazioni API dello spazio utente Linux
  • Per compilare il codice: GCC 6 (versione 7 o successive consigliate) o Clang 7 (o versioni successive)
  • Per la generazione automatica dei file di intestazione: Clang Python Bindings
  • Python 3.5 o versioni successive
  • Bazel versione 2.2.0 o CMake versione 3.12 o successive.
    • Solo CMake: GNU Make o una versione delle intestazioni della libreria libcap e uno strumento di build come Ninja (consigliato).

Utilizzo di Bazel

Bazel è il sistema di build consigliato ed è il più facile da integrare.

La nostra documentazione utilizza il compilatore Clang. Se hai bisogno di una toolchain specifica (ad es. compilatore, linker e così via), consulta la documentazione di Bazel per informazioni su come modificare la toolchain del compilatore predefinita.

Debian 10 (Buster)

Per installare le dipendenze di build:

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

Opzioni del kernel richieste:

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

Per installare le dipendenze di build:

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

Utilizzo di CMake

CMake è un sistema di meta-build open source diffuso che genera file di progetto per strumenti di build come Ninja o Make.

Debian 10 (Buster)

Per installare le dipendenze di build:

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

Opzioni del kernel richieste:

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

Per installare le dipendenze di build:

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

Processo di sviluppo

Per inserire in sandbox una libreria C/C++, devi preparare due elementi per il tuo progetto:

Potresti conoscere zlib dagli esempi di Sandbox2, dove è stato inserito in sandbox un intero programma (zpipe.c) . Nei passaggi seguenti, scoprirai come utilizzare SAPI per inserire in sandbox la libreria zlib e utilizzare la libreria in sandbox.

1. Decidi quali funzioni sono necessarie

Se esamini il codice host zlib (main_zlib.cc), puoi notare che la funzionalità dello strumento consiste nel leggere i dati da stdin e utilizzare la funzione deflate() di zlib per comprimere i dati finché non viene letto un marcatore EOF. In totale, il programma utilizza tre funzioni di zlib:

  • deflateInit_(): per inizializzare la compressione
  • deflate(): per eseguire l'operazione di compressione sul blocco di dati
  • deflateEnd(): per terminare la compressione e liberare le strutture di dati allocate dinamicamente

In un esempio reale, esamineresti la libreria C/C++ e decideresti quali funzioni sono necessarie. Una strategia possibile è iniziare con il codice host e utilizzare la libreria senza sandbox. Poi, in un secondo passaggio, potresti generare la libreria in sandbox e adattare il codice host per utilizzare le chiamate di funzione in sandbox.

2. Scrivi la regola di build sapi_library

Dopo aver identificato le tre funzioni zlib necessarie dalla libreria zlib in sandbox, puoi definire la regola di build nel file BUILD. La documentazione per la regola di build sapi_library è disponibile nella pagina Regole di build.

Lo snippet di codice riportato di seguito mostra la definizione di sapi_library per l'esempio SAPI zlib. Utilizzando l'attributo lib, Bazel riceve l'istruzione di cercare la libreria zlib nel file WORKSPACE.

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

Il risultato è la generazione della libreria zlib in sandbox. L'output è l'oggetto SAPI che può essere incluso nel codice host e utilizzato per comunicare con la libreria in sandbox tramite chiamate RPC.

Questo esempio utilizza la configurazione sandbox predefinita fornita da DefaultConfig (sapi::SandboxConfig::DefaultConfig()).

3. Scrivi o modifica il codice host

È il momento di incorporare la libreria SAPI generata nel codice host.

Crea la sandbox

Utilizza sapi::zlib::ZlibSandbox sandbox; per creare un oggetto sandbox.

Utilizza sapi::zlib::ZlibApi api(&sandbox); per creare un'istanza dell'oggetto SAPI e rendere disponibili le funzioni in sandbox.

Per personalizzare la policy, i flag, le variabili di ambiente o i limiti della sandbox, inizia da sapi::SandboxConfig::DefaultConfig(), modifica i campi pertinenti (utilizzando sapi::Sandbox2Config::DefaultPolicyBuilder(), sapi::SandboxConfig::DefaultFlags(), o sapi::SandboxConfig::DefaultEnvironmentVariables() in modo che le impostazioni di base vengano mantenute) e passa sapi::SandboxConfig al costruttore della sandbox:

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

Utilizza i tipi SAPI

I tipi SAPI sono tipi speciali sotto forma di classi C++ fornite da SAPI perché a volte i tipi C normali non funzionano.

Il primo utilizzo di un tipo SAPI è visibile nella dichiarazione di strm, dove viene utilizzata una struttura SAPI: sapi::v::Struct<sapi::zlib::z_stream> strm;

Il tipo di modello (sapi::zlib::z_stream) è un buon esempio di codice generato automaticamente dalla regola di build.

Per ulteriori dettagli, consulta la pagina Variabili.

Effettua chiamate API

Per effettuare chiamate a defalteInit_, deflate o deflateEnd, utilizza l'oggetto SAPI. Se decidi di utilizzare l'approccio "modifica", devi assicurarti che i parametri della funzione corrispondano ai valori previsti.

Un esempio di ciascuna delle chiamate nell'esempio 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();

Utilizzo delle transazioni SAPI

SAPI isola il codice host dalla libreria in sandbox e consente al chiamante di riavviare o interrompere le richieste di trattamento dati problematiche. La transazione SAPI va un passo oltre e ripete automaticamente i processi non riusciti.

Per ulteriori dettagli, consulta la pagina Transazioni SAPI.

Esempi

In Esempi puoi trovare alcune librerie già preparate dal team SAPI.