sandbox

package
v0.3.50 Latest Latest
Warning

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

Go to latest
Published: Oct 3, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package sandbox provides sandbox/bwrap: the single source of truth for the tenant sandbox view. The same view config drives two enforcement mechanisms:

  • WrapArgv: subprocess isolation — translates the view into a bwrap argv prefix around bash / ACP subprocesses (each call runs in its own mount namespace, keeping 127.0.0.1 / the Pod network stack);
  • CheckRead / CheckWrite: in-process enforcement — for the filesystem/sandbox decorator and other in-process tools that bwrap cannot confine.

View: tmpfs masks the tenant-root parent (neighbor tenants invisible) → bind back this tenant root (rw); global root read-only with secretFiles masked; minimal system ro-binds; no --unshare-net.

Index

Constants

View Source
const (
	ModeOff   = "off"   // no wrapping, no checks — equivalent to no sandbox
	ModeAuto  = "auto"  // enable when bwrap is usable, warn and degrade otherwise (local dev only)
	ModeBwrap = "bwrap" // force enable; missing deps fail closed per FailIfUnavailable (default)
)

Sandbox modes.

View Source
const (
	MapModeRO = "ro" // default: read-only mapping
	MapModeRW = "rw" // read-write mapping (tenant can modify the host source)
)

Map modes for MapConfig.Mode.

Variables

This section is empty.

Functions

func New

func New(cfg Config, d Deps) (capsandbox.Service, error)

New constructs sandbox/bwrap. With mode=bwrap and unavailable deps (bwrap not in PATH or unprivileged user namespaces disabled) it fails closed per failIfUnavailable (default true).

Types

type Config

type Config struct {
	Mode              string `json:"mode"`
	FailIfUnavailable *bool  `json:"failIfUnavailable"`
	// Env marks the deployment environment: "prod"/"production" upgrades
	// mode=auto to fail-closed bwrap, so a misconfig cannot silently run
	// unsandboxed in production. Explicit config, no implicit env detection.
	Env string `json:"env"`
	// RoBinds are extra read-only binds (public read-only paths shared by all
	// tenants; source and target are the same path). Host absolute paths or
	// workspace refs (e.g. "global:share_dir", resolved per call per tenant).
	RoBinds []string `json:"roBinds"`
	// RwBinds are extra read-write binds (public writable paths shared by all
	// tenants; beware concurrent write conflicts). Host absolute paths or
	// workspace refs (missing dirs are created).
	RwBinds []string `json:"rwBinds"`
	// HidePaths are masked: directories become invisible and read-only,
	// files are masked empty. Host absolute paths or workspace refs. Applied
	// after all binds, so they can mask sensitive subpaths of system binds or
	// the global root. Resolution fails closed (a failed mask means a leak).
	HidePaths []string `json:"hidePaths"`
	// SecretFiles are sensitive files under the global root masked empty
	// (relative to the global root), default ["secrets.enc.json"].
	// Applied after the global ro-bind and ro/rwBinds.
	SecretFiles []string `json:"secretFiles"`
	// HomeRef selects the in-sandbox HOME. Default "." (the writable tenant
	// root, i.e. workspace "."). Values: a workspace ref ("global:dir",
	// "local:path"), a host absolute path, or the literal "host" (pass the
	// host user's home through, ro-bound — intended for single-tenant trusted
	// setups). The resolved home must live inside the view (tenant root,
	// global root or a configured bind); otherwise rendering fails closed
	// instead of pointing HOME at an unmounted void.
	HomeRef string `json:"homeRef"`
	// Maps bind a host source onto a different in-sandbox destination, e.g.
	// share the host's ~/.ssh or ~/.gitconfig into the tenant HOME. Applied
	// after ro/rwBinds and before secretFiles/hidePaths (masks always win).
	Maps []MapConfig `json:"maps"`
	// SystemBinds overrides the default minimal system read-only bind list;
	// empty uses the built-in default (/usr /bin /lib /lib64 /opt plus
	// DNS/cert files under /etc).
	SystemBinds []string `json:"systemBinds"`
	// TmpBase is the base dir on a host tmpfs (default /dev/shm/shellbwrap):
	// each tenant gets a subdir bound rw as in-sandbox /tmp. /dev/shm has its
	// own size cap (usually half of RAM), preventing tmpfs writes from eating
	// node memory. The explicit value "tmpfs" falls back to bwrap's own
	// --tmpfs /tmp (no size limit, not recommended). Must be exclusive per
	// runner instance on a shared host: startup removes leftover contents.
	TmpBase string `json:"tmpBase"`
}

