[codex] Add OCI sandbox execution backend - #155
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5b7b197ea1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| "--security-opt", "no-new-privileges", | ||
| "--tmpfs", "/tmp:rw,nosuid,nodev,size=64m", | ||
| "--workdir", containerWorkDir, | ||
| "--mount", fmt.Sprintf("type=bind,source=%s,target=%s", hostWorkdir, containerWorkDir), |
There was a problem hiding this comment.
Bound writes to the sandbox workspace
When the Docker backend is enabled, this bind mount is writable by default, and Docker's --read-only only protects the container root filesystem while allowing writes to mounted volumes/binds. A submitted program can therefore write unbounded data under /work (for example yes > bigfile) and consume the host filesystem despite the memory, pid, timeout, and /tmp limits; the legacy path applied file-size limits, but this sandbox path has no equivalent quota for the host workspace.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Review this again
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bb5753e2b5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } | ||
|
|
||
| if runprops.PreRunProps != nil { | ||
| preRunProps = runPropsWithRuntimeIdentity(preRunProps, agent) |
There was a problem hiding this comment.
Keep compiler UID aligned with runner workdirs
In the default server Docker image using the legacy backend, this now runs compile steps as the controller's agent UID (1, 2, ...), but docker/server-debian/entrypoint.sh creates /tmp/runnerN owned by UIDs starting at 1001. For compiled languages, g++/go build/rustc therefore run as a user that can read the source file but cannot create a.out or run in the 0755 workdir, so C++/Go/Rust requests through make dkr-server fail before execution; interpreters are unaffected because they do not write build artifacts.
Useful? React with 👍 / 👎.
| return CodeRunner{ | ||
| sandboxRunner: config.Runner, | ||
| sandboxPolicy: config.Policy, | ||
| sandboxImages: config.Images, | ||
| } |
There was a problem hiding this comment.
Cap Docker backend concurrency
When CODECANVAS_EXECUTION_BACKEND=docker is enabled, this sandbox runner bypasses the legacy NUM_RUNNERS/NewAsyncController reservation path and has no replacement semaphore. Every simultaneous /api/v1/run request therefore starts a docker run immediately, so a request flood can create unbounded containers and exhaust aggregate host CPU, memory, or Docker capacity despite each individual container having limits.
Useful? React with 👍 / 👎.
| } | ||
| steps = append(steps, sandbox.Command{Args: plan.runCmd}) | ||
|
|
||
| out, err := cr.sandboxRunner.Run(context.Background(), sandbox.Job{ |
There was a problem hiding this comment.
Propagate request cancellation into sandbox runs
When the Docker backend is configured with CODECANVAS_SANDBOX_TIMEOUT above the server's 10-second middleware timeout, this context.Background() prevents HTTP cancellation from reaching the sandbox. Timed-out or disconnected requests can keep the handler and Docker container running until the sandbox timeout instead of stopping when the request context is canceled, tying up Docker resources after the client is gone.
Useful? React with 👍 / 👎.
| if len(image) == 0 { | ||
| continue | ||
| } | ||
| if output, err := exec.CommandContext(ctx, docker, "image", "inspect", image).CombinedOutput(); err != nil { |
There was a problem hiding this comment.
Honor pull policy during preflight
When CODECANVAS_SANDBOX_PULL is set to missing or always, Docker's --pull policy is supposed to pull absent or refreshed images during docker run, but the default startup preflight still fails first unless every image is already in the local cache. Operators following the documented pull-policy setting on a fresh host therefore get a startup panic instead of an image pull, making those new policy values unusable unless they also disable preflight.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 41e1d1da03
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| "--label", "codecanvas.sandbox=true", | ||
| "--pull", policy.PullPolicy, |
There was a problem hiding this comment.
Disable Docker logging for sandbox output floods
When the Docker backend runs an output-heavy snippet such as yes, cappedBuffer only truncates the API response while continuing to drain the Docker CLI stream, and this docker run leaves the daemon's logging driver at its default. Docker documents --log-driver for docker run and explicitly notes --log-driver=none disables container logging, so without that or a bounded max-size log option, a request can still force the host Docker daemon to write unbounded stdout/stderr logs during the sandbox timeout despite CODECANVAS_SANDBOX_OUTPUT_BYTES.
Useful? React with 👍 / 👎.
| DefaultContainerWorkDir = "/work" | ||
| DefaultTimeoutSec = 2 | ||
| DefaultCPUs = "1" | ||
| DefaultMemory = "128m" |
There was a problem hiding this comment.
Increase the Docker timeout for cold Go builds
With the Docker backend's default policy, each Go request starts a fresh container and sends GOCACHE=/tmp/go-cache, so common submissions such as a fmt.Println program have to populate the build cache inside that container before running. This 2-second wall timeout covers the go build run.go compile step and execution together, so default Docker deployments can time out supported Go submissions unless operators know to raise CODECANVAS_SANDBOX_TIMEOUT.
Useful? React with 👍 / 👎.
Summary
Adds a Docker/OCI sandbox backend for code execution behind
CODECANVAS_EXECUTION_BACKEND=dockerwhile keeping the legacy process backend available by default.Key changes:
engine/sandboxandengine/sandbox/dockerwith per-request disposable container execution.no-new-privileges, cgroup CPU/memory/pids limits, nofile ulimit, private IPC, init process, and bounded stdout/stderr capture.CODECANVAS_SANDBOX_RUNTIME=runsc.docker/sandbox/Dockerfileplusmake dkr-build-sandbox.docs/oci-sandbox-runtime.md.cmd.Dirinstead of process-widechdir.Validation
go test ./...docker build -t codecanvas-sandbox:local -f docker/sandbox/Dockerfile .CODECANVAS_DOCKER_INTEGRATION=1 CODECANVAS_SANDBOX_IMAGE=codecanvas-sandbox:local go test ./engine/sandbox/docker ./engine/coderunner/v2 -run Integration -count=1 -vThe gated Docker tests prove sandbox execution, CodeRunner integration, startup preflight, non-root UID, and blocked outbound network.
Notes
The host currently has Docker runtimes
runc,io.containerd.runc.v2, andnvidia;runscis not registered yet. This PR supports and preflightsCODECANVAS_SANDBOX_RUNTIME=runsc, but enabling gVisor still requires installing/registeringrunscon the host.