sandbox

package
v0.4.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 1, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

Documentation

Overview

Package sandbox is the Mode-3 setup-script jail (plan §7/§9) — the single most dangerous feature in Mooring, so it is built assuming the script IS hostile. It is OFF by default and FAIL-CLOSED: a script runs only inside a throwaway, unprivileged, no-docker.sock, no-network, read-only-rootfs, resource-capped jail whose live self-test must pass before EVERY run; ALL captured output is treated as hostile data. The jail backend is Linux-only (cgroup-freeze + userns isolation); on any other OS Available()/SelfTest()/Run() fail closed.

Index

Constants

View Source
const (
	TriggerNever            = "never"
	TriggerOnDemand         = "on_demand"
	TriggerOnFirstDeploy    = "on_first_deploy"
	TriggerBeforeEachDeploy = "before_each_deploy"
)

Trigger decides whether a setup STEP is included in an operator-initiated, confirm-token-gated deploy plan — it is a planner input, NEVER an executor, and never runs the jail from a webhook/auto-deploy/boot (plan §7).

Variables

View Source
var ErrUnavailable = errors.New("sandbox: unavailable on this host (fail-closed)")

ErrUnavailable means the host cannot provide a working sandbox (non-Linux, or a failed self-test). It is ALWAYS fail-closed: when in doubt, refuse to run.

Functions

func Available

func Available() (bool, string)

Available reports the sandbox can run here. The real posture is asserted by the per-run SelfTest; this only checks the docker CLI is present.

func ConfineCapturePath

func ConfineCapturePath(runDir, name string) (string, error)

ConfineCapturePath resolves a script-declared captured file name to an absolute destination under runDir, rejecting traversal/absolute/escaping paths. The caller still writes it O_NOFOLLOW + regular-file-only + size-capped + 0600.

func IsAutoTrigger

func IsAutoTrigger(t string) bool

autoTriggers are incompatible with git.auto_deploy (auto-deploy + auto-setup is a parse-time hard reject, plan §7).

func SelfTest

func SelfTest(ctx context.Context, c Config) error

SelfTest runs a probe with the SAME hardening as a real run and asserts (plan §7): empty effective capabilities, only the loopback NIC present (no egress / no reach to control ports under --network none), and a read-only rootfs. Any deviation — or a probe that won't run — is FAIL-CLOSED. (This is the §15 escape test as a runtime precondition.)

func ValidateCapturedEnvKey

func ValidateCapturedEnvKey(k string) error

ValidateCapturedEnvKey enforces the key shape on a value the SCRIPT emitted.

func ValidateCapturedEnvValue

func ValidateCapturedEnvValue(v string) error

ValidateCapturedEnvValue rejects a captured value containing newline/CR/NUL or a ${ sequence — captured values are stored OPAQUE and must never be re-expanded through compose interpolation (plan §7 red-team).

Types

type Config

type Config struct {
	Image  string // digest-pinned base image
	Binary string // docker CLI ("docker")
	Limits Limits
	UID    int // run the script as this (non-root) uid so captures are Mooring-owned
	GID    int
}

Config configures the throwaway-container jail backend.

type Limits

type Limits struct {
	WallClock   time.Duration
	CPUs        string
	MemoryMB    int
	PidsLimit   int
	ScratchMB   int
	OutputCapKB int
}

Limits are the jail resource caps (from the validated config.setup block).

type PlanResult

type PlanResult struct {
	Bytes    int
	Lines    int
	Findings []string // advisory notes (e.g. "references the network")
}

PlanResult is the STATIC analysis of a setup script (plan §7 setup/plan): a no-exec summary shown on the confirm screen so the operator sees what they are authorizing. It never runs anything; the findings are advisory (the jail is the real containment), surfacing intent the operator should eyeball.

func Plan

func Plan(script string) PlanResult

Plan statically analyzes a setup script. It performs NO execution.

type RunResult

type RunResult struct {
	ExitCode int
	Output   string // combined stdout+stderr, capped at OutputCapKB
	TimedOut bool
}

RunResult is the outcome of a jail execution.

func Run

func Run(ctx context.Context, c Config, ss ScriptSet, scratchDir string) (RunResult, error)

Run executes the script in the jail and returns the capped combined output. The caller MUST have run SelfTest immediately before (and hold the global one-docker-child semaphore).

type ScriptSet

type ScriptSet struct {
	Script    string   // the script bytes (run with /bin/sh in the jail)
	Trigger   string   // never | on_demand | on_first_deploy | before_each_deploy
	Produces  []string // declared outputs: "env:NAME" and/or "file:relpath"
	PinnedSHA string   // the repo/spec sha this is bound to ("" if none)
}

ScriptSet is one app's setup script plus the policy that pins its identity.

func (ScriptSet) Checksum

func (ss ScriptSet) Checksum(lim Limits) string

Checksum binds the FULL identity of a setup run (plan §7): script bytes + limits + produces + trigger + pinned-sha. The confirm token and the setup_runs idempotence key derive from this, so raising a cap, adding a capture, changing the trigger, or editing a byte all VOID a prior confirmation. Encoded unambiguously (length-prefixed) so no two distinct inputs can collide.

func (ScriptSet) Validate

func (ss ScriptSet) Validate(autoDeploy bool) error

Validate enforces the structural rules on a script set (plan §7). autoDeploy is the app's git.auto_deploy flag — auto-setup + auto-deploy is a hard reject.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL