WASI-Standard: Starten Sie WebAssembly über das Web hinaus

Am 27. März kündigten wir bei Mozilla den Beginn der Standardisierung von WASI an, der WebAssembly-Systemschnittstelle (WebAssembly-Systemschnittstelle).

Warum: Entwickler haben begonnen, WebAssembly außerhalb des Browsers zu verwenden, da WASM eine schnelle, skalierbare und sichere Möglichkeit bietet, auf allen Computern denselben Code auszuführen. Wir haben jedoch noch keine solide Grundlage für eine solche Entwicklung. Außerhalb des Browsers benötigen Sie eine Möglichkeit, mit dem System, dh der Systemschnittstelle, zu kommunizieren. Die WebAssembly-Plattform verfügt jedoch noch nicht darüber.

Was: WebAssembly ist ein Assembler für eine konzeptionelle und keine physische Maschine. Es funktioniert auf verschiedenen Architekturen, daher wird eine Systemschnittstelle benötigt, damit ein konzeptionelles Betriebssystem auf verschiedenen Betriebssystemen funktioniert.

WASI ist: Es ist eine Systemschnittstelle für die WebAssembly-Plattform.

Wir bemühen uns, eine Systemschnittstelle zu erstellen, die ein echter Begleiter für WebAssembly mit maximaler Portabilität und Sicherheit wird.

Wer: Als Teil des WebAssembly-Entwicklungsteams haben wir eine Untergruppe organisiert, die sich auf WASI standardisiert. Wir haben bereits interessierte Partner gesammelt und suchen neue.

Hier sind einige Gründe, warum wir, unsere Partner und Unterstützer dies für wichtig halten:

Sean White, F & E-Direktor von Mozilla:
„WebAssembly verändert bereits die Art und Weise, wie Menschen neue Arten ansprechender Inhalte bereitstellen. Es hilft Entwicklern und Erstellern von Inhalten. Bisher hat alles über Browser funktioniert, aber mit WASI werden mehr Benutzer und mehr Geräte an verschiedenen Orten von WebAssembly profitieren. “

Tyler McMullen, CTO Schnell:
„Wir sehen in WebAssembly eine Plattform für die schnelle und sichere Ausführung von Code in einer Edge-Cloud. Trotz der unterschiedlichen Umgebungen (Edge und Browser) müssen Sie dank WASI den Code nicht auf jede Plattform portieren. "

Miles Borins, CTO des Node Steering Committee:
„WebAssembly kann eines der größten Probleme von Node lösen: wie man nahezu native Geschwindigkeit erreicht und Code wiederverwendet, der in anderen Sprachen wie C und C ++ geschrieben ist, während Portabilität und Sicherheit erhalten bleiben. Die WASI-Standardisierung ist der erste Schritt in diese Richtung. “

Lori Voss, Mitbegründerin von npm:
„Npm ist äußerst begeistert von den potenziellen WebAssembly-Funktionen des npm-Ökosystems, da es viel einfacher ist, nativen Code in serverseitigen JavaScript-Anwendungen auszuführen. Wir freuen uns auf die Ergebnisse dieses Prozesses. “

Das ist also eine große Veranstaltung!

Derzeit gibt es drei WASI-Implementierungen:


WASI-Demonstration in Aktion:


Als nächstes werden wir über Mozillas Vorschlag sprechen, wie diese Systemschnittstelle funktionieren soll.

Was ist eine Systemschnittstelle?


Viele sagen, Sprachen wie C bieten direkten Zugriff auf Systemressourcen. Dies ist jedoch nicht ganz richtig. Auf den meisten Systemen haben diese Sprachen keinen direkten Zugriff auf Dinge wie das Öffnen oder Erstellen von Dateien. Warum nicht?

Weil diese Systemressourcen - Dateien, Speicher und Netzwerkverbindungen - für Stabilität und Sicherheit zu wichtig sind.

Wenn ein Programm versehentlich die Ressourcen eines anderen ruiniert, kann dies zu einem Absturz führen. Schlimmer noch, wenn ein Programm (oder ein Benutzer) speziell in die Ressourcen anderer Personen eindringt, kann es vertrauliche Daten stehlen.



