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
- (Nur macOS) OSXFUSE installieren.
(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.
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.