Sandbox-Technologie

In diesem Artikel wird die Sandbox-Erstellung in Bazel erläutert, die Installation von sandboxfs und die Fehlerbehebung in Ihrer Sandbox-Umgebung.

Sandboxing ist eine Strategie zur Einschränkung von Berechtigungen, mit der Prozesse voneinander oder von Ressourcen in einem System isoliert werden. Für Bazel bedeutet dies, den Zugriff auf das Dateisystem einzuschränken.

Die Dateisystem-Sandbox von Bazel führt Prozesse in einem Arbeitsverzeichnis aus, das nur bekannte Eingaben enthält, sodass Compiler und andere Tools keine Quelldateien sehen, auf die sie nur zugreifen sollten, es sei denn, sie kennen die absoluten Pfade zu ihnen.

Durch die Sandbox-Technologie wird die Hostumgebung in keiner Weise verborgen. Prozesse können kostenlos auf alle Dateien im Dateisystem zugreifen. Auf Plattformen, die Nutzer-Namespaces unterstützen, können über Prozesse jedoch keine Dateien außerhalb ihres Arbeitsverzeichnisses geändert werden. Dadurch wird gewährleistet, dass die Build-Grafik keine ausgeblendeten Abhängigkeiten hat, die die Reproduzierbarkeit des Builds beeinträchtigen können.

Konkret erstellt Bazel für jede Aktion ein execroot/-Verzeichnis, das bei der Ausführung als Arbeitsverzeichnis der Aktion fungiert. execroot/ enthält alle Eingabedateien für die Aktion und dient als Container für alle generierten Ausgaben. Danach verwendet Bazel ein vom Betriebssystem bereitgestellte Verfahren: Container unter Linux und sandbox-exec unter macOS, um die Aktion in execroot/ einzuschränken.

Gründe für das Sandboxing

  • Ohne Aktions-Sandboxing weiß Bazel nicht, ob ein Tool nicht deklarierte Eingabedateien (Dateien, die nicht explizit in den Abhängigkeiten einer Aktion aufgeführt sind) verwendet. Wenn sich eine der nicht deklarierten Eingabedateien ändert, geht Bazel davon aus, dass der Build auf dem neuesten Stand ist, und erstellt die Aktion nicht neu. Dies kann zu einem falschen inkrementellen Build führen.

  • Eine falsche Wiederverwendung von Cache-Einträgen verursacht Probleme beim Remote-Caching. Ein fehlerhafter Cache-Eintrag in einem gemeinsamen Cache wirkt sich auf jeden Entwickler des Projekts aus und das Löschen des gesamten Remote-Cache ist keine praktikable Lösung.

  • Sandboxing entspricht dem Verhalten der Remote-Ausführung. Wenn ein Build gut mit der Sandbox funktioniert, funktioniert er wahrscheinlich auch mit der Remote-Ausführung. Wenn Sie die Remote-Ausführung aller erforderlichen Dateien (einschließlich lokaler Tools) vornehmen, können Sie die Wartungskosten für Kompilierungscluster erheblich reduzieren, anstatt die Tools jedes Mal auf jedem Computer im Cluster installieren zu müssen.{101 }einen neuen Compiler testen oder eine Änderung an einem vorhandenen Tool vornehmen möchten.

Welche Sandbox-Strategie verwendet wird

Sie können die zu verwendende Art der Sandbox mit den Strategie-Flags auswählen. Mit der Strategie sandboxed wählt Bazel eine der unten aufgeführten Sandbox-Implementierungen aus und bevorzugt eine betriebssystemspezifische Sandbox, die weniger hermetisch ist. Nichtflüchtige Worker werden in einer generischen Sandbox ausgeführt, wenn Sie das Flag --worker_sandboxing übergeben.

Die Strategie local (oder standalone) führt keine Sandboxing-Methode aus. Er führt einfach die Befehlszeile der Aktion aus, wobei das Arbeitsverzeichnis auf den ausführbaren Stammverzeichnis Ihres Arbeitsbereichs festgelegt ist.

processwrapper-sandbox ist eine Sandbox-Strategie, für die keine "erweiterten" Features erforderlich sind. Sie sollte in jedem POSIX-System sofort einsatzbereit sein. Es wird ein Sandbox-Verzeichnis erstellt, das aus Symlinks besteht, die auf die ursprünglichen Quelldateien verweisen, führt die Befehlszeile der Aktion aus, wobei das Arbeitsverzeichnis auf dieses Verzeichnis und nicht auf "execroot" eingestellt ist und verschiebt dann die bekannten Ausgabeartefakte. 101}aus der Sandbox in den execroot und löscht die Sandbox. Dadurch wird verhindert, dass die Aktion versehentlich nicht deklarierte Eingabedateien verwendet und den ausführbaren Stamm mit unbekannten Ausgabedateien verbringt.

linux-sandbox geht noch einen Schritt weiter und baut auf der processwrapper-sandbox auf. Ähnlich wie die Docker verwendet sie Linux-Namespaces (Nutzer-, Bereitstellungs-, PID-, Netzwerk- und IPC-Namespaces), um die Aktion vom Host zu isolieren. Das bedeutet, dass das gesamte Dateisystem mit Ausnahme des Sandbox-Verzeichnisses schreibgeschützt ist, sodass die Aktion nicht versehentlich etwas im Host-Dateisystem ändern kann. Dadurch werden Situationen wie beispielsweise ein fehlerhafter Test versehentlich mit dem Befehl "rm -rf" im Verzeichnis "$HOME" verhindert. Sie können auch verhindern, dass die Aktion auf das Netzwerk zugreift. linux-sandbox verwendet PID-Namespaces, um zu verhindern, dass die Aktion andere Prozesse erkennt, und um alle Prozesse (auch von der Aktion ausgegebene Daemons) zuverlässig am Ende zu beenden.

