Erste Schritte mit Sandboxed API

Auf dieser Seite erfahren Sie, wie Sie mit Sandboxed API (SAPI) Ihre eigene Sandbox-C/C++-Bibliothek erstellen. Verwenden Sie sie als Leitfaden zusammen mit den Beispielen und der Code-Dokumentation in den Header-Dateien.

Build-Abhängigkeiten

Die folgenden Abhängigkeiten müssen auf dem System installiert sein:

  • Linux-Kernel mit Unterstützung für UTS-, IPC-, Nutzer-, PID- und Netzwerk-Namespaces
  • Linux-Userspace-API-Header
  • Zum Kompilieren des Codes: GCC 6 (Version 7 oder höher empfohlen) oder Clang 7 (oder höher)
  • Zum automatischen Generieren von Header-Dateien: Clang Python-Bindungen
  • Python 3.5 oder höher
  • Bazel-Version 2.2.0 oder CMake- Version 3.12 oder höher.
    • Nur CMake: GNU Make oder eine Version der libcap-Bibliotheksheader und ein Build-Tool wie Ninja (empfohlen).

Bazel verwenden

Bazel ist das empfohlene Build-System und lässt sich am einfachsten einbinden.

In unserer Dokumentation wird der Clang-Compiler verwendet. Wenn Sie eine bestimmte Toolchain (z.B. Compiler, Linker usw.) benötigen, finden Sie in der Bazel Dokumentation Informationen zum Ändern der Standard-Compiler-Toolchain.

Debian 10 (Buster)

So installieren Sie Build-Abhängigkeiten:

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

Erforderliche Kernel-Optionen:

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

So installieren Sie Build-Abhängigkeiten:

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

CMake verwenden

CMake ist ein beliebtes Open-Source-Meta-Build-System, das Projektdateien für Build-Tools wie Ninja oder Make generiert.

Debian 10 (Buster)

So installieren Sie Build-Abhängigkeiten:

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

Erforderliche Kernel-Optionen:

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

So installieren Sie Build-Abhängigkeiten:

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

Entwicklungsprozess

Um eine C/C++-Bibliothek in einer Sandbox auszuführen, müssen Sie zwei Elemente für Ihr Projekt vorbereiten:

Möglicherweise kennen Sie die zlib-Bibliothek aus den Sandbox2 Beispielen hier wurde ein ganzes Programm (zpipe.c) in einer Sandbox ausgeführt. In den folgenden Schritten erfahren Sie, wie Sie mit SAPI die zlib-Bibliothek in einer Sandbox ausführen und die Sandbox-Bibliothek verwenden.

1. Entscheiden Sie, welche Funktionen benötigt werden

Wenn Sie sich den zlib-Host-Code (main_zlib.cc), sehen Sie, dass die Funktion des Tools darin besteht, Daten aus der Standardeingabe zu lesen und die Funktion deflate() von zlib zu verwenden, um die Daten zu komprimieren, bis ein EOF Marker gelesen wird. Insgesamt verwendet das Programm drei Funktionen aus zlib:

  • deflateInit_(): Zur Initialisierung für die Komprimierung
  • deflate(): Zum Ausführen des Komprimierungsvorgangs für den Datenblock
  • deflateEnd(): Zum Beenden der Komprimierung und Freigeben dynamisch zugewiesener Datenstrukturen

In einem realen Beispiel würden Sie die C/C++-Bibliothek überprüfen und entscheiden, welche Funktionen benötigt werden. Eine mögliche Strategie besteht darin, mit dem Host-Code zu beginnen und die Bibliothek ohne Sandbox zu verwenden. In einem zweiten Schritt könnten Sie dann die Sandbox-Bibliothek generieren und den Host-Code so anpassen, dass die Sandbox-Funktionsaufrufe verwendet werden.

2. Erstellen Sie die Build-Regel für die sapi_library

Nachdem Sie die drei zlib-Funktionen identifiziert haben, die aus der Sandbox-zlib-Bibliothek benötigt werden, können Sie die Build-Regel in der BUILD Datei definieren. Die Dokumentation zur Build-Regel sapi_library finden Sie auf der Seite Build-Regeln.

Das folgende Code-Snippet zeigt die Definition von sapi_library für das zlib-SAPI-Beispiel. Mit dem Attribut lib wird Bazel angewiesen, in der WORKSPACE-Datei nach der zlib-Bibliothek zu suchen.

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

Das Ergebnis ist, dass die Sandbox-zlib-Bibliothek generiert wird. Die Ausgabe ist das SAPI-Objekt, das in den Host-Code aufgenommen und verwendet werden kann, um über RPC-Aufrufe mit der Sandbox-Bibliothek zu kommunizieren.

In diesem Beispiel wird die Standard-Sandbox-Konfiguration verwendet, die von DefaultConfig (sapi::SandboxConfig::DefaultConfig()) bereitgestellt wird.

3. Host-Code schreiben oder ändern

Jetzt ist es an der Zeit, die generierte SAPI-Bibliothek in den Host-Code einzubinden.

Sandbox erstellen

Verwenden Sie sapi::zlib::ZlibSandbox sandbox;, um ein Sandbox-Objekt zu erstellen.

Verwenden Sie sapi::zlib::ZlibApi api(&sandbox);, um das SAPI-Objekt zu instanziieren und so die Sandbox-Funktionen zur Verwendung verfügbar zu machen.

Wenn Sie die Sandbox-Richtlinie, Flags, Umgebungsvariablen oder Limits anpassen möchten, beginnen Sie mit sapi::SandboxConfig::DefaultConfig(), ändern Sie die entsprechenden Felder (mit sapi::Sandbox2Config::DefaultPolicyBuilder(), sapi::SandboxConfig::DefaultFlags() oder sapi::SandboxConfig::DefaultEnvironmentVariables(), damit die Baseline-Einstellungen beibehalten werden) und übergeben Sie sapi::SandboxConfig an den Sandbox-Konstruktor:

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-Typen verwenden

SAPI-Typen sind spezielle Typen in Form von C++-Klassen, die von SAPI bereitgestellt werden, da reguläre C-Typen manchmal nicht funktionieren.

Die erste Verwendung eines SAPI-Typs ist in der Deklaration von strm zu sehen, wo ein SAPI-Struct verwendet wird: sapi::v::Struct<sapi::zlib::z_stream> strm;

Der Vorlagentyp (sapi::zlib::z_stream) ist ein gutes Beispiel für Code, der automatisch von der Build-Regel generiert wird.

Weitere Informationen finden Sie auf der Seite Variablen.

API-Aufrufe ausführen

Verwenden Sie das SAPI Objekt, um Aufrufe an defalteInit_, deflate oder deflateEnd auszuführen. Wenn Sie sich für die Methode „Ändern“ entscheiden, müssen Sie darauf achten, dass die Funktionsparameter mit den erwarteten Werten übereinstimmen.

Ein Beispiel für jeden der Aufrufe im zlib Beispiel:

  • 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-Transaktionen verwenden

SAPI isoliert den Host-Code von der Sandbox-Bibliothek und gibt dem Aufrufer die Möglichkeit, problematische Datenverarbeitungsanfragen neu zu starten oder abzubrechen. Die SAPI-Transaktion geht noch einen Schritt weiter und wiederholt fehlgeschlagene Prozesse automatisch.

Weitere Informationen finden Sie auf der Seite SAPI-Transaktionen.

Beispiele

Unter Beispiele finden Sie einige Bibliotheken, die bereits vom SAPI-Team vorbereitet wurden.