Daher müssen Sie steuern können, welche Programme und Benutzer auf Ressourcen zugreifen können. Lange Zeit hatten Systementwickler einen Weg gefunden, eine solche Kontrolle bereitzustellen: Schutzringe.

Mit Schutzringen richtet das Betriebssystem im Wesentlichen eine Schutzbarriere um die Systemressourcen ein. Das ist der Kern. Nur es kann Vorgänge wie das Erstellen einer Datei, das Öffnen einer Datei oder das Öffnen einer Netzwerkverbindung ausführen.

Benutzerprogramme werden außerhalb des Kernels im sogenannten Benutzerbereich ausgeführt. Wenn das Programm die Datei öffnen möchte, sollte es nach dem Kernel fragen.



Hier entsteht das Konzept eines Systemaufrufs. Wenn ein Programm den Kernel nach einer Operation fragen muss, sendet es einen Systemaufruf. Der Kernel überprüft den kontaktierenden Benutzer und prüft, ob er über die Berechtigung zum Zugriff auf diese Datei verfügt.

Auf den meisten Geräten können Sie nur über Systemaufrufe auf Systemressourcen zugreifen.



Das Betriebssystem bietet Zugriff auf Systemaufrufe. Aber wenn jedes Betriebssystem seine eigenen Systemaufrufe hat, müssen sie dann nicht unterschiedliche Versionen des Codes schreiben? Zum Glück nicht. Das Problem wird durch Abstraktion gelöst.

Die meisten Sprachen haben eine Standardbibliothek. Beim Codieren muss der Programmierer nicht wissen, für welches System er schreibt. Es wird nur die Schnittstelle verwendet. Beim Kompilieren wählt Ihre Toolkette dann aus, welche Schnittstellenimplementierung für welches System verwendet werden soll. Diese Implementierung verwendet Funktionen aus der API des Betriebssystems und ist daher spezifisch für diese.

Hier erscheint das Konzept einer Systemschnittstelle. Wenn Sie beispielsweise printf für einen Windows-Computer kompilieren, wird die Windows-API verwendet. Wenn für Mac oder Linux kompiliert, wird POSIX verwendet.



Dies stellt jedoch ein Problem für WebAssembly dar. Hier wissen wir nicht, für welches Betriebssystem das Programm auch während der Kompilierung optimiert werden soll. Daher können Sie die Systemschnittstelle eines Betriebssystems in der Implementierung der Standardbibliothek in WebAssembly nicht verwenden.



Ich habe bereits gesagt, dass WebAssembly ein Assembler für eine konzeptionelle Maschine ist , keine echte Maschine. Ebenso benötigt WebAssembly eine Systemschnittstelle für ein konzeptionelles und kein reales Betriebssystem.

Es gibt jedoch bereits Laufzeiten, in denen WebAssembly auch ohne diese Systemschnittstelle außerhalb des Browsers ausgeführt werden kann. Wie machen sie das? Mal sehen.

Wie funktioniert WebAssembly jetzt außerhalb des Browsers?


Das erste Tool zum Generieren von WebAssembly-Code war Emscripten. Es emuliert im Web eine bestimmte Betriebssystemschnittstelle - POSIX. Dies bedeutet, dass der Programmierer die Funktionen aus der Standard-C-Bibliothek (libc) verwenden kann.

Zu diesem Zweck verwendet Emscripten eine eigene libc-Implementierung. Es ist in zwei Teile unterteilt: Der erste Teil wird in ein WebAssembly-Modul kompiliert und der andere Teil wird in JS-Glue-Code implementiert. Dieser JS-Kleber sendet Anrufe an den Browser, der mit dem Betriebssystem kommuniziert.



Der größte Teil des frühen WebAssembly-Codes wird mit Emscripten kompiliert. Als Benutzer anfingen, WebAssembly ohne Browser auszuführen, begannen sie daher, Emscripten-Code auszuführen.

In diesen Laufzeiten sollten Sie also Ihre eigenen Implementierungen für alle Funktionen erstellen, die im JS-Glue-Code enthalten waren.

