sandlock

package module
v0.8.6 Latest Latest
Warning

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

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

README

sandlock Go SDK

Go bindings for sandlock, a lightweight Linux process sandbox built on Landlock, seccomp-bpf, and seccomp user notification. No root, no Docker, no namespaces.

The bindings bind the sandlock C ABI (libsandlock_ffi) via cgo, mirroring the Python SDK's Sandbox surface. Linux only; by default the runtime requires Linux 6.12+ (Landlock ABI v6), but the AllowDegraded / Disable fields let a sandbox run on older kernels by degrading or disabling the v6-only protections.

import sandlock "github.com/multikernel/sandlock/go"

Building

cgo links against libsandlock_ffi, produced by the Rust workspace. There are two build modes.

Released mode (default): installed library via pkg-config

The default build resolves the library and header through pkg-config, so the SDK is usable from another module once the native side is installed. From a checkout of the sandlock repository:

sudo make install-go-lib         # installs libsandlock_ffi.so, sandlock.h, sandlock.pc
go get github.com/multikernel/sandlock/go

make install-go-lib honors PREFIX (default /usr/local) and DESTDIR. For a non-standard prefix, point pkg-config at it:

make install-go-lib PREFIX=$HOME/.local
export PKG_CONFIG_PATH=$HOME/.local/lib/pkgconfig

The installed sandlock.pc bakes an rpath to its libdir, so binaries find the shared library at runtime without LD_LIBRARY_PATH.

In-tree mode: build against this checkout (-tags sandlock_repo)

For development without installing, build with -tags sandlock_repo, which points cgo at this checkout's target/release:

cargo build --release -p sandlock-ffi    # writes target/release/libsandlock_ffi.so
cd go && go build -tags sandlock_repo ./...
# run the test suite the same way:
go test -tags sandlock_repo ./...

Quick start

package main

import (
	"context"
	"fmt"
	"log"

	sandlock "github.com/multikernel/sandlock/go"
)

func main() {
	sb := &sandlock.Sandbox{
		FSReadable: []string{"/usr", "/lib", "/lib64", "/bin", "/etc"},
		FSWritable: []string{"/tmp"},
	}
	res, err := sb.Run(context.Background(), "echo", "hello")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("exit=%d: %s", res.ExitCode, res.Stdout) // exit=0: hello
}

API

Sandbox

Sandbox is a plain configuration struct; every field is optional and an unset field means "no restriction" unless noted. sandlock's default syscall blocklist is always applied. A Sandbox carries no runtime state, so it is safe to reuse and share across goroutines — Run, RunInteractive, and DryRun build a fresh native policy on each call.

Group Fields
Filesystem FSReadable, FSWritable, FSDenied, Workdir, Cwd, Chroot, FSMount
Network NetAllow, NetDeny, NetAllowBind, NetDenyBind, PortRemap
HTTP ACL HTTPAllow, HTTPDeny, HTTPPorts, HTTPCAFile, HTTPKeyFile
Resources MaxMemory, MaxDisk, MaxProcesses, MaxCPU, MaxOpenFiles, CPUCores, NumCPUs, GPUDevices
Syscalls ExtraAllowSyscalls, ExtraDenySyscalls
Determinism RandomSeed, TimeStart, NoRandomizeMemory, NoHugePages, DeterministicDirs
Environment CleanEnv, Env
Misc UID, GID, NoCoredump, Name
COW branch FSStorage, OnExit, OnError
Dynamic policy PolicyFn

NetAllow entries follow sandlock's rule grammar: bare host:port is TCP ("api.openai.com:443", "github.com:22,443", ":53"); a target may be a host, IP, or CIDR ("10.0.0.0/8:443", "[2606:4700::/32]:443"); scheme prefixes opt other protocols in ("udp://1.1.1.1:53", "udp://*", "icmp://host", "icmp://*"). NetDeny is the inverse (default-allow denylist, IP/CIDR targets only, mutually exclusive with NetAllow). NetAllowBind entries are comma-separated single ports or inclusive ranges ("8080", "3000-3010", "8080,9000-9005"). NetDenyBind is the inverse (default-allow bind, deny these TCP ports; same syntax, mutually exclusive with NetAllowBind).

