Gravelbox is a containerized execution platform with a REST API, it's main functions is to run unsafe source code (or anything, really) with granular controls such as timeouts and resource access.
- Install Docker
git pull github.com/nokusutwo/gravelboxdocker build atom\go run .
There's an included gravel.ini with rudimentary configuration options.
[docker]
# the docker command to use
command=docker
# Global docker execution timeout for all docker commands
# (this includes when sending a /atom/execute) request
timeout=120s
[atom]
# Path to the atom folder
path=atom
[gravelbox]
# Mount point to where the files are saved prior to execution
mountdir=_mountSee examples below.
- GET
/api/versionto get the docker version. - GET
/api/atoms/create/:nameto create a new atom.- The first atom usually takes forever to create
- GET
/api/atoms/delete/:nameto remove atom. - GET
/api/atoms/listto list current atoms. - POST
/api/atoms/executeto execute a thing.
{
"binaries": [
{
"name": "exec.sh",
"data": "....",
"resolve": true,
"decode_b64": true
},
{
"name": "test.cs", "data": "..."
}
],
"command": ["sh", "exec.sh"],
"atom": "mono",
"timeout": "20s",
"network": false,
"read_only": true
}Items with * are required.
- *
binaries: the array of files to send to the sandbox- *
name: name of the file - *
data: file contents, (could be JSON, string or base64 string) resolve: change replace all instances of{path}in the datadecode_b64: treat the data as a base64 string and decode before saving the binarydecode_json: treat the data as a JSON object and marshal before saving the binary.
- *
- *
command: command to run inside of the atom - *
atom: name of the atom - *
timeout: container timeout network: enable/disable network access (default: false)read_only: enable disable writing to the filesystem (write access is required especially in compiled programs) (default: false)
Before the container is started, the binaries are saved in a unique folder specified in gravelbox.mountdir in gravel.ini.
The folder name is random so gravelbox automatically replaces the instances of {path} prior to execution in $.command as well in
the binaries using the resolve flag.
"command": ["sh", "{path}/exec.sh"] is automatically resolved to sh /mnt/12345ABCDE/exec.sh.
Atoms now have a built in utility named executor which facilitates fine grained program execution within the container.
Executor works by following a .execute file. The working path is also changed to where the .execute file lives.
Sample .execute file
{
"commands": [{
"command": "mcs",
"args": ["-out:test.exe", "test.cs"]
},{
"command": "mono",
"args": ["test.exe"],
"timeout": "100ms"
}]
}- *
commands: an array of commands to execute- *
command: the main executable args: an array of argumentstimeout: a gotime.Durationstring
- *
no_parse: Disables the attempt to parse each execution as an array. One output string per command. (default:false)export_json: Export the parsed output to JSON. (default:false)
This example file builds test.cs and then runs it with a maximum execution time of 100ms.
This example uses the executor utility in order to build the C# source code first.
The binary includes two files namely .execute which is a JSON file and test.cs which is a base64 encoded source code.
The atom execution is limited to run for 20s in which the rest endpoint returns with an error if it exceeds past that.
POST http://localhost:12375/api/atoms/execute
{
"binaries": [
{
"name": ".execute",
"data": {
"commands": [{
"command": "mcs",
"args": ["-out:test.exe", "test.cs"]
},{
"command": "mono",
"args": ["test.exe"],
"timeout": "100ms"
}]
},
"decode_json": true
},
{
"name": "test.cs", "data": "dXNpbmcgU3lzdGVtOwoKcHVibGljIGNsYXNzIEhlbGxvV29ybGQKewogICAgcHVibGljIHN0YXRpYyB2b2lkIE1haW4oc3RyaW5nW10gYXJncykKICAgIHsKICAgICAgICBDb25zb2xlLldyaXRlTGluZSAoIkhlbGxvIE1vbm8gV29ybGQiKTsKICAgIH0KfQ==",
"decode_b64": true
}
],
"command": ["executor", ".execute"],
"atom": "v2",
"timeout": "20s"
}Output
{
"data": {
"output": "Hello Mono World",
"runtime": "bt1b67d6t3iklc53046g"
},
"error": null
}This example just executes the JS script.
POST http://localhost:12375/api/atoms/execute
{
"binaries": [
{
"name": "test.js", "data": "console.log('Hello universe')"
}
],
"command": ["node", "test.js"],
"atom": "v2",
"timeout": "1s"
}Output
{
"data": {
"output": "Hello universe",
"runtime": "bt1bbu56t3iklc53047g"
},
"error": null
}