Skip to content

Latest commit

 

History

History
165 lines (127 loc) · 5.08 KB

File metadata and controls

165 lines (127 loc) · 5.08 KB

Codize Sandbox

A sandboxed code execution engine.

GitHub Release GitHub Actions Workflow Status Go Report Card GitHub License

Codize Sandbox is a code execution engine that runs arbitrary code safely inside Linux namespace jails (google/nsjail). It exposes an HTTP API to receive code, execute it in an isolated environment, and return the output.

Supported Runtimes

Runtime Identifier
Node.js node
Ruby ruby
Go go
Bash bash

Usage

The container must run in privileged mode (required for nsjail to create Linux namespaces) with --cgroupns=host (required for nsjail to manage cgroups for resource limiting).

Docker

$ docker run \
    --privileged \
    --cgroupns=host \
    -p 8080:8080 \
    ghcr.io/codize-dev/sandbox:latest serve

Behavior can be customized via CLI flags (see CLI Flags for the full list):

$ docker run \
    --privileged \
    --cgroupns=host \
    -p 8080:8080 \
    ghcr.io/codize-dev/sandbox:latest serve --run-timeout 10 --compile-timeout 10

Docker Compose

Create a compose.yml:

services:
  sandbox:
    image: ghcr.io/codize-dev/sandbox:latest
    privileged: true
    cgroup: host
    command: ["serve", "--run-timeout", "10", "--compile-timeout", "10"]
    ports:
      - "8080:8080"
$ docker compose up

CLI Flags

Flag Default Description
--port 8080 (overridden by PORT env var) Listen port
--run-timeout 30 Run timeout in seconds
--compile-timeout 30 Compile timeout in seconds
--output-limit 1048576 (1 MiB) Maximum combined output bytes
--max-files 10 Maximum number of files per request
--max-file-size 262144 (256 KiB) Maximum file size in bytes
--max-body-size 5242880 (5 MiB) Maximum request body size in bytes

API

POST /v1/run

Request:

{
  "runtime": "node",
  "files": [
    {
      "name": "index.js",
      "content": "Y29uc29sZS5sb2coIkhlbGxvLCBXb3JsZCEiKQ=="
    }
  ]
}
  • runtime (required): one of "node", "ruby", "go", "bash"
  • files (required): array of source files. content is Base64-encoded. The first file in the array is used as the entrypoint

Response:

{
  "compile": null,
  "run": {
    "stdout": "SGVsbG8sIFdvcmxkIQo=",
    "stderr": "",
    "output": "SGVsbG8sIFdvcmxkIQo=",
    "exit_code": 0,
    "status": "OK",
    "signal": null
  }
}
  • compile: compilation result (same schema as run). null for interpreted runtimes (node, ruby, bash). When compilation fails, run is null
  • run: execution result. null when compilation fails
    • stdout / stderr / output: Base64-encoded output. output is the interleaved combination of stdout and stderr
    • exit_code: process exit code
    • status: one of "OK", "SIGNAL", "TIMEOUT", "OUTPUT_LIMIT_EXCEEDED"
    • signal: signal name if the process was killed by a signal (e.g. "SIGKILL"), null otherwise

How It Works

Architecture

POST /v1/run
  → Echo HTTP server (request validation, Base64 decoding, write files to tmpdir)
    → nsjail (execute code in a namespace-isolated environment)
      → Return response

Sandbox Isolation

Code is isolated by google/nsjail with multiple layers of defense:

  • Linux namespaces: PID, network, mount, UTS, IPC, and cgroup namespaces are all isolated. External network access is completely blocked, and loopback communication is also disabled.
  • UID/GID mapping: Sandboxed processes run as nobody (65534). Only a single UID is mapped, making setuid impossible.
  • Filesystem restrictions: Only the minimum required paths are mounted (shared libraries, device files, user code directory). Everything except the user code directory is read-only. /tmp is a 64 MiB tmpfs mounted with noexec.
  • Resource limits: Execution time is enforced by nsjail's --time_limit and --rlimit_cpu. Cgroups limit PID count, memory, and CPU usage. Rlimits constrain stack size and other per-process resources.
  • Seccomp-BPF: Dangerous syscalls (io_uring, bpf, mount, ptrace, unshare, etc.) are blocked at the kernel level. Clone calls with namespace creation flags are also blocked.
  • Output limits: The process is killed if the combined stdout and stderr exceeds the configured limit.

License

MIT