Execution
func (s *Sandbox) Run(ctx context.Context, cmd ...string) (*Result, error)
func (s *Sandbox) RunInteractive(ctx context.Context, cmd ...string) (int, error)
func (s *Sandbox) DryRun(ctx context.Context, cmd ...string) (*DryRunResult, error)
func (s *Sandbox) Spawn(cmd ...string) (*Process, error)
func (s *Sandbox) Popen(stdio Stdio, cmd ...string) (*Process, error)
  • Run captures stdout/stderr and waits. A ctx deadline kills the process and returns a result with ExitCode == -1. ctx cancellation without a deadline does not preempt a running child.
  • RunInteractive inherits the caller's stdio and returns the exit code.
  • DryRun runs against a temporary copy-on-write layer, reports the filesystem Changes it would have made, and discards them. Requires Workdir.
  • Spawn starts a process without waiting, returning a *Process.
  • Popen is the streaming counterpart of Spawn: each stream set to StdioPiped is handed back on the *Process as an *os.File (Stdin/Stdout/Stderr) you read/write while the child runs. The zero Stdio inherits all three (identical to Spawn). Close a piped Stdin before Wait (or let Wait close it for you) so a reader child sees EOF; drain a piped Stdout/Stderr before Wait, or Kill from another goroutine to interrupt a blocked Wait. Wait returns a Result with the exit status only — a Popen'd process sends piped output to the Stdout/Stderr fields (inherited/null streams go to the parent fd or /dev/null), so (unlike Run) Result.Stdout/Result.Stderr are always empty.
p, _ := sb.Popen(sandlock.Stdio{Stdin: sandlock.StdioPiped, Stdout: sandlock.StdioPiped}, "cat")
defer p.Close()
p.Stdin.Write([]byte("hi\n"))
p.Stdin.Close()                 // EOF so cat exits
out, _ := io.ReadAll(p.Stdout)  // "hi\n"
res, _ := p.Wait()
Dynamic policy callbacks
type PolicyFunc func(event SyscallEvent, ctx *PolicyContext) PolicyDecision

func Allow() PolicyDecision
func Deny() PolicyDecision
func Audit() PolicyDecision
func DenyWith(errnoValue int) PolicyDecision

func (e SyscallEvent) ArgvContains(sub string) bool

func (ctx *PolicyContext) RestrictNetwork(ips []string) error
func (ctx *PolicyContext) GrantNetwork(ips []string) error
func (ctx *PolicyContext) RestrictMaxMemory(bytes uint64)
func (ctx *PolicyContext) RestrictMaxProcesses(n uint32)
func (ctx *PolicyContext) RestrictPIDNetwork(pid uint32, ips []string) error
func (ctx *PolicyContext) DenyPath(path string) error
func (ctx *PolicyContext) AllowPath(path string) error

PolicyFn receives dynamic syscall events from sandlock's policy-fn worker thread. Path strings are deliberately absent; use Landlock fields for static path policy and DenyPath/AllowPath for the dynamic path-deny hook. Argv is populated for execve/execveat events.

sb := &sandlock.Sandbox{
    FSReadable: []string{"/usr", "/lib", "/lib64", "/bin", "/etc"},
    PolicyFn: func(event sandlock.SyscallEvent, ctx *sandlock.PolicyContext) sandlock.PolicyDecision {
        if event.Syscall == "execve" && event.ArgvContains("curl") {
            return sandlock.Deny()
        }
        return sandlock.Allow()
    },
}
Process lifecycle
func (p *Process) Pid() int
func (p *Process) Wait() (*Result, error)
func (p *Process) Pause() error           // SIGSTOP to the process group
func (p *Process) Resume() error          // SIGCONT
func (p *Process) Kill() error            // SIGKILL
func (p *Process) Ports() (map[int]int, error) // virtual→real, with PortRemap
func (p *Process) Close() error           // release the handle (kills if running), close piped streams