darwin-sandbox ist ähnlich, aber für macOS. Dabei wird das sandbox-exec-Tool von Apple verwendet, um dieselbe Vorgehensweise wie bei einer Linux-Sandbox zu erreichen.

Sowohl linux-sandbox als auch darwin-sandbox funktionieren aufgrund von Einschränkungen in den von den Betriebssystemen bereitgestellten Mechanismen nicht in einem "verschachtelten" Szenario. Da Docker für seine Containermagie auch Linux-Namespaces verwendet, können Sie linux-sandbox nicht einfach in einem Docker-Container ausführen, es sei denn, Sie verwenden docker run --privileged. Unter macOS können Sie sandbox-exec nicht in einem Prozess ausführen, der bereits in einer Sandbox ausgeführt wird. In diesen Fällen greift Bazel automatisch auf die Verwendung von processwrapper-sandbox zurück.

Wenn Sie lieber einen Build-Fehler erhalten möchten, z. B. um nicht versehentlich eine weniger strikte Ausführungsstrategie zu erstellen, ändern Sie explizit die Liste der Ausführungsstrategien, die Bazel verwenden möchte (z. B. bazel build --spawn_strategy=worker,linux-sandbox).

Sandboxing

sandboxfs ist ein FUSE-Dateisystem, das eine beliebige Ansicht des zugrunde liegenden Dateisystems ohne Zeitstrafen verfügbar macht. Bazel verwendet sandboxfs, um execroot/ für jede Aktion sofort zu generieren. Dadurch werden die Kosten für Tausende von Systemaufrufen vermieden. Beachten Sie, dass weitere E/A-Vorgänge in execroot/ aufgrund von FUSE-Overhead langsamer sein können.

Sandboxing installieren

Führen Sie die folgenden Schritte aus, um sandboxfs zu installieren und damit einen Bazel-Build auszuführen:

Herunterladen

Laden Sie sandboxfs herunter und installieren Sie es, damit die sandboxfs-Binärdatei in Ihrer PATH landet.

sandboxfs ausführen

  1. (Nur macOS) OSXFUSE installieren.
  2. (nur macOS) Führen Sie diesen Befehl aus:

    sudo sysctl -w vfs.generic.osxfuse.tunables.allow_other=1
    

    Das müssen Sie nach der Installation und nach jedem Neustart tun, um zu gewährleisten, dass die macOS-Kerndienste die Sandbox-Technologie nutzen.

  3. Führen Sie einen Bazel-Build mit --experimental_use_sandboxfs aus.

    bazel build target --experimental_use_sandboxfs
    

Fehlerbehebung

Wenn local anstelle von darwin-sandbox oder linux-sandbox als Annotation für die ausgeführten Aktionen angezeigt wird, kann dies bedeuten, dass das Sandboxing deaktiviert ist. Übergeben Sie --genrule_strategy=sandboxed --spawn_strategy=sandboxed, um sie zu aktivieren.

Debugging

Folgen Sie den unten stehenden Strategien, um Probleme beim Sandboxing zu beheben.

Deaktivierte Namespaces

Auf einigen Plattformen wie Google Kubernetes Engine-Clusterknoten oder Debian sind Nutzer-Namespaces aufgrund von Sicherheitsbedenken standardmäßig deaktiviert. Wenn die Datei /proc/sys/kernel/unprivileged_userns_clone vorhanden ist und eine 0 enthält, können Sie Nutzer-Namespaces mit dem folgenden Befehl aktivieren:

   sudo sysctl kernel.unprivileged_userns_clone=1

Fehler bei der Ausführung von Regeln

Aufgrund der Systemeinrichtung kann die Sandbox möglicherweise keine Regeln ausführen. Wenn eine Meldung wie namespace-sandbox.c:633: execvp(argv[0], argv): No such file or directory angezeigt wird, deaktivieren Sie die Sandbox mit --strategy=Genrule=local für Genrules und --spawn_strategy=local für andere Regeln.

Detaillierte Fehlerbehebung für Build-Fehler

Wenn Ihr Build fehlgeschlagen ist, verwenden Sie --verbose_failures und --sandbox_debug, um Bazel den genauen Befehl anzuzeigen, der beim Fehlschlagen des Builds ausgeführt wurde, einschließlich des Teils, der die Sandbox eingerichtet.

Beispiel für Fehlermeldung:

ERROR: path/to/your/project/BUILD:1:1: compilation of rule
'//path/to/your/project:all' failed:

Sandboxed execution failed, which may be legitimate (such as a compiler error),
or due to missing dependencies. To enter the sandbox environment for easier
debugging, run the following command in parentheses. On command failure, a bash
shell running inside the sandbox will then automatically be spawned

namespace-sandbox failed: error executing command
  (cd /some/path && \
  exec env - \
    LANG=en_US \
    PATH=/some/path/bin:/bin:/usr/bin \
    PYTHONPATH=/usr/local/some/path \
  /some/path/namespace-sandbox @/sandbox/root/path/this-sandbox-name.params --
  /some/path/to/your/some-compiler --some-params some-target)

Sie können nun das generierte Sandbox-Verzeichnis prüfen und sehen, welche Dateien Bazel erstellt hat und den Befehl noch einmal ausführen, um sein Verhalten zu prüfen.

Beachten Sie, dass Bazel das Sandbox-Verzeichnis nicht löscht, wenn Sie --sandbox_debug verwenden. Wenn Sie das Debugging nicht aktiv ausführen, sollten Sie --sandbox_debug deaktivieren, da das Laufwerk im Laufe der Zeit voll ausgelastet wird.