WebAssembly: Adding Python support to WASM language runtimes
wasmlabs.dev
wasmlabs.dev
The thing I most want to solve right now is this: I want to write a regular Python application that can safely execute untrusted Python code in a WASM sandbox as part of its execution.
I want to do this so I can let end users customize my web applications in weird and interesting ways by pasting their own Python code into a textarea - think features like "run this Python code to transform my stored data" - without them being able to break my system.
This feels like it should be pretty easy with WebAssembly! It's the classic code sandboxing problem - long a big challenge in Python world - finally solved in a robust way.
I've been finding it surprisingly hard to get a proof-of-concept of this working though.
Essentially I want to be able to do this, in my regular Python code:
import some_webassembly_engine
python = some_webassembly_engine.load(
"python.wasm",
max_cpu_time_in_seconds=3.0,
max_allowed_memory_in_bytes=32000000
)
result = python.execute("3 + 5")
I've not yet figured out the incantations I need to actually do this - in particular the limits on CPU and memory time.I posed this question on Mastodon recently and Jim Kring put together this demo, which gets most of the way there (albeit using an old Python 3.6 build): https://github.com/jimkring/python-sandbox-wasm
It doesn't feel like this should be as hard to figure out as it is!
1. Sandboxing for CPU time and max allowed memory requires the enterprise edition, so you'd have to pay for it.
2. The Python engine isn't 100% compatible with regular Python, although that may not matter for your use case as the compatibility is pretty good and issues mostly show up around extension modules.
1. Capability control only works for JavaScript (https://www.graalvm.org/latest/reference-manual/embed-langua...)
2. The documentation says in no uncertain terms that running untrusted code is unsupported (https://www.graalvm.org/latest/security-guide/#security-mode...)
(I'm so close to building my own search engine just against my own content there.)
If anyone else is awake and wants to pick up the baton...
- CPU limits are just using an arbitrary amount of wasmtime "fuel". Would be worth looking into epoch interrupts instead.
- Memory limits aren't implemented. Seems like wasmtime-py doesn't expose bindings to anything with a wasmtime::ResourceLimiter trait.
- All i/o is going through tempfiles instead of dealing with proper interfaces.
Either way, I couldn't figure out how to do the above sequence of steps with any of the available Python WASM runtimes - they're all very under-documented at the moment, sadly. I tried all three of these:
- https://github.com/wasmerio/wasmer-python
The remaining piece of the puzzle would be to create a wit-bindgen guest generator https://github.com/bytecodealliance/wit-bindgen#guests for this build of the python interpreter. You could then seamlessly call back and forth between the host and guest pythons, without even knowing that wasmtime is under the hood.
In general the Wasm Component ecosystem is still a few months away from being generally useful. There are a lot of people across the bytecode alliance working on the fundamentals right now, and we are making great progress, but its not ready to ship quite yet.
https://gist.github.com/pims/711549577759ad1341f1a90860f1f3a...
Just tried this and it works great!
I changed app.py to this:
import sqlite3
print(sqlite3.connect(":memory:").execute(
"select sqlite_version()"
).fetchone()[0])
And it output "3.39.2" - but the same code in my regular Python interpreter output "3.40.1", which demonstrates that the WASM Python there has its own WASM-compiled SQLite.And even then the security stuff is based on previously existing work in the cloud space. Which has existed for some time but is not widespread.
Python SDK: https://extism.org/docs/integrate-into-your-codebase/python-...
Python is 1 of 15+ languages we support, and as far as I know it's the easiest way to setup a wasm engine in your app, load wasm code, and call a function using complex data I/O.
I'm one of the authors - happy to share more about it, or if you'd like to join our Discord we have lots of active users and contributors there: https://discord.gg/cx3usBCWnc
cpu is really controlled by # milliseconds until the wasm code is trapped.
I see that the library itself is available in Python, but this page https://extism.org/docs/category/write-a-plug-in currently only lists Rust, JavaScript, Go, Haskell, AssemblyScript, Zig and C. Any chance Python might get added to that list as well?
Just found https://github.com/extism/extism-sqlite3 which is very relevant to my interests too!
[1] https://github.com/extism/js-pdk [2] https://discord.gg/mAADpt9r
I'm excited for pairing wasm with WebGPU, which will likely unblock these projects from building support for the web/untrusted ecosystem. A useful project would be one that makes this integration really easy to build today and a flip of the switch to turn on in the future.
What reason is there to suppose this is true? It seems surprising to me.
So, most ML users are python users. I don't know how that group compares to non-ML python users, but I have a feeling there isn't a flood of eager new Django devs the way there is Pytorch users. Most non-ML things you could do with python can be done similarly well in Go/Rust/Typescript, but there's no other option for most ML stuff.
[1] https://lp.jetbrains.com/python-developers-survey-2021/#Gene...
I'm curious what the longer-term trends look like; not much change between consecutive years.
Data analysis is basically a pre-requisite for ML, so the combined "data stuff" usage is quite a lot bigger than web dev usage!
One reason is its just super easy for input output operations. ML is all about data and getting the data to the right place is really easy in python compared to some other languages..
Python is OOP; but the "classical" data-centric languages are actually all more or less in the FP space. (I count array languages and APL-likes to FP in this case).
Just an example: You don't have immutable data types by default in Python. This is actually a pretty bad default for data processing tasks.
You now say it's like that because Python has already quite some libs / frameworks in that direction.
This looks like circular reasoning.
Also the the "prototypical ML dude" comes form the math department. People with math background have a much easier time to grasp FP than procedural programming. FP is much more "natural" when you're used to math.
(Procedural programming says things like `x = x + 1`, but even my grandma would know that "this has no solution", or is likewise "plain wrong" ;-))
In fact, in what concerns compute, it is hardly any better than GL ES compute shaders that Chrome refused to add to WebGL.
For external libraries, it requires you to mount the libraries with WASI when running the python.wasm module. Another option we're exploring is to use wasi-vfs[1] to include some common modules in our pre-built binaries. For example, Ruby does require some extra libraries for common workloads (like JSON parsing). This is still on the exploration phase, but we may do something with it.
It builds a single Python WASM module with all dependencies included (they use VFS) and a Dockerfile to make the process easy (and actually worked first go). It does produce large files though: wasi-python3.11.wasm 110MB
In addition to wasmtime socket, WasmEdge supports async non-blocking socket for high-performance apps
I don't understand what's the point of this article.
I'm already okay with brython, which is fast enough, but sometimes it generates JavaScript errors, which are difficult to understand.
https://www.ibm.com/docs/en/zos/2.4.0?topic=descriptions-lan...
Is it just the sandbox or is there anything else I'm missing?
I remember a NodeJs CVE that was caused by a poisoned dependency. It was affecting people when downloading it from npm.
There’s still a gap here to cover, but the benefits may be worth :)
The incident I was talking about was the event-stream[1] vulnerability. The attacker introduced code that looked for the data of a crypto wallet. This data was stored in the user's home.
By default, interpreters may get access to the same resources that the user running the process. In Wasm, the resources are granted manually.
[1] https://blog.npmjs.org/post/180565383195/details-about-the-e...
What's the difference to run the code under a different user (like for example `nobody` for "full sandboxing", or a "clone of nobody" with some additional access rights)?
So macOS needs to work.
Microsoft Flight Simulator uses WebAssembly for its extensions system. I want to do the same thing for my own projects.
That's not even funny, as in real life people would run something like that actually in a VM.
So we have now: HW memory protection -> HW virtualization -> VM -> OS -> Docker -> WASM -> language runtime -> some code snippet.
Things become quite crazy these days, to be honest…
You build one image for the wasm32/wasi platform and now it can run on any architecture that has Docker, just like that. And also you don't carry the whole OS baggage of a typical container. Only the language runtime. So download times are faster.
---
Lastly, the Docker deployment is just the easiest way for a WASM application to reach people. They can try it out without the necessity to setup and support a WASI runtime.