// Popen only: caller-owned pipe ends, non-nil per stream wired StdioPiped.
p.Stdin  // *os.File
p.Stdout // *os.File
p.Stderr // *os.File
Confine the current process
func Confine(s *Sandbox) error

Applies the sandbox's Landlock filesystem rules to the current process, in place and irreversibly — no fork, no exec. Only filesystem fields are honored; configuration that needs a supervisor or a fresh child (seccomp, network, resource limits, environment, ...) is rejected rather than silently ignored. This is something the sandlock CLI cannot do.

Platform
func LandlockABIVersion() int        // kernel's Landlock ABI, or -1
func MinLandlockABI() int            // minimum this build requires
func SyscallNr(name string) (int, error)

Status

This SDK covers the static policy surface, dynamic policy_fn callbacks, and in-process Confine. The following sandlock features are not yet bound and are tracked as follow-ups: custom seccomp handlers, pipelines, gather (fan-in), COW fork/reduce, and checkpoint/restore.

License

Apache-2.0

Documentation

Overview

Package sandlock provides Go bindings for sandlock, a lightweight Linux process sandbox built on Landlock, seccomp-bpf, and seccomp user notification. It binds the sandlock C ABI (libsandlock_ffi) via cgo and mirrors the Python SDK's Sandbox surface.

The bindings are Linux-only. By default the runtime requires Linux 6.12+ (Landlock ABI v6); use the AllowDegraded or Disable fields to run on older kernels by degrading or disabling the v6-only protections. See the project README for the full kernel feature matrix.

Building

cgo links against libsandlock_ffi, which is produced by the Rust workspace:

cargo build --release        # writes target/release/libsandlock_ffi.so
cd go && go test ./...

The default cgo link flags resolve the library relative to this package (../target/release). Build from a checkout of the sandlock repository, or adjust the link flags for an installed library.

Quick start

sb := &sandlock.Sandbox{
    FSReadable: []string{"/usr", "/lib", "/lib64", "/bin", "/etc"},
    FSWritable: []string{"/tmp"},
}
res, err := sb.Run(context.Background(), "echo", "hello")
if err != nil {
    log.Fatal(err)
}
fmt.Printf("%d: %s", res.ExitCode, res.Stdout)

Index

Constants

This section is empty.

Variables

View Source
var ErrInvalidString = errors.New("sandlock: string contains NUL byte")

ErrInvalidString is returned when a string passed to the SDK contains an interior NUL byte, which cannot cross the C ABI boundary intact.

View Source
var ErrNotRunning = errors.New("sandlock: process is not running")

ErrNotRunning is returned by *Process lifecycle methods when no process is currently running in the handle.

Functions

func Confine

func Confine(s *Sandbox) error

Confine applies the Sandbox's Landlock filesystem rules to the current process, in place and irreversibly. Only filesystem fields are honored; configuration that requires a supervisor or a fresh child (seccomp, network, resource limits, environment, etc.) is rejected by the core rather than silently ignored.

func LandlockABIVersion

func LandlockABIVersion() int

LandlockABIVersion returns the Landlock ABI version supported by the running kernel, or -1 if Landlock is unavailable.

func MinLandlockABI

func MinLandlockABI() int

MinLandlockABI returns the minimum Landlock ABI version this build requires.

func ProtectionMinABI added in v0.8.4

func ProtectionMinABI(p Protection) int

ProtectionMinABI returns the minimum Landlock ABI version the host kernel must support for the given protection to be available, or 0 for an unknown protection value.

func SyscallNr

func SyscallNr(name string) (int, error)

