Documentation
¶
Overview ¶
Package os provides OS-level kernel sandbox primitives for tool execution. On macOS, it uses Seatbelt (sandbox-exec). Linux isolation is planned but not yet enforced.
Index ¶
- Variables
- func CleanupProfileFile(_ *exec.Cmd)
- func GenerateSeatbeltProfile(policy Policy) (string, error)
- func SelectBackend(mode BackendMode, candidates []BackendCandidate) (OSIsolator, BackendInfo)
- type BackendCandidate
- type BackendInfo
- type BackendMode
- type BwrapIsolator
- func (b *BwrapIsolator) Apply(_ context.Context, cmd *exec.Cmd, policy Policy) error
- func (b *BwrapIsolator) Available() bool
- func (b *BwrapIsolator) Name() string
- func (b *BwrapIsolator) NetworkIsolationAvailable() bool
- func (b *BwrapIsolator) NetworkIsolationReason() string
- func (b *BwrapIsolator) Reason() string
- func (b *BwrapIsolator) Version() string
- type FilesystemPolicy
- type NetworkPolicy
- type OSIsolator
- type PlatformCapabilities
- type Policy
- type ProcessPolicy
- type SandboxStatus
- type SeatbeltIsolator
Constants ¶
This section is empty.
Variables ¶
var ( ErrIsolatorUnavailable = errors.New("OS sandbox isolator unavailable") // ErrSandboxRequired indicates that sandbox.failClosed is true and no OS sandbox is available. ErrSandboxRequired = errors.New("sandbox required but OS isolator unavailable") // ErrInvalidPolicy indicates a policy configuration error. ErrInvalidPolicy = errors.New("invalid sandbox policy") )
Functions ¶
func CleanupProfileFile ¶
CleanupProfileFile is a no-op on non-macOS.
func GenerateSeatbeltProfile ¶
GenerateSeatbeltProfile creates a macOS Seatbelt .sb profile string from a Policy. All paths are resolved to absolute paths and validated against injection.
func SelectBackend ¶
func SelectBackend(mode BackendMode, candidates []BackendCandidate) (OSIsolator, BackendInfo)
SelectBackend picks the appropriate isolator from candidates based on the requested mode.
BackendAuto: returns the first available candidate. If none available, returns a noopIsolator. BackendNone: always returns a noopIsolator. Explicit mode (seatbelt/bwrap/native): returns the matching candidate's isolator as-is, even if unavailable. If the mode is not found in candidates, returns a noopIsolator.
Types ¶
type BackendCandidate ¶
type BackendCandidate struct {
Mode BackendMode
Isolator OSIsolator
}
BackendCandidate pairs a backend mode with its isolator implementation.
func PlatformBackendCandidates ¶
func PlatformBackendCandidates() []BackendCandidate
PlatformBackendCandidates returns the candidate list for the current platform. macOS: seatbelt, bwrap (Linux-only stub on darwin), native (stub). Linux: bwrap (real isolator if bubblewrap installed, otherwise unavailable), native (stub). Other: empty (will fallback to noop via SelectBackend).
type BackendInfo ¶
type BackendInfo struct {
Name string
Mode BackendMode
Available bool
Reason string
}
BackendInfo describes the status of a single sandbox backend.
func ListBackends ¶
func ListBackends(candidates []BackendCandidate) []BackendInfo
ListBackends returns status information for each candidate backend.
type BackendMode ¶
type BackendMode int
BackendMode identifies a sandbox backend by type.
const ( // BackendAuto selects the first available backend automatically. BackendAuto BackendMode = iota // BackendSeatbelt selects the macOS Seatbelt (sandbox-exec) backend. BackendSeatbelt // BackendBwrap selects the Linux bubblewrap (bwrap) backend. BackendBwrap // BackendNative selects the native kernel sandbox (landlock+seccomp) backend. BackendNative // BackendNone disables all sandbox backends explicitly. BackendNone )
func ParseBackendMode ¶
func ParseBackendMode(s string) (BackendMode, error)
ParseBackendMode converts a string to a BackendMode. An empty string maps to BackendAuto.
func (BackendMode) String ¶
func (m BackendMode) String() string
String returns a human-readable label for the backend mode.
type BwrapIsolator ¶
type BwrapIsolator struct {
// contains filtered or unexported fields
}
BwrapIsolator wraps exec.Cmd with bubblewrap (bwrap) on Linux to provide process-level isolation: filesystem bind mounts (read-only root + writable workspace), network unshare, and PID/IPC/UTS/cgroup namespaces.
The absolute bwrap path is captured at probe time (NewBwrapIsolator) and reused at Apply time so that PATH or working-directory changes between probe and execution cannot redirect to a different binary.
Availability is determined by a two-phase smoke probe:
- Base probe — reuses compileBwrapArgs with a read-only-global + NetworkAllow policy (matching MCPServerPolicy's network model) to verify the pid/ipc/uts/cgroup namespaces and root ro-bind that every lango policy requires. Failure marks the whole isolator unavailable.
- Network probe — additionally runs a NetworkDeny policy through compileBwrapArgs to verify --unshare-net works. Failure does NOT mark the isolator unavailable; instead, it downgrades Apply() so that only NetworkDeny/NetworkUnixOnly policies are rejected, while NetworkAllow policies (e.g. MCPServerPolicy) still succeed.
This two-phase design keeps the probe argv in lockstep with the runtime argv (zero drift because both go through compileBwrapArgs) while preventing a host that blocks --unshare-net from false-negating MCP.
func (*BwrapIsolator) Apply ¶
Apply rewrites cmd to run inside bwrap with arguments compiled from policy. The original argv becomes the child program after the bwrap "--" separator.
Apply-time network gate: if the probe could not verify --unshare-net, the isolator rejects NetworkDeny/NetworkUnixOnly policies with an ErrIsolatorUnavailable-wrapped error BEFORE mutating cmd. NetworkAllow policies proceed normally — partial degradation does not affect consumers that do not require network isolation.
func (*BwrapIsolator) Available ¶
func (b *BwrapIsolator) Available() bool
func (*BwrapIsolator) Name ¶
func (b *BwrapIsolator) Name() string
func (*BwrapIsolator) NetworkIsolationAvailable ¶
func (b *BwrapIsolator) NetworkIsolationAvailable() bool
NetworkIsolationAvailable reports whether the network smoke probe verified that --unshare-net works on this host. When false, Apply() rejects NetworkDeny/NetworkUnixOnly policies. NetworkAllow policies are unaffected. Available()==true does not imply network isolation availability; consumers that require network isolation should check this method separately.
func (*BwrapIsolator) NetworkIsolationReason ¶
func (b *BwrapIsolator) NetworkIsolationReason() string
NetworkIsolationReason returns the diagnostic message when network isolation is unavailable. Empty when NetworkIsolationAvailable()==true. The primary Reason() method (from the OSIsolator interface) remains empty whenever Available()==true, preserving the existing spec contract that partial degradation is surfaced only through the dedicated network methods.
func (*BwrapIsolator) Reason ¶
func (b *BwrapIsolator) Reason() string
func (*BwrapIsolator) Version ¶
func (b *BwrapIsolator) Version() string
Version returns the captured `bwrap --version` string, or "" when the isolator is unavailable. CLI surfaces (e.g. `lango sandbox test`) can type-assert to a versioner interface to display this.
type FilesystemPolicy ¶
type FilesystemPolicy struct {
// ReadOnlyGlobal allows read access to the entire filesystem.
// When true, ReadPaths is ignored — all paths are readable.
ReadOnlyGlobal bool
// ReadPaths are paths allowed for reading (used when ReadOnlyGlobal is false).
ReadPaths []string
// WritePaths are paths allowed for writing.
WritePaths []string
// DenyPaths are paths explicitly denied (takes precedence over allow).
DenyPaths []string
}
FilesystemPolicy defines filesystem access rules.
type NetworkPolicy ¶
type NetworkPolicy string
NetworkPolicy controls network access from the sandbox.
const ( // NetworkDeny blocks all network access. NetworkDeny NetworkPolicy = "deny" // NetworkAllow permits unrestricted network access. NetworkAllow NetworkPolicy = "allow" // NetworkUnixOnly allows only AF_UNIX sockets (for local IPC/proxy). NetworkUnixOnly NetworkPolicy = "unix-only" )
type OSIsolator ¶
type OSIsolator interface {
// Apply configures the given exec.Cmd to run under OS-level isolation.
// The command may be wrapped (e.g., sandbox-exec on macOS).
// Apply does not start the process.
Apply(ctx context.Context, cmd *exec.Cmd, policy Policy) error
// Available reports whether this isolator's OS primitives are functional.
Available() bool
// Name returns the isolator name (e.g., "seatbelt", "noop").
Name() string
// Reason returns a human-readable explanation of why the isolator is
// unavailable. Returns "" when Available() is true.
Reason() string
}
OSIsolator applies OS-level security restrictions to a subprocess command. It modifies exec.Cmd in place before the caller runs it. On macOS: wraps with sandbox-exec and a generated Seatbelt profile. On Linux: wraps with bubblewrap (bwrap) when the binary is installed (BwrapIsolator). The native Landlock+seccomp backend is not yet implemented; selecting backend=native or relying on the default noop path leaves the command unsandboxed.
func NewBwrapIsolator ¶
func NewBwrapIsolator() OSIsolator
NewBwrapIsolator probes the system for bwrap and returns an isolator that either reports availability with a captured absolute path + version, or is unavailable with a human-readable reason. See the BwrapIsolator doc comment for the two-phase probe contract.
func NewCompositeIsolator ¶
func NewCompositeIsolator(isolators ...OSIsolator) OSIsolator
NewCompositeIsolator returns an isolator that applies multiple isolators in sequence.
func NewNativeStub ¶
func NewNativeStub() OSIsolator
NewNativeStub returns a stub isolator for the native kernel sandbox backend.
func NewOSIsolator ¶
func NewOSIsolator() OSIsolator
NewOSIsolator returns the legacy "default" isolator for the current platform. It is kept for backwards compatibility with callers that have not yet migrated to the backend registry.
On macOS: SeatbeltIsolator. On Linux: noopIsolator pointing to backend=bwrap (the native Landlock+ seccomp backend is not yet implemented; bwrap is wired via the registry, not via this function). On unsupported platforms: noopIsolator.
New code should use ParseBackendMode + SelectBackend(mode, PlatformBackendCandidates()) instead so that backend selection (auto, seatbelt, bwrap, native, none) is honored.
type PlatformCapabilities ¶
type PlatformCapabilities struct {
// HasSeatbelt indicates macOS sandbox-exec is available.
HasSeatbelt bool
// SeatbeltReason explains the Seatbelt probe result (e.g., "sandbox-exec found", "not on darwin").
SeatbeltReason string
// HasLandlock indicates Linux Landlock LSM is available (kernel 5.13+).
// Detected by calling landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION).
HasLandlock bool
// LandlockABI is the detected Landlock ABI version (0 = unavailable, 1+ = supported).
LandlockABI int
// LandlockReason explains the Landlock probe result (e.g., "Landlock ABI 3",
// "Landlock not supported by this kernel (requires 5.13+)").
LandlockReason string
// HasSeccomp indicates that the kernel exposes the seccomp prctl interface
// (PR_GET_SECCOMP succeeds). This does NOT prove that BPF filters can be
// installed — it is a generic presence signal only. The SeccompReason field
// carries the qualified description.
HasSeccomp bool
// SeccompReason explains the seccomp probe result, including the caveat
// that PR_GET_SECCOMP success only proves interface presence, not filter
// capability.
SeccompReason string
// Platform is the runtime GOOS value.
Platform string
// KernelVersion is the OS kernel version string.
KernelVersion string
}
PlatformCapabilities describes what OS-level sandbox primitives are available.
func Probe ¶
func Probe() PlatformCapabilities
Probe detects the available OS-level sandbox capabilities.
func (PlatformCapabilities) Summary ¶
func (c PlatformCapabilities) Summary() string
Summary returns a human-readable summary of the detected capabilities.
type Policy ¶
type Policy struct {
// Filesystem controls file access.
Filesystem FilesystemPolicy
// Network controls network access.
Network NetworkPolicy
// Process controls process creation and signals.
Process ProcessPolicy
// AllowedNetworkIPs are IP addresses allowed for outbound connections (macOS only).
// On Linux, this field is ignored (seccomp cannot filter by IP).
AllowedNetworkIPs []string
}
Policy defines the sandbox restrictions for a single tool execution.
func DefaultToolPolicy ¶
DefaultToolPolicy returns the standard sandbox policy for local tool execution. Read-global, write-workspace+/tmp, no network. The first ancestor git metadata discovered via upward walk from workDir and the entire lango control-plane (dataRoot, e.g. ~/.lango) are denied so that sandboxed children cannot read or write the agent's own state, secrets, session database, skills directory, or other internal data.
Git metadata discovery uses findGitRoot's two-path model: a standard `.git` directory adds one DenyPaths entry; a linked worktree `.git` file adds two — the pointer file itself (denied at file level via PR 5c) AND the resolved gitdir target it points to. A fully non-repo workspace or a worktree pointer whose content is malformed silently degrades to whatever coverage is possible. A missing dataRoot is also skipped; pass an empty dataRoot to intentionally drop the mask.
workDir itself flows through filepath.Abs + filepath.EvalSymlinks so that WritePaths[0] is the canonical filesystem path (symlinked workDirs no longer leak their pre-resolve path into the writable set).
func MCPServerPolicy ¶
MCPServerPolicy returns a policy for MCP stdio server processes. Read-global, write-/tmp only, network allowed (MCP servers need network). The first ancestor `.git` directory discovered via walk-up from workDir and the lango control-plane (dataRoot) are denied so that misbehaving MCP server child processes cannot read or write git metadata or lango's internal state. This mirrors DefaultToolPolicy's baseline deny — both sandboxed-children surfaces share the same protection.
Baseline deny paths are added only when they exist as directories. An empty workDir or a workDir with no ancestor `.git` directory is silently skipped. A missing or non-directory dataRoot is also skipped so that isolated unit tests and minimal environments can still build the policy. Pass empty strings to intentionally drop the masks.
func StrictToolPolicy ¶
StrictToolPolicy returns a maximally restrictive policy. Currently identical to DefaultToolPolicy because .git denial and control-plane masking are now part of the baseline. The function is preserved as a separate symbol so callers (and future strict-only options) can branch later without another signature migration.
type ProcessPolicy ¶
type ProcessPolicy struct {
// AllowFork permits fork/exec within the sandbox.
AllowFork bool
// AllowSignals permits sending signals to other processes.
AllowSignals bool
}
ProcessPolicy defines process creation controls.
type SandboxStatus ¶
type SandboxStatus struct {
// Enabled reflects the sandbox.enabled config value.
Enabled bool
// FailClosed reflects the sandbox.failClosed config value.
FailClosed bool
// Isolator is the active OS isolator (never nil).
Isolator OSIsolator
// Capabilities holds the platform primitive probe results.
Capabilities PlatformCapabilities
}
SandboxStatus summarizes the operational state of the sandbox subsystem. It combines configuration values with runtime probe results.
func NewSandboxStatus ¶
func NewSandboxStatus(enabled, failClosed bool, iso OSIsolator) SandboxStatus
NewSandboxStatus creates a SandboxStatus from config and an isolator. If iso is nil (sandbox disabled), a disabledIsolator is substituted.
type SeatbeltIsolator ¶
type SeatbeltIsolator struct{}
SeatbeltIsolator is unavailable on non-macOS platforms.
func NewSeatbeltIsolator ¶
func NewSeatbeltIsolator() *SeatbeltIsolator
NewSeatbeltIsolator returns a stub on non-macOS.
func (*SeatbeltIsolator) Available ¶
func (s *SeatbeltIsolator) Available() bool
func (*SeatbeltIsolator) Name ¶
func (s *SeatbeltIsolator) Name() string
func (*SeatbeltIsolator) Reason ¶
func (s *SeatbeltIsolator) Reason() string