Config configures sandbox/bwrap: the tenant view fields (roBinds/rwBinds/ hidePaths/secretFiles — shared by subprocess wrapping and in-process enforcement) plus the bwrap-mechanism knobs (mode/systemBinds/tmpBase/ probe-failure policy).

func (*Config) SetDefaults

func (c *Config) SetDefaults()

SetDefaults implements pluginkit.Defaulter.

func (*Config) Validate

func (c *Config) Validate() error

Validate implements pluginkit.Validator.

type Deps

type Deps struct {
	Workspace workspace.Service `json:"workspace"`
}

Deps for sandbox/bwrap.

type MapConfig added in v0.3.49

type MapConfig struct {
	// Src is the host source: an absolute path or a workspace ref
	// ("global:...", "local:..."), resolved per call per tenant.
	Src string `json:"src"`
	// Dst is the in-sandbox destination: an absolute path, or a relative path
	// resolved against the rendered HOME (e.g. ".ssh" → $HOME/.ssh), so the
	// mapping follows homeRef. A relative dst must not escape HOME ("..").
	Dst string `json:"dst"`
	// Mode is "ro" (default) or "rw". rw hands the host source's writable
	// surface to the tenant — a deliberate choice (e.g. never rw-map .ssh).
	Mode string `json:"mode"`
}

MapConfig binds a host source onto a different in-sandbox destination.

type Sandbox

type Sandbox struct {
	// contains filtered or unexported fields
}

Sandbox is the tenant sandbox view; tenant identity is resolved from ctx on every call.

func (*Sandbox) CheckRead

func (s *Sandbox) CheckRead(ctx context.Context, path string) error

CheckRead decides per the current tenant view whether an in-process read is allowed (used by the filesystem/sandbox decorator and other in-process tools — bwrap cannot confine Go code). path is a host absolute path. When the sandbox is disabled everything is allowed (confinement falls back to the tool layer, e.g. filesystem/local root).

The read model mirrors the bwrap view's allowlist: inside the sandbox only {tenantRoot, globalRoot, roBinds, rwBinds, systemBinds} exist and everything else is invisible; accordingly an in-process read is allowed only under {tenantRoot, globalRoot, roBinds, rwBinds} (system binds are a subprocess mechanism detail — fs tools have no legitimate need to read /usr; add an explicit roBind if one ever does). Deny rules:

  • masked paths: matching hidePaths (incl. subpaths) or secretFiles;
  • everything outside the allowlist (incl. neighbor tenants and any unrelated host path).

func (*Sandbox) CheckWrite

func (s *Sandbox) CheckWrite(ctx context.Context, path string) error

CheckWrite decides per the current tenant view whether an in-process write is allowed. path is a host absolute path. Aligned with the bwrap view's read-only floor: only the tenant root and rwBinds are writable; everything else (global root, roBinds, system paths, unbound paths) is denied — the in-process equivalent of EROFS inside the sandbox.

func (*Sandbox) Enabled

func (s *Sandbox) Enabled() bool

Enabled reports whether the sandbox is active (false in off mode or after an auto-mode probe fallback). When false, WrapArgv passes through and CheckRead/CheckWrite allow everything (confinement falls back to the tool layer).

func (*Sandbox) TranslatePath added in v0.3.49

func (s *Sandbox) TranslatePath(ctx context.Context, path string) (string, bool)

TranslatePath implements capsandbox.PathTranslator: a path under a map dst is rewritten to its host src (longest dst prefix wins), so in-process consumers read/write the same backing content the subprocess sees at dst. The dst itself may not exist on the host — without this translation the in-process view would be an empty shell compared to the bwrap view.

func (*Sandbox) WrapArgv

func (s *Sandbox) WrapArgv(ctx context.Context, workDir string, inner []string) ([]string, error)

WrapArgv renders bwrap args for the current tenant and wraps the inner command (e.g. ["bash","-lc",cmd] or an ACP agent command). When the sandbox is disabled, inner is returned unchanged.

View: tmpfs masks the tenant-root parent (neighbor tenants invisible) → bind back this tenant root (rw); global root read-only with secretFiles masked; minimal system ro-binds; no --unshare-net (127.0.0.1 / Pod network stack preserved).

Jump to

Keyboard shortcuts

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