SyscallNr resolves a syscall name (e.g. "openat") to its kernel syscall number for the host architecture. It returns an error for names sandlock cannot resolve (syscalls outside the set it filters or supervises).

Types

type BranchAction

type BranchAction uint8

BranchAction is the action taken on a copy-on-write working-directory branch when the sandbox exits. The zero value, BranchActionDefault, leaves the choice to sandlock's own defaults (commit on success, abort on error).

const (
	// BranchActionDefault defers to sandlock's built-in default.
	BranchActionDefault BranchAction = iota
	// BranchActionCommit merges the branch's writes into the parent on exit.
	BranchActionCommit
	// BranchActionAbort discards all of the branch's writes on exit.
	BranchActionAbort
	// BranchActionKeep leaves the branch in place for the caller to handle.
	BranchActionKeep
)

type Change

type Change struct {
	Kind ChangeKind // 'A' added, 'M' modified, 'D' deleted
	Path string     // path relative to the working directory
}

Change is a single filesystem change detected by DryRun.

type ChangeKind

type ChangeKind byte

ChangeKind classifies a filesystem change observed during a dry run.

const (
	ChangeAdded    ChangeKind = 'A'
	ChangeModified ChangeKind = 'M'
	ChangeDeleted  ChangeKind = 'D'
)

type DryRunResult

type DryRunResult struct {
	Result
	Changes []Change
}

DryRunResult is the outcome of a dry run: a normal Result plus the list of filesystem changes the command would have made, all of which are discarded.

type ExitReason added in v0.8.5

type ExitReason uint32

ExitReason is why a sandboxed process terminated. It mirrors the C sandlock_exit_reason enum. Linux bottoms both a timeout and an OOM kill out in SIGKILL, so there is no distinct OOM reason: a timeout sandlock enforced is ReasonTimeout, any other kill is ReasonKilled.

const (
	// ReasonExited: exited normally with a code (Result.ExitCode).
	ReasonExited ExitReason = 0
	// ReasonSignaled: terminated by a signal (Result.Signal).
	ReasonSignaled ExitReason = 1
	// ReasonKilled: killed with no recoverable signal number.
	ReasonKilled ExitReason = 2
	// ReasonTimeout: killed by sandlock because it exceeded its timeout.
	ReasonTimeout ExitReason = 3
)

type PolicyContext

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

PolicyContext lets a PolicyFunc adjust selected live policy state.

A PolicyContext is valid only during the PolicyFunc call that received it. Do not retain it after the callback returns.

func (*PolicyContext) AllowPath

func (c *PolicyContext) AllowPath(path string) error

AllowPath removes a dynamic path denial previously added by DenyPath.

func (*PolicyContext) DenyPath

func (c *PolicyContext) DenyPath(path string) error

DenyPath dynamically denies access to path for mediated openat checks.

func (*PolicyContext) GrantNetwork

func (c *PolicyContext) GrantNetwork(ips []string) error

GrantNetwork grants ips within the sandbox's immutable network ceiling.

func (*PolicyContext) RestrictMaxMemory

func (c *PolicyContext) RestrictMaxMemory(bytes uint64)

RestrictMaxMemory permanently restricts the live max-memory policy.

func (*PolicyContext) RestrictMaxProcesses

func (c *PolicyContext) RestrictMaxProcesses(n uint32)

RestrictMaxProcesses permanently restricts the live max-processes policy.

func (*PolicyContext) RestrictNetwork

func (c *PolicyContext) RestrictNetwork(ips []string) error

RestrictNetwork permanently restricts the live network policy to ips.

func (*PolicyContext) RestrictPIDNetwork

func (c *PolicyContext) RestrictPIDNetwork(pid uint32, ips []string) error

RestrictPIDNetwork restricts network access for a specific process.

type PolicyDecision

type PolicyDecision int32

PolicyDecision is the result returned by a PolicyFunc.

