Class: Silas::Sandbox::Docker
- Inherits:
-
Object
- Object
- Silas::Sandbox::Docker
- Defined in:
- lib/silas/sandbox/docker.rb
Overview
Interim adapter: shells to an ephemeral, locked-down docker run container
(no network, read-only fs, dropped caps, no-new-privileges, memory/cpu/pid
limits, host-side timeout). Weaker than a microVM — flag honestly. The argv
is a pure function (unit-tested); the exec runner is injectable so the
command building is testable without Docker present.
Constant Summary collapse
- TIMEOUT_EXIT =
coreutils convention
124
Instance Method Summary collapse
-
#docker_argv(command, name:) ⇒ Object
Pure: command is a single argv element to
sh -c, never interpolated into a shell string — no injection surface. - #enabled? ⇒ Boolean
-
#initialize(image:, network: "none", memory: "512m", cpus: "1", pids: 256, workdir: "/workspace", docker_bin: "docker", runner: nil) ⇒ Docker
constructor
A new instance of Docker.
- #run(command, files: {}, timeout: 30) ⇒ Object
Constructor Details
#initialize(image:, network: "none", memory: "512m", cpus: "1", pids: 256, workdir: "/workspace", docker_bin: "docker", runner: nil) ⇒ Docker
Returns a new instance of Docker.
14 15 16 17 18 19 20 21 22 23 24 25 26 |
# File 'lib/silas/sandbox/docker.rb', line 14 def initialize(image:, network: "none", memory: "512m", cpus: "1", pids: 256, workdir: "/workspace", docker_bin: "docker", runner: nil) raise SandboxError, "config.sandbox_image is required for :docker" if image.to_s.strip.empty? @image = image @network = network @memory = memory @cpus = cpus @pids = pids @workdir = workdir @docker_bin = docker_bin @runner = runner || method(:shell_capture) end |
Instance Method Details
#docker_argv(command, name:) ⇒ Object
Pure: command is a single argv element to sh -c, never interpolated into
a shell string — no injection surface.
43 44 45 46 47 48 49 50 |
# File 'lib/silas/sandbox/docker.rb', line 43 def docker_argv(command, name:) [ @docker_bin, "run", "--rm", "--name", name, "--network", @network, "--memory", @memory, "--cpus", @cpus.to_s, "--pids-limit", @pids.to_s, "--read-only", "--tmpfs", @workdir, "--workdir", @workdir, "--cap-drop", "ALL", "--security-opt", "no-new-privileges", @image, "/bin/sh", "-c", command ] end |
#enabled? ⇒ Boolean
28 |
# File 'lib/silas/sandbox/docker.rb', line 28 def enabled? = true |
#run(command, files: {}, timeout: 30) ⇒ Object
30 31 32 33 34 35 36 37 38 39 |
# File 'lib/silas/sandbox/docker.rb', line 30 def run(command, files: {}, timeout: 30) if Silas::Ledger.in_transaction? raise SandboxError, "sandbox exec inside a ledger transaction — sandbox-backed tools must be at_most_once!, never transactional!" end raise SandboxError, "files: is not supported in v1 (deferred)" unless files.empty? name = "silas-sbx-#{SecureRandom.hex(6)}" out, err, status = @runner.call(docker_argv(command, name: name), timeout: timeout, name: name) Result.new(stdout: out, stderr: err, exit_status: status) end |