Aber es gibt ein Problem. Die vom JS-Klebercode bereitgestellte Schnittstelle wurde nicht als Standard- oder öffentliche Schnittstelle konzipiert. Um beispielsweise wie in der normalen API read aufzurufen, verwendet der JS- _system3(which, varargs) Aufruf _system3(which, varargs) .



Der erste Parameter ist eine Ganzzahl, die immer mit der Zahl im Namen übereinstimmt (in unserem Fall 3).

Der zweite Parameter, varargs listet die Argumente auf. Es heißt varargs weil wir eine andere Anzahl von Argumenten haben können. WebAssembly erlaubt jedoch nicht die Übergabe einer variablen Anzahl von Argumenten an eine Funktion. Daher werden sie durch einen linearen Speicher übertragen, der unsicher und langsamer als durch Register ist.

Für Emscripten im Browser ist dies normal. Aber jetzt sehen die Laufzeiten dies als De-facto-Standard an und implementieren ihre eigenen Versionen von JS-Kleber. Sie emulieren die internen Details der POSIX-Emulationsschicht.

Dies bedeutet, dass sie den Code erneut implementieren (z. B. Argumente als Heap-Werte übergeben), was angesichts der Emscripten-Einschränkungen sinnvoll war. In diesen Laufzeitumgebungen gibt es jedoch keine derartigen Einschränkungen.



Wenn wir das WebAssembly-Ökosystem seit Jahrzehnten aufbauen, braucht es ein solides Fundament, keine Krücken. Dies bedeutet, dass unser tatsächlicher Standard keine Emulationsemulation sein kann.

Aber welche Grundsätze gelten in diesem Fall?

Welche Grundsätze sollte die WebAssembly-Systemschnittstelle einhalten?


Zwei Grundprinzipien von WebAssembly:

  • Portabilität
  • Sicherheit

Wir gehen über den Browser hinaus, behalten aber diese Schlüsselprinzipien bei.

Der POSIX-Ansatz und das Unix-Zugriffskontrollsystem liefern jedoch nicht das gewünschte Ergebnis. Mal sehen, was das Problem ist.

Portabilität


POSIX bietet Portabilität des Quellcodes. Sie können denselben Quellcode mit verschiedenen libc-Versionen für verschiedene Computer kompilieren.



Aber WebAssembly muss darüber hinausgehen. Wir müssen einmal kompilieren, um auf einer ganzen Reihe verschiedener Systeme ausgeführt zu werden. Wir brauchen tragbare Binärdateien.



Dies vereinfacht die Codeverteilung.

Wenn beispielsweise native Knotenmodule in WebAssembly geschrieben sind, müssen Benutzer bei der Installation von Anwendungen mit nativen Modulen Node-Gyp nicht ausführen, und Entwickler müssen nicht Dutzende von Binärdateien konfigurieren und verteilen.

Sicherheit


Wenn der Code das Betriebssystem auffordert, Eingaben oder Ausgaben vorzunehmen, sollte das Betriebssystem die Sicherheit dieses Vorgangs bewerten, normalerweise unter Verwendung eines Zugriffskontrollsystems, das auf Besitz und Gruppen basiert.

Ein Programm fordert beispielsweise zum Öffnen einer Datei auf. Der Benutzer verfügt über einen bestimmten Satz von Dateien, auf die er Zugriff hat.

Wenn ein Benutzer ein Programm startet, startet das Programm im Namen dieses Benutzers. Wenn der Benutzer Zugriff auf die Datei hat - entweder er ist ihr Eigentümer oder Teil einer Gruppe, die Zugriff auf die Datei hat -, hat das Programm denselben Zugriff.



Dies schützt Benutzer voreinander, was früher sinnvoll war, als viele Leute an einem Computer arbeiteten und Administratoren die Software kontrollierten. Dann war die Hauptbedrohung, dass andere Benutzer Ihre Dateien betrachteten.

Alles hat sich geändert. Derzeit sind Systeme normalerweise Einzelbenutzer, verwenden jedoch Code von Drittanbietern mit unbekannter Zuverlässigkeit. Die Hauptbedrohung geht jetzt von dem Code aus, den Sie selbst ausführen.