const (
	// DecisionAllow allows the syscall.
	DecisionAllow PolicyDecision = 0
	// DecisionDeny denies the syscall with EPERM.
	DecisionDeny PolicyDecision = -1
	// DecisionAudit allows the syscall and flags it for audit.
	DecisionAudit PolicyDecision = -2
)

func Allow

func Allow() PolicyDecision

Allow returns a decision that allows the syscall.

func Audit

func Audit() PolicyDecision

Audit returns a decision that allows the syscall and flags it for audit.

func Deny

func Deny() PolicyDecision

Deny returns a decision that denies the syscall with EPERM.

func DenyWith

func DenyWith(errnoValue int) PolicyDecision

DenyWith returns a decision that denies the syscall with errnoValue.

type PolicyFunc

type PolicyFunc func(event SyscallEvent, ctx *PolicyContext) PolicyDecision

PolicyFunc is a dynamic policy callback invoked from sandlock's policy-fn worker thread. Callbacks may be invoked concurrently with other sandbox activity, so captured state should be synchronized when mutated.

type Process

type Process struct {

	// Caller-owned stdio for a process started by Popen. Each is non-nil only
	// for a stream wired StdioPiped; it owns the pipe fd (closing the file
	// closes the fd). Nil for Spawn'd processes and for inherit/null streams.
	Stdin  *os.File
	Stdout *os.File
	Stderr *os.File
	// contains filtered or unexported fields
}

Process is a live sandboxed process started by Spawn. It supports PID inspection, pause/resume/kill via the process group, and Wait. A Process holds at most one running command; create separate Spawns for concurrency.

The underlying FFI handle is not safe for concurrent access, so all handle operations are serialized. Pause/Resume/Kill act on the OS process group by PID and touch no handle state, so they remain usable while Wait blocks on the handle — that is how Kill interrupts a blocked Wait. Ports, by contrast, reads the handle and is reported as empty while a Wait is in flight.

func (*Process) Close

func (p *Process) Close() error

Close releases the process handle, killing the process if it is still running. It is safe to call multiple times.

func (*Process) Kill

func (p *Process) Kill() error

Kill sends SIGKILL to the sandbox process group. It is idempotent: a process that already exited (ESRCH) or was already reaped by Wait (ErrNotRunning) is not an error — killing it is a no-op success, matching the FFI (sandlock_handle_kill returns 0) and the Python binding (silent no-op after wait()). This keeps the "Kill from another goroutine" escape hatch race-free: a Kill that fires just as Wait reaps the child still returns nil.

func (*Process) Pause

func (p *Process) Pause() error

Pause sends SIGSTOP to the sandbox process group.

func (*Process) Pid

func (p *Process) Pid() int

Pid returns the child process ID, or 0 if it is not available.

func (*Process) Ports

func (p *Process) Ports() (map[int]int, error)

Ports returns the current virtual-to-real TCP port mappings while the process is running. It is non-empty only when PortRemap is enabled and at least one port has been remapped.

func (*Process) Resume

func (p *Process) Resume() error

Resume sends SIGCONT to the sandbox process group.

func (*Process) Wait

func (p *Process) Wait() (*Result, error)

Wait blocks until the process exits, returns its Result, and releases the handle. After Wait the Process is no longer running.

The Result carries the exit status only. Unlike Run, a Popen'd process sends any StdioPiped output to the caller through the Stdout/Stderr fields (and StdioInherit/StdioNull output to the inherited fd or /dev/null), so it is never captured into Result.Stdout/Result.Stderr — read piped bytes off p.Stdout/p.Stderr, not off the Result.

The blocking native wait runs without holding the mutex so that Kill (and Pause/Resume), which signal the process group by PID, can run concurrently and interrupt it. The waiting flag reserves exclusive use of the handle for the duration, so no other handle operation aliases it.

