My main side-project is a server that runs personal web-based applications. The idea is that web apps could be built and distributed more like the smartphone ecosystem than the current model of “everything online is a service”.
I’ll talk more about this project some other time, but for now suffice it to say that for the idea to work, a user should be able to install a web-app without putting themselves at risk.
Basically, whatever code runs and however it runs, it must have very limited and controlled access to the underlying system.
Examples of actions that must be controlled:
- read/write to disk
- outgoing web requests
- create network listeners (the host takes care of that)
- consume too much space on the disk
- consume too many CPU cycles
- consume too much memory
My project has been going on for close to two years now, and a lot of that time was spent trying to figure out how I can run code with such restrictions.
My first attempt was using a JS “sandbox VM” thing. I quickly saw that this wasn’t good enough. JS is like the gunk that comes off of pine trees: once you touch it, it gets everywhere and you just can’t be sure you’ve gotten rid of it. It’s nearly impossible with current JS to isolate one part of it. There is always some weird prototype chain trick that can be exploited to get out.
My second hope was for JS Realms. It addresses the above point directly: arm the language with the ability to segregate parts of itself. This seems like a good move for the JS ecosystem as a whole, and I thought the proposal was moving along nicely, but it appears stalled.
Next, I dove into containers. If JS can’t be tamed then I’ll tame it at the OS level. Or so I thought, but this was tough. First, I am not a Linux system expert by any means. I know just enough to get by. I got hung up on esoteric linux stuff. Second, JS isn’t a great language for this kind of work, so I needed an upgrade. A lot of container tech is written in Go, so that’s what I landed on.
It’s weird that container stuff is written in Go since it’s inherently multi-threaded, which causes problems. But hey whatever, I still like the language (especially now that I’ve moved on from containers).
Anyhoo I learned me some Go, and I am now quite dangerous with this language, and I got some custom built container orchestration Rube Goldberg machine going.
It worked, sort of. I want to be able to turn sandboxes on and off rapidly, but the latency and overhead of the system was not encouraging. I didn’t expect it to be great, but I was desperately in need of something.
Along the way I learned that containers are one thing, sandboxes are another. While creating containers is easy, making a truly robust sandbox is a different level of challenge. Any little mistake in the setup of the container can leave a door wide open for an escape. Jessie Frazelle can do it, but I should probably not try.
Sometime after I had sunk a lot of work into my container-dispatcher-axe-throwing thing I started noticing a project called Deno. It was in very early stages then, but its tagline was a promise that went straight to my heart: “A Secure Runtime For JS”. Oh baby.
I decided this had to be the solution. Each app could run in its own instance of Deno with its own permissions, and cgroups to control resources. Startup latency is good enough for now, and I can already think of ways to make it seem faster.
I created a branch and obliterated my container-juggling-circus-act code and went all-in on Deno. I figured I’d have time to write all the other parts of the system before Deno is ready (I was wrong, I’m nowhere near done).
Deno and Security
At first I wasn’t sure how serious the devs were about the security aspect of the project. But come on, “secure” is the fourth word on their homepage after “Deno is a…” so surely they’d be on it?
I filed a few issues (#2705, #2761, #3401) against early builds around security and permissions. Ryan Dahl, creator of Deno (and co-creator of Node) was receptive to my concerns, and most of my issues are now fixed. This gives me some confidence that Deno would eventually become pretty tight.
Sadly I couldn’t contribute actual code because I would have to know Rust. Having learned some Go the year before my enthusiasm for learning Rust was low. Besides I’d like to become less dangerous with Go instead of super-dangerous in two languages.
At this stage I wouldn’t run untrusted code in Deno, but since it’s reached 1.0 we can hope that the community will start poking at it and expose the flaws, and they’ll get fixed. The fundamentals of its design are sound so it’s a matter of finding and quashing bugs.
Thank You Deno
I’m really glad Deno is here. It’s important to be able to run code that you can’t necessarily trust in an environment that limits what an attacker should do. We should have that for JS.
I’m also glad because my project would be dead without it.
This was day 16 of the #100DaysToOffload challenge.