Help us shape the future of Go! Take the 2026 Go Developer Survey

sandbox

package
v0.4.45 Latest Latest
Warning

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

Go to latest
Published: Nov 3, 2026 License: MIT Imports: 14 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