Wait closes a still-open piped Stdin to deliver EOF, so do not write to Stdin from another goroutine while Wait runs — the write would race a closing fd.

type Protection added in v0.8.4

type Protection uint32

Protection identifies a single Landlock protection whose enforcement posture can be opted out of via the Sandbox AllowDegraded / Disable fields. The values match the sandlock C ABI discriminants.

const (
	ProtectionFSRefer                 Protection = 0 // file reparenting (Landlock ABI v2)
	ProtectionFSTruncate              Protection = 1 // truncate(2) (ABI v3)
	ProtectionNetTCP                  Protection = 2 // TCP bind/connect (ABI v4)
	ProtectionFSIoctlDev              Protection = 3 // device ioctl(2) (ABI v5)
	ProtectionSignalScope             Protection = 4 // signal scoping (ABI v6)
	ProtectionAbstractUnixSocketScope Protection = 5 // abstract UNIX socket scoping (ABI v6)
)

type Result

type Result struct {
	ExitCode int        // process exit code, or -1 if terminated abnormally
	Reason   ExitReason // why the process terminated (exit / signal / kill / timeout)
	Signal   int        // signal number for a ReasonSignaled result, else -1
	Success  bool       // true when the process exited 0
	Stdout   []byte     // captured standard output
	Stderr   []byte     // captured standard error
}

Result is the outcome of a captured run.

type Sandbox

type Sandbox struct {
	// Filesystem (Landlock).
	FSReadable []string // paths the sandbox may read (and execute)
	FSWritable []string // paths the sandbox may write
	FSDenied   []string // paths explicitly denied

	Workdir string // copy-on-write root; enables COW protection of this tree
	Cwd     string // child working directory (chdir target)
	Chroot  string // path to chroot into before applying confinement

	// FSMount maps virtual paths inside the chroot to host directories,
	// like a bind mount without kernel mounts or root.
	FSMount map[string]string

	// Protection opt-out (Landlock per-protection posture).
	//
	// By default every protection is enforced strictly, which requires the
	// host kernel to support it (the highest floor is ABI v6); on an older
	// kernel a strict protection it cannot satisfy makes the build fail.
	// AllowDegraded marks protections to enforce where the host supports them
	// and silently skip otherwise; Disable turns them off entirely. Together
	// they let a sandbox run on a kernel below the default v6 floor.
	//
	// A protection listed in both fields is disabled: Disable is applied
	// last and takes precedence.
	AllowDegraded []Protection
	Disable       []Protection

	// Network.
	//
	// NetAllow entries are outbound endpoint rules. The bare form is TCP
	// ("api.openai.com:443", "github.com:22,443", ":53"); a target may be a
	// host, IP, or CIDR ("10.0.0.0/8:443", "[2606:4700::/32]:443"), and
	// scheme prefixes opt other protocols in ("tcp://", "udp://host:port",
	// "udp://*", "icmp://host", "icmp://*"). Empty denies all outbound.
	NetAllow []string
	// NetDeny is the inverse of NetAllow: default-allow networking, block
	// these targets. Same grammar as NetAllow except targets must be a
	// literal IP/CIDR or "*" (no hostnames; use HTTPDeny for domains).
	// Mutually exclusive with NetAllow.
	NetDeny []string
	// NetAllowBind lists TCP ports the sandbox may bind/listen on
	// (default-deny). Each entry is a comma-separated list of single ports
	// or inclusive "lo-hi" ranges ("8080", "3000-3010", "8080,9000-9005").
	// The "*" wildcard allows binding any port and cannot be mixed with
	// port entries.
	// Mutually exclusive with NetDenyBind.
	NetAllowBind []string
	// NetDenyBind is the inverse of NetAllowBind: default-allow binding,
	// deny these TCP ports (same port syntax). Mutually exclusive with
	// NetAllowBind.
	NetDenyBind []string
	PortRemap   bool // transparent per-sandbox TCP port virtualization

	// HTTP ACL (method + host + path rules via a transparent proxy).
	HTTPAllow   []string // allow rules, "METHOD host/path"
	HTTPDeny    []string // deny rules, checked before allow rules
	HTTPPorts   []int    // ports to intercept (defaults to 80, plus 443 with a CA)
	HTTPCAFile  string   // PEM CA certificate for HTTPS MITM
	HTTPKeyFile string   // PEM CA private key (required with HTTPCAFile)

	// Resource limits.
	MaxMemory    string   // e.g. "512M"; empty = unlimited
	MaxDisk      string   // disk quota for COW storage, e.g. "1G"
	MaxProcesses uint32   // peak concurrent process cap; 0 = sandlock default
	MaxCPU       uint8    // CPU throttle, percent of one core (1-100); 0 = unset
	MaxOpenFiles uint32   // RLIMIT_NOFILE soft+hard in the child, clamped to sandlock's own limits; 0 = inherit
	CPUCores     []uint32 // cores to pin to via sched_setaffinity
	NumCPUs      uint32   // synthetic /proc/cpuinfo processor count; 0 = unset
	GPUDevices   []uint32 // GPU device indices to expose; nil = none

	// Syscall filtering (on top of sandlock's default blocklist).
	ExtraAllowSyscalls []string // syscall groups to allow, e.g. "sysv_ipc"
	ExtraDenySyscalls  []string // extra syscall names to block

	// Determinism.
	RandomSeed        *uint64 // seed getrandom() deterministically
	TimeStart         string  // virtual clock start: RFC3339 or unix seconds
	NoRandomizeMemory bool    // disable ASLR
	NoHugePages       bool    // disable transparent huge pages
	DeterministicDirs bool    // sort readdir() entries

	// Environment.
	CleanEnv bool              // start from a minimal environment
	Env      map[string]string // variables to set/override in the child

	// Misc.
	UID        *int // map to this UID inside a user namespace; nil = unset
	GID        *int // map to this GID inside the user namespace; must be set together with UID
	NoCoredump bool // disable core dumps and restrict /proc/pid access

	// Copy-on-write branch handling.
	FSStorage string       // storage directory for COW deltas
	OnExit    BranchAction // branch action on normal exit
	OnError   BranchAction // branch action on error exit

	// Name is the sandbox name and its virtual hostname inside the sandbox.
	// Empty auto-generates "sandbox-{pid}".
	Name string

	// PolicyFn receives dynamic syscall events and may return an allow/deny
	// decision or modify live policy through the supplied context.
	PolicyFn PolicyFunc
}

