Codebox is a container designed to run untrusted code in a secure way, from a restricted virtualized environment.
Important
Codebox is not supposed to be exposed directly to the internet/user because it does not have runtime constraints nor authentication, authorization, and caching mechanisms. Instead, you must use an intermediary layer to provide those.
Code Lab is an example project that uses Codebox through an intermediary layer to handle caching and runtime constraints.
Codebox uses NsJail to create a jail environment to run safely an untrusted piece of code. The jail environment establishes several constraints:
- Time limit
- Memory limit
- Process count limit
- No networking
- Restricted, read-only filesystem
Some of those exact limits can be configured in app/config.py and app/nsjail.cfg.
┌───────────────────┐ │ │ source files │ │ ───────────────►│ Codebox ├─────────────► commands │ │ responses │ │ └───────────────────┘
source files
is a dictionary where keys are file paths, and values are their respective file contents.commands
is a list of commands, each one containingcommand
,timeout
andstdin
fields.responses
is a list of responses, each one corresponding to a command and containingstdout
,stderr
andexit_code
fields.
Note
The exact type interface is declared in app/models.py.
- The source files are available in a directory named
/sandbox
- Each command from the list runs in the jail environemnt,
with
/sandbox
as the current working directory. - The response for each command is appended to a list
- The responses are returned as the result
A simplified Python code version of the executing process:
def run_project(sources: Sourcefiles, commands: list[Command]) -> list[Response]:
save_sources('/sandbox', sources)
responses = []
for command in commands:
resp = nsjail.execute(command)
responses.append(resp)
return responses
Build the image:
$ docker build -t codebox https://github.com/andredias/codebox.git#main
Run the container:
$ docker run -it --rm --privileged -p 8000:8000 codebox
Clone/download the project
Build the image:
$ make build
Run the container:
$ make run
You can execute the examples accessing the API through http://localhost:8000/execute
or via the interactive documentation at http://localhost:8000/docs
.
Using http://localhost:8000/docs
:
{
"sources": {
"hello.py": "print('Hello World!')"
},
"commands": [
{
"command": "/venv/bin/python hello.py"
}
]
}
Using httpie:
$ http :8000/execute sources['hello.py']="print('Hello World')" \
commands[]['command']='/venv/bin/python hello.py'
Using http://localhost:8000/docs
:
{
"sources": {
"app/__init__.py": "",
"app/main.py": "import sys\n\nfor line in sys.stdin.readlines():\n sys.stdout.write(line)\n"
},
"commands": [
{
"command": "/venv/bin/python app/main.py", "timeout": 0.1, "stdin": "1\n2\n3"
}
]
}
Using httpie
:
$ http :8000/execute <<< '{
"sources": {
"app/__init__.py": "",
"app/main.py": "import sys\n\nfor line in sys.stdin.readlines():\n sys.stdout.write(line)\n"
},
"commands": [
{
"command": "/venv/bin/python app/main.py", "timeout": 0.1, "stdin": "1\n2\n3"
}
]
}'
{
"sources": {},
"commands": [
{
"command": "/bin/sleep 1", "timeout": 0.1
}
]
}
$ http :8000/execute <<< '{
"sources": {},
"commands": [
{
"command": "/bin/sleep 1",
"timeout": 0.1
}
]
}'
{
"sources": {},
"commands": [
{
"command": "/venv/bin/pip freeze", "timeout": 1.0
}
]
}
{
"sources": {
"code.rs": "fn main() {\n println!(\"Hello World!\");\n}"
},
"commands": [
{
"command": "/usr/local/cargo/bin/rustc code.rs", "timeout": 0.5
},
{
"command":"./code", "timeout": 0.1
}
]
}
{
"sources": {
"main.py": "def double(x: int) -> int:\n return 2 * x\n",
"test_main.py": "from main import double\n\ndef test_double() -> None:\n assert double(2) == 4\n"
},
"commands": [
{
"command": "/venv/bin/pytest", "timeout": 1.0
}
]
}
$ http :8000/execute <<< '{
"sources": {
"main.py": "def double(x: int) -> int:\n return 2 * x\n",
"test_main.py": "from main import double\n\ndef test_double() -> None:\n assert double(2) == 4\n"
},
"commands": [
{
"command": "/venv/bin/pytest", "timeout": 1.0
}
]
}'