reberhardt7 / cplayground

GNU General Public License v3.0
115 stars 14 forks source link

Add wrapper program to apply seccomp to user code #47

Open glen3b opened 3 years ago

glen3b commented 3 years ago

Fixes #41 by creating a "sandbox" executable which is deployed to the Docker image for running user code, and using it as exec-wrapper in GDB (or calling it directly as a wrapper when not debugging).

Currently it bans syscalls (only ptrace at the moment) by returning ERFKILL - I'm definitely open to alternatives here (EPERM comes to mind); I primarily picked ERFKILL so it's obviously weird when debugging the sandbox feature as opposed to ambiguous with a potentially "real" EPERM. (Alternatively we can just kill the child.)

Modifies the Dockerfile to use a multi-stage build: uses the gcc:10.2.0 image to compile the sandbox program, then takes the compiled executable and copies it into the final cplayground Docker image. This lets us avoid installing any of the compilation garbage in the target image, although since we're installing build-essential anyway this may be less relevant. However, from what I can tell, this is the "right" way to make a Docker image for a C program. If we want to change the CFLAGS or whatnot used for the build of the sandbox, that will entail a Dockerfile tweak.

The sandbox program itself is a fairly straightforward seccomp BPF program which blocklists ptrace and allows all other syscalls. Seccomp policies can be stacked, so we won't be overriding Docker's policy or anything. I haven't thoroughly examined this for security, but I imagine as long as the user code cannot coerce any of its parent processes to run code not affected by this policy, we should be fine. If we knew more about user programs, it might make more sense to only list allowed calls and block the rest, but considering we accept essentially arbitrary user programs, this is probably out of scope for this PR. If the sandbox program fails to apply its policy and exec the child, it returns status 100 +/- 1.

My biggest gripe at the moment is probably that the responsibility for ensuring the sandbox actually gets installed is split between run.py and container.ts. I'm also not 100% happy with the ERFKILL return status, but I'm not sure what the right thing to do there is. Opening this PR for discussion and review.