Für die Bibliothek in Ihrer Anwendung wurde beispielsweise ein neuer Betreuer gestartet (wie dies häufig bei Open Source der Fall ist). Er kann ein aufrichtiger Aktivist sein ... oder ein Eindringling. Und wenn er Zugriff auf Ihr System hat - zum Beispiel die Möglichkeit, eine Datei zu öffnen und über das Netzwerk zu senden -, kann dieser Code großen Schaden anrichten.


Verdächtige Anwendung : Ich arbeite für Benutzer Bob. Darf ich seine Bitcoin-Brieftasche öffnen?
Kern : Für Bob? Natürlich!
Verdächtige App : Großartig! Was ist mit der Netzwerkkonnektivität?

Aus diesem Grund ist die Verwendung von Bibliotheken von Drittanbietern gefährlich. WebAssembly bietet Sicherheit auf andere Weise - über die Sandbox. Hier kann der Code nicht direkt mit dem Betriebssystem kommunizieren. Aber wie kann man dann auf Systemressourcen zugreifen? Die Sandbox-Funktionen des Hosts (des Browsers oder der Wasm-Laufzeit), die der Code verwenden kann.

Dies bedeutet, dass der Host die Funktionalität des Programms programmgesteuert einschränkt und Sie nicht einfach im Namen des Benutzers handeln können, was zu Systemaufrufen mit vollen Benutzerrechten führt.

Eine Sandbox an sich macht das System nicht sicher - der Host kann weiterhin die volle Funktionalität auf die Sandbox übertragen. In diesem Fall bietet er keinen Schutz. Die Sandbox bietet Hosts jedoch zumindest eine theoretische Möglichkeit, ein sichereres System aufzubauen.


WA : Bitte, hier sind einige sichere Spielzeuge für die Interaktion mit dem Betriebssystem (safe_write, safe_read).
Verdächtige Anwendung : Oh verdammt ... wo ist mein Zugang zum Netzwerk?

In jeder Systemschnittstelle müssen Sie diese beiden Prinzipien einhalten. Portabilität erleichtert die Softwareentwicklung und -verteilung, und Tools zum Schutz des Hosts und der Benutzer sind unbedingt erforderlich.

Wie soll eine solche Systemschnittstelle aussehen?


Was sollte angesichts dieser beiden Grundprinzipien die WebAssembly-Systemschnittstelle sein?

Dies werden wir im Standardisierungsprozess herausfinden. Wir haben jedoch einen Vorschlag für den Anfang:

  • Erstellen eines modularen Satzes von Standardschnittstellen
  • Beginnen wir mit der Standardisierung des Wasi-Core-Kernmoduls.




Was wird in Wasi-Core sein? Dies sind die Grundlagen, die von allen Programmen benötigt werden. Das Modul deckt die meisten POSIX-Funktionen ab, einschließlich Dateien, Netzwerkverbindungen, Uhren und Zufallszahlen.

Ein Großteil der Grundfunktionen erfordert einen sehr ähnlichen Ansatz. Beispielsweise wird ein POSIX-dateiorientierter Ansatz mit Systemaufrufen zum Öffnen, Schließen, Lesen und Schreiben bereitgestellt, und alles andere sind Add-Ons von oben.

Wasi-Core deckt jedoch nicht alle POSIX-Funktionen ab. Beispielsweise passt das Konzept eines Prozesses nicht eindeutig in WebAssembly. Darüber hinaus ist klar, dass jede WebAssembly-Engine Prozessvorgänge wie fork . Wir wollen aber auch eine fork ermöglichen.



Sprachen wie Rust verwenden wasi-core direkt in ihren Standardbibliotheken. Beispielsweise wird open from Rust beim Kompilieren in WebAssembly durch Aufrufen von __wasi_path_open .

Für C und C ++ haben wir wasi-sysroot erstellt , das libc in Bezug auf wasi-core-Funktionen implementiert.



