Class: Hermetic::Backends::Docker
- Defined in:
- lib/hermetic/backends/docker.rb
Overview
Hardened docker run — Silas's interim adapter ported onto the Request
contract and extended with what it lacked (files:, stdin:, clean env,
per-run limits). Container namespaces are weaker than a microVM and the
daemon this adapter shells to lives on the app host, so this is
trust :host — dev-only for untrusted code (spec §3).
The argv is a pure function of the Request — files: included — so the
whole wire form is unit-testable without Docker present; the runner is
injectable and a real run is a single docker run leg.
Direct Known Subclasses
Constant Summary
Constants inherited from Base
Base::OFF_HOST_TRUST, Base::TIMEOUT_EXIT
Instance Attribute Summary collapse
-
#image ⇒ Object
readonly
Returns the value of attribute image.
-
#workdir ⇒ Object
readonly
Returns the value of attribute workdir.
Attributes inherited from Base
Instance Method Summary collapse
-
#bootstrap_script(request) ⇒ Object
Pure: the files: delivery.
-
#container_argv(request, name:) ⇒ Object
Pure: Request -> locked-down
docker runargv. -
#initialize(image:, workdir: "/workspace", docker_bin: "docker", runner: nil, **base_opts) ⇒ Docker
constructor
A new instance of Docker.
Methods inherited from Base
#backend_name, #enabled?, #off_host?, #run
Methods included from SilasLedgerGuard
Constructor Details
#initialize(image:, workdir: "/workspace", docker_bin: "docker", runner: nil, **base_opts) ⇒ Docker
Returns a new instance of Docker.
18 19 20 21 22 23 24 25 26 |
# File 'lib/hermetic/backends/docker.rb', line 18 def initialize(image:, workdir: "/workspace", docker_bin: "docker", runner: nil, **base_opts) raise ConfigError, "image: is required" if image.to_s.strip.empty? super(trust: :host, **base_opts) @image = image @workdir = workdir @docker_bin = docker_bin @runner = runner || method(:shell_run) end |
Instance Attribute Details
#image ⇒ Object (readonly)
Returns the value of attribute image.
16 17 18 |
# File 'lib/hermetic/backends/docker.rb', line 16 def image @image end |
#workdir ⇒ Object (readonly)
Returns the value of attribute workdir.
16 17 18 |
# File 'lib/hermetic/backends/docker.rb', line 16 def workdir @workdir end |
Instance Method Details
#bootstrap_script(request) ⇒ Object
Pure: the files: delivery. docker cp can't reach a read-only rootfs
and a workdir tmpfs only exists once the container starts (the smoke
layer proved both), so files are written by the guest itself at exec
time: each one a base64 blob decoded into the workdir tmpfs, then
exec "$@" hands off to the real command. User data enters only as
base64 ([A-Za-z0-9+/=], inert in single quotes) and escaped paths;
the command stays positional-args — still no injection surface.
Needs /bin/sh and base64 in the image (any debian/alpine/busybox).
54 55 56 57 58 59 60 61 |
# File 'lib/hermetic/backends/docker.rb', line 54 def bootstrap_script(request) writes = request.files.map do |path, content| dir = File.dirname(path) write = "printf '%s' '#{[ content ].pack("m0")}' | base64 -d > #{Shellwords.escape(path)}" dir == "." ? write : "mkdir -p #{Shellwords.escape(dir)} && #{write}" end (writes + [ 'exec "$@"' ]).join(" && ") end |
#container_argv(request, name:) ⇒ Object
Pure: Request -> locked-down docker run argv. The command rides as
argv elements, never interpolated into a shell string — no injection
surface. Docker passes no host vars unless named in --env, so the
guest env is exactly Request#clean_env by construction. The rootfs is
read-only; the workdir is a tmpfs — the guest's only writable scratch,
which is also where the files: bootstrap lands.
34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/hermetic/backends/docker.rb', line 34 def container_argv(request, name:) [ @docker_bin, "run", "--rm", "--name", name, *runtime_flags, *stdin_flags(request), "--network", request.network.docker_flag, "--memory", request.limits.memory, "--cpus", request.limits.cpus, "--pids-limit", request.limits.pids.to_s, "--read-only", "--tmpfs", @workdir, "--workdir", @workdir, "--cap-drop", "ALL", "--security-opt", "no-new-privileges", *env_flags(request), @image, *guest_argv(request) ] end |