Class: Ask::Sandbox::Docker

Inherits:
Base
  • Object
show all
Defined in:
lib/ask/sandbox/docker.rb

Overview

Executes commands in a Docker container with hardened security settings.

Uses the docker CLI binary directly (no gem dependencies). Requires a running Docker daemon.

Security measures:

  • Read-only root filesystem (--read-only)

  • All Linux capabilities dropped (+–cap-drop ALL+)

  • No privilege escalation (+–security-opt no-new-privileges+)

  • Network egress disabled by default (+–network none+)

  • PIDs limit (+–pids-limit 100+)

  • Memory and CPU limits

  • Container auto-removal (--rm)

Examples:

sandbox = Ask::Sandbox::Docker.new(image: "ruby:3.4-alpine")
sandbox.call(["ruby", "-e", "puts 1+1"])

Instance Method Summary collapse

Constructor Details

#initialize(image: "ruby:3.4-alpine", memory: "512m", cpus: 1.0, network: false, read_only: true, cap_drop: "ALL", user: nil, timeout: 30, remove: true) ⇒ Docker

Returns a new instance of Docker.

Parameters:

  • image (String) (defaults to: "ruby:3.4-alpine")

    Docker image to use (default: “ruby:3.4-alpine”)

  • memory (String, nil) (defaults to: "512m")

    memory limit (e.g. “512m”, “1g”)

  • cpus (Float, nil) (defaults to: 1.0)

    CPU limit (e.g. 1.0)

  • network (Boolean) (defaults to: false)

    allow network egress (default: false)

  • read_only (Boolean) (defaults to: true)

    read-only root filesystem (default: true)

  • cap_drop (String, nil) (defaults to: "ALL")

    capabilities to drop (default: “ALL”)

  • user (String, nil) (defaults to: nil)

    run as specific user (default: nil = container default)

  • timeout (Integer) (defaults to: 30)

    default timeout in seconds

  • remove (Boolean) (defaults to: true)

    auto-remove container after execution (default: true)



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/ask/sandbox/docker.rb', line 33

def initialize(
  image: "ruby:3.4-alpine",
  memory: "512m",
  cpus: 1.0,
  network: false,
  read_only: true,
  cap_drop: "ALL",
  user: nil,
  timeout: 30,
  remove: true
)
  @image = image
  @memory = memory
  @cpus = cpus
  @network = network
  @read_only = read_only
  @cap_drop = cap_drop
  @user = user
  @default_timeout = timeout
  @remove = remove
end

Instance Method Details

#call(command, timeout: @default_timeout, workdir: nil, env: {}, stdin: nil) ⇒ Ask::Sandbox::Result

Execute a command in the sandbox.

Parameters:

  • command (String, Array<String>)

    When a String, executed via a shell (bash -c). When an Array, executed directly with no shell interpretation.

  • timeout (Integer) (defaults to: @default_timeout)

    max execution time in seconds (default: 30)

  • workdir (String, nil) (defaults to: nil)

    working directory inside the sandbox

  • env (Hash{String => String}) (defaults to: {})

    extra environment variables

  • stdin (String, nil) (defaults to: nil)

    data to pipe to the command’s stdin

Returns:

Raises:

  • (ArgumentError)


56
57
58
59
60
61
62
63
64
# File 'lib/ask/sandbox/docker.rb', line 56

def call(command, timeout: @default_timeout, workdir: nil, env: {}, stdin: nil)
  raise ArgumentError, "command must not be nil" if command.nil?
  raise ArgumentError, "command must not be empty" if command.respond_to?(:empty?) && command.empty?

  check_docker_available!

  docker_argv = build_docker_argv(command, env, workdir)
  run_docker(docker_argv, timeout, stdin)
end