Sandbox holds the policy configuration for confining a process. Every field is optional; an unset field means "no restriction" unless documented otherwise. sandlock's default syscall blocklist is always applied.

A Sandbox value carries no runtime state: Run, RunInteractive, and DryRun build a fresh native policy on each call, so a single Sandbox may be reused and shared across goroutines. Use Spawn for explicit process lifecycle control, which returns an independent *Process handle.

func (*Sandbox) DryRun

func (s *Sandbox) DryRun(ctx context.Context, cmd ...string) (*DryRunResult, error)

DryRun executes cmd against a temporary copy-on-write layer, collects the filesystem changes it would have made, then discards them. It requires Workdir to be set.

func (*Sandbox) Popen added in v0.8.5

func (s *Sandbox) Popen(stdio Stdio, cmd ...string) (*Process, error)

Popen forks the sandboxed child with per-stream stdio and returns a live Process without waiting — the streaming counterpart of Spawn. For each stream set to StdioPiped, the matching Process field (Stdin/Stdout/Stderr) is an *os.File the caller reads/writes while the child runs; inherit/null streams leave it nil. The zero Stdio inherits all three (identical to Spawn).

Deadlock warning (as with os/exec pipes): a piped Stdin is yours — close it before Wait, or a child that reads to EOF (e.g. cat) never exits and Wait blocks. Likewise drain a piped Stdout/Stderr before Wait, or a child that fills the pipe buffer blocks on write. As an escape hatch, Kill from another goroutine interrupts a blocked Wait; Close reaps the child and closes the streams. Wait closes a still-open piped Stdin for you to deliver EOF.

