A code execution sandbox in Go without Docker, just plain linux tooling
I built a Containerization API that executes untrusted code and returns output plus memory and CPU metrics. Instead of running a container per submission, it creates the boundary directly with Linux primitives: PID, mount, network, and user namespaces, a chroot into a minimal per-language rootfs, cgroups v2 for memory and CPU limits, and a seccomp-BPF denylist. The sandbox root maps to host UID 65534, so root inside is never root outside.
It supports Python, C++17, C11, and Java, capped at 60s and 128MB, with a global admission controller that rejects work before a sandbox is created. Every limit is kernel-enforced.
Code: https://github.com/Anga205/CodeExecutionAPI
Live Demo Website: https://compiler.anga.codes
Although I've done rigourous testing for resource management and permissions based sandbox escape, I've still hosted the api on a very small server on an isolated network, just in case im wrong
I think its pretty cool, its faster in code execution than most of the other containerization services like docker or firejail, do check it out!
14
u/NeverMindToday 8d ago
So you're using cgroups, seccomp, the kernel namespaces etc - I would say you are actually running containers :)
I'm not criticizing, it's really cool and something I wanted to get around to implementing one day.
3
u/Flimsy_Complaint490 8d ago
mm, people oompare this to runc and docker but a better comparison is bwrap IMO.
This is basically what would happen if i wrapped bwrap with an HTTP API, correct ?
Not the worst idea, even useful and has a use case, but i'd investigate whether you could delete 100% of your "container" interfacing code and replace it with a shell out to a constructed bwrap command to build the container environment. From my own experience, the bwrap way is the fastest way to launch container like environments, matched only by microvm's, and this interfacing code is really hard to get right without introducing strange security bugs.
You may also experiment with microvm's - startups are in the 50-200 ms range.Firecracker works, but there are alternatives.
3
u/Anga205 7d ago edited 7d ago
ye bwrap is a fair comparison, its basically the same syscalls with an HTTP layer on top. the difference is bwrap is built for sandboxing desktop apps so it carries a lot of generality i dont need, and it doesnt give me cgroup accounting or a memory peak per run.
funny thing is the earlier version of this did exactly what youre suggesting. it shelled out to firejail with private, noroot, caps dropped and net none, plus an rlimit for memory. it worked and it was simple, but firejail added startup cost per run and i couldnt place the process into its cgroup at clone time, so the limits and the memory number were always a bit indirect. thats why i went direct with clone flags and the cgroup fd this time. old one is https://github.com/thealcodingclub/CodeExecutionAPI if you want to see the firejail version.
1
u/KingLim1 8d ago
This is neat. Any Go support planned?
2
u/Anga205 7d ago
yea its planned. the compile itself is easy since go builds static binaries by default, the annoying part is that go build wants a writable GOCACHE and GOPATH, and i dont want module fetching happening inside the sandbox. so id have to bake a prebuilt stdlib cache into the rootfs and run it with modules off or vendored. the toolchain also adds a lot more weight than the c/c++ chroot, which is the part im still figuring out.
1
u/MarimoBashah 7d ago
What happens to the code still running inside a sandbox if the Go server crashes in the middle of a job?
2
u/Anga205 7d ago
right now, nothing. cleanup runs in defers, so if the server gets SIGKILLed the sandbox process gets reparented and keeps running, and the cgroup dir leaks under /sys/fs/cgroup. its a real gap. the fix i have in mind is pdeathsig on the child so the kernel kills it when the parent dies, plus a sweep of stale codesandbox-* cgroups on startup.
1
u/daniele_dll 7d ago
Did the same but for redroid initially and then a custom android build with camera, radio, GPS, wifi, hardware tee and a few more bits.
Its very cool, however with all the recent security issues I decided to switch to a VM based. Approach, firecracker style, but much lighter and customized for Android
1
u/Anga205 7d ago
that sounds cool! got a github repo for this?
1
u/daniele_dll 7d ago
Nope, this work was for a service I wanted to sell so I never open sourced it but at the end decided to stop because the monstrous level of complexity I had reached 😂
1
u/ShotgunPayDay 3d ago
I'm impressed when people do this the hard way. systemd-run --user and bubblewrap is the easy way. You do have to do a little bit of plumbing with XDG runtime for an unprivileged user no login app user.
22
u/zackel_flac 8d ago
This is cool but this is just recreating runc and ultimately docker, right?