Wir erwarten, dass Compiler wie Clang mit der WASI-API interagieren können, und vollständige Toolketten wie der Rust-Compiler und Emscripten werden WASI als Teil ihrer Systemimplementierungen verwenden.

Wie ruft benutzerdefinierter Code diese WASI-Funktionen auf?

Die Laufzeit, in der der Code ausgeführt wird, übergibt die Wasi-Core-Funktion und platziert das Objekt in der Sandbox.



Dies bietet Portabilität, da jeder Host seine eigene Wasi-Core-Implementierung speziell für seine Plattform haben kann: von WebAssembly-Laufzeiten wie Mozilla Wasmtime und Fastly Lucet bis hin zu Node oder sogar einem Browser.

Es bietet auch eine zuverlässige Isolation, da der Host auf Softwarebasis auswählt, welche Wasi-Core-Funktionen auf die Sandbox übertragen werden sollen, dh welche Systemaufrufe dies zulassen sollen. Das ist Sicherheit.



WASI verbessert und erweitert die Sicherheit durch die Einführung eines autorisierungsbasierten Sicherheitskonzepts in das System.

Wenn der Code die Datei öffnen muss, ruft er normalerweise open mit dem Pfadnamen in der Zeile auf. Anschließend prüft das Betriebssystem, ob der Code das Recht auf eine solche Aktion hat (basierend auf den Rechten des Benutzers, der das Programm gestartet hat).

Im Fall von WASI müssen Sie beim Aufrufen einer Funktion für den Zugriff auf eine Datei einen Dateideskriptor übergeben, an den Berechtigungen für die Datei selbst oder für das Verzeichnis, das die Datei enthält, angehängt sind.

Daher können Sie keinen Code haben, der Sie versehentlich auffordert, /etc/passwd zu öffnen. Stattdessen kann Code nur mit eigenen Verzeichnissen arbeiten.



Dadurch können verschiedene Systemaufrufe sicher in den isolierten Code aufgelöst werden, da die Funktionen dieser Systemaufrufe begrenzt sind.

Und so in jedem Modul. Standardmäßig hat das Modul keinen Zugriff auf Dateideskriptoren. Wenn der Code in einem Modul jedoch einen Dateideskriptor hat, kann er an Funktionen übergeben werden, die in anderen Modulen aufgerufen werden. Oder erstellen Sie eingeschränktere Versionen des Dateideskriptors, um sie an andere Funktionen zu übergeben.

Daher übergibt die Laufzeit Dateideskriptoren, die die Anwendung im Code der obersten Ebene verwenden kann, und dann werden Dateideskriptoren nach Bedarf im Rest des Systems verteilt.



Dies bringt WebAssembly näher an das Prinzip der geringsten Berechtigungen heran, bei dem das Modul nur auf die Mindestressourcen zugreifen kann, die für seine Arbeit erforderlich sind.

Dieses Konzept basiert auf privilegierter Sicherheit wie in CloudABI und Capsicum. Eines der Probleme bei diesen Systemen ist die schwierige Portabilität des Codes. Wir glauben jedoch, dass dieses Problem gelöst werden kann.

Wenn der Code bereits openat mit relativen Dateipfaden verwendet, openat Kompilieren des Codes einfach.

Wenn der Code open und die Migration im Openat-Stil zu drastisch ist, bietet WASI eine inkrementelle Lösung. Mit libpreopen erstellen Sie eine Liste von Dateipfaden, auf die die Anwendung legalen Zugriff hat. Verwenden Sie dann open , aber nur mit diesen Pfaden.

Was weiter?


Wir glauben, dass Wasi-Core ein guter Anfang ist. Es behält die Portabilität und Sicherheit von WebAssembly bei und bietet eine solide Grundlage für das Ökosystem.

Nach der vollständigen Standardisierung von wasi-core müssen jedoch andere Probleme gelöst werden, darunter:

  • asynchrone Eingabe-Ausgabe
  • Dateiüberwachung
  • Dateisperre

Dies ist nur der Anfang. Wenn Sie also Ideen haben, engagieren Sie sich !

Source: https://habr.com/ru/post/de446764/


All Articles