func (*Sandbox) Run

func (s *Sandbox) Run(ctx context.Context, cmd ...string) (*Result, error)

Run executes cmd in the sandbox, capturing stdout and stderr, and waits for it to finish. If ctx carries a deadline, the process is killed and a result with ExitCode -1 is returned once it elapses. ctx cancellation without a deadline does not preempt an already-running child.

func (*Sandbox) RunInteractive

func (s *Sandbox) RunInteractive(ctx context.Context, cmd ...string) (int, error)

RunInteractive executes cmd with the calling process's stdio inherited (no capture) and returns the exit code. The context is honored only as a pre-run cancellation check; interactive runs are not interrupted by a deadline.

func (*Sandbox) Spawn

func (s *Sandbox) Spawn(cmd ...string) (*Process, error)

Spawn forks the sandboxed child, installs the policy, and releases it to exec cmd without waiting. Use the returned Process to manage its lifecycle.

type Stdio added in v0.8.5

type Stdio struct {
	Stdin  StdioMode
	Stdout StdioMode
	Stderr StdioMode
}

Stdio selects the wiring of a Popen'd process's three standard streams. The zero value wires all three as StdioInherit, matching Spawn.

type StdioMode added in v0.8.5

type StdioMode uint32

StdioMode selects how one of a Popen'd process's standard streams is wired. The values are the stable ABI discriminants shared with the C/Rust core.

const (
	// StdioInherit shares the parent's fd (the child writes to the same
	// terminal/file). It is the zero value, so an unset stream inherits.
	StdioInherit StdioMode = 0
	// StdioPiped connects the stream to a pipe; Popen hands the caller the
	// owning end as an *os.File on the returned Process.
	StdioPiped StdioMode = 1
	// StdioNull connects the stream to /dev/null.
	StdioNull StdioMode = 2
)

type SyscallCategory

type SyscallCategory uint8

SyscallCategory is the high-level category of an intercepted syscall event.

const (
	// CategoryFile covers filesystem operations such as openat and unlinkat.
	CategoryFile SyscallCategory = iota
	// CategoryNetwork covers network operations such as connect and bind.
	CategoryNetwork
	// CategoryProcess covers process lifecycle operations such as execve.
	CategoryProcess
	// CategoryMemory covers memory-management operations such as mmap.
	CategoryMemory
)

func (SyscallCategory) String

func (c SyscallCategory) String() string

String returns the category name used by the Python SDK.

type SyscallEvent

type SyscallEvent struct {
	Syscall   string
	Category  SyscallCategory
	PID       uint32
	ParentPID uint32
	Host      string
	Port      uint16
	Denied    bool
	Argv      []string
}

SyscallEvent is a policy_fn event delivered by the sandbox supervisor.

Path strings are intentionally absent. Path-based access control belongs in Landlock rules (FSReadable, FSWritable, FSDenied). Argv is populated only for execve/execveat events, where sandlock freezes sibling tasks before exposing it to the policy callback.

func (SyscallEvent) ArgvContains

func (e SyscallEvent) ArgvContains(sub string) bool

ArgvContains reports whether any argv element contains sub.

Directories

Path Synopsis
examples
basic command
Command basic demonstrates running a command under a sandlock sandbox with a read-only root filesystem and a single writable directory.
Command basic demonstrates running a command under a sandlock sandbox with a read-only root filesystem and a single writable directory.
internal
policy
Package policy holds pure, platform-independent parsing helpers shared by the sandlock Go SDK.
Package policy holds pure, platform-independent parsing helpers shared by the sandlock Go SDK.

Jump to

Keyboard shortcuts

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