helper

package
v0.720.2 Latest Latest
Warning

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

Go to latest
Published: Sep 18, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package helper is the in-process half of the Linux sandbox: the `pando __sandbox-exec` re-exec helper. The parent (internal/sandbox, see wrapper_linux.go) rewrites a command to

<pando> __sandbox-exec --policy-fd N -- <argv...>

optionally prefixed by bubblewrap, and passes a Spec as JSON on fd N. The helper confines its own thread (no_new_privs, Landlock, seccomp) and then execve()s the real command, which inherits the confinement.

The helper must start fast and must never load Pando's configuration, logging or database. It therefore dispatches from this package's init function, which runs before the heavy packages of the binary are initialised, and this package imports only the standard library and golang.org/x/sys/unix (enforced by TestHelperImports). Any binary that links this package (pando itself, the desktop app, test binaries of packages that import internal/sandbox) can act as the helper.

Index

Constants

View Source
const (
	// ExitSetupFailed: the sandbox could not be applied; the command did not run.
	ExitSetupFailed = 126
	// ExitNotFound: the command to run does not exist.
	ExitNotFound = 127
)

Exit codes of the helper itself (the exec'd command's own codes pass through untouched, since the helper is replaced by execve).

View Source
const (
	// NetworkAllowed: no network filter.
	NetworkAllowed = "allowed"
	// NetworkRestricted: the seccomp network filter and, when the kernel
	// supports it, Landlock TCP and abstract-unix-socket restrictions.
	NetworkRestricted = "restricted"
)

Network modes of Spec.Network.

View Source
const (
	SeccompRetAllow uint32 = 0x7fff0000
	SeccompRetErrno uint32 = 0x00050000
)

seccomp return values (linux/seccomp.h).

View Source
const Arg = "__sandbox-exec"

Arg is the argv[1] that turns the binary into the sandbox helper.

View Source
const (

	// CloneNamespaceBits are the clone(2) flags that create a namespace.
	CloneNamespaceBits = cloneNewTime | cloneNewNS | cloneNewCgroup | cloneNewUTS |
		cloneNewIPC | cloneNewUser | cloneNewPID | cloneNewNet
)

Flag and constant values shared by amd64 and arm64.

View Source
const LandlockNetABI = 4

LandlockNetABI is the first Landlock ABI with TCP bind/connect rules (Linux 6.7).

View Source
const SpecVersion = 1

SpecVersion is the wire version of Spec. The helper refuses other versions (parent and helper are always the same binary, so a mismatch means a stale or foreign caller).

Variables

View Source
var (
	ArchAMD64 = Arch{
		Name: "amd64", AuditArch: 0xc000003e, X32: true,
		Mount: 165, Umount2: 166, PivotRoot: 155, Unshare: 272, Setns: 308, Clone: 56, Ioctl: 16,
		Socket: 41, Connect: 42, Bind: 49, Sendto: 44, Sendmsg: 46, Sendmmsg: 307,
		Listen: 50, Accept: 43, Accept4: 288,
	}
	ArchARM64 = Arch{
		Name: "arm64", AuditArch: 0xc00000b7,
		Mount: 40, Umount2: 39, PivotRoot: 41, Unshare: 97, Setns: 268, Clone: 220, Ioctl: 29,
		Socket: 198, Connect: 203, Bind: 200, Sendto: 206, Sendmsg: 211, Sendmmsg: 269,
		Listen: 201, Accept: 202, Accept4: 242,
	}
)

ArchAMD64 and ArchARM64 are the supported architectures.

Functions

func Args

func Args(policyFD int, argv []string) []string

Args builds the helper's own arguments (after the executable): [Arg, "--policy-fd", N, "--", argv...].

func IoUringSyscalls

func IoUringSyscalls() []uint32

IoUringSyscalls can create and connect sockets without entering the syscalls above, so they are refused under a restricted network.

func LandlockABI

func LandlockABI() (int, error)

LandlockABI returns the kernel's Landlock ABI version (landlock_create_ruleset with LANDLOCK_CREATE_RULESET_VERSION). ENOSYS means the kernel lacks Landlock (before 5.13); EOPNOTSUPP means it is built in but disabled.

func LandlockUnavailableReason

func LandlockUnavailableReason(err error) string

LandlockUnavailableReason renders a LandlockABI error for Capability.Reason.

func ParseArgs

func ParseArgs(args []string) (policyFD int, argv []string, err error)

ParseArgs parses the arguments that follow Arg: "--policy-fd N -- argv...".

func Run

func Run(args []string) int

Run is the helper entry point: args are the arguments after Arg. On success it does not return (the process image is replaced by the command); on failure it returns the exit code after printing the reason on stderr.

Types

type Arch

type Arch struct {
	Name string
	// AuditArch is the AUDIT_ARCH_* value in seccomp_data.arch.
	AuditArch uint32
	// X32 marks x86_64, whose x32 ABI shares the arch value but sets bit 30
	// of the syscall number; x32 calls are refused outright.
	X32 bool

	Mount, Umount2, PivotRoot, Unshare, Setns, Clone, Ioctl uint32
	Socket, Connect, Bind, Sendto, Sendmsg, Sendmmsg        uint32
	Listen, Accept, Accept4                                 uint32
}

Arch is the per-architecture syscall table the filters need.

func ArchFor

func ArchFor(goarch string) (Arch, error)

ArchFor returns the table for a GOARCH.

func NativeArch

func NativeArch() (Arch, error)

NativeArch returns the table for the running architecture.

func (Arch) MountSyscalls

func (a Arch) MountSyscalls() []uint32

MountSyscalls are the mount-API calls the namespace lockdown refuses.

func (Arch) NetworkSyscalls

func (a Arch) NetworkSyscalls() []uint32

NetworkSyscalls are refused under a restricted network when AF_UNIX is not kept (Grok Build child_net.rs:178-199); sendto only with a destination address, so send(2) on an already connected socketpair keeps working.

type FilterOptions

type FilterOptions struct {
	// RestrictNetwork adds the network set.
	RestrictNetwork bool
	// AllowUnixSockets (with RestrictNetwork) refuses only socket(2) of non
	// AF_UNIX families and io_uring, keeping local IPC usable.
	AllowUnixSockets bool
}

FilterOptions selects the parts of the program.

type SockFilter

type SockFilter struct {
	Code uint16
	Jt   uint8
	Jf   uint8
	K    uint32
}

SockFilter is one classic BPF instruction (struct sock_filter).

func BuildFilter

func BuildFilter(arch Arch, opts FilterOptions) ([]SockFilter, error)

BuildFilter assembles the helper's seccomp program for arch:

  • wrong architecture or x32 syscall number: EPERM;
  • mount API (mount, umount2, pivot_root, open_tree[_attr], move_mount, fsopen, fsconfig, fsmount, fspick, mount_setattr), unshare, setns: EPERM, so a child cannot build a user namespace and remount its way out of bubblewrap's read-only binds;
  • clone with any CLONE_NEW* flag: EPERM; clone3: ENOSYS (its flags live in memory classic BPF cannot read; libc falls back to clone);
  • ioctl TIOCSTI / TIOCLINUX: EPERM (terminal input injection);
  • with RestrictNetwork: io_uring_*: EPERM; socket(2) of a non-AF_UNIX family: EPERM; and unless AllowUnixSockets, connect, bind, sendmsg, sendmmsg, listen, accept, accept4 and sendto with an address: EPERM.

Everything else is allowed.

type Spec

type Spec struct {
	// V is SpecVersion.
	V int `json:"v"`
	// Path is the executable to run (exec.Cmd.Path). Empty means: look up
	// argv[0] in PATH.
	Path string `json:"path,omitempty"`
	// ReadDirs may be read and executed recursively ("/" means everything).
	ReadDirs []string `json:"readDirs,omitempty"`
	// WriteDirs may be read, written, created in and removed from
	// recursively.
	WriteDirs []string `json:"writeDirs,omitempty"`
	// WriteFiles are individual files that may be read and written (the
	// Landlock-only protected-path fallback grants the top-level files of a
	// writable root one by one).
	WriteFiles []string `json:"writeFiles,omitempty"`
	// Devices are device files and directories granted read/write/ioctl.
	// Missing ones, and ones that cannot be opened (a /dev/tty without a
	// controlling terminal), are skipped.
	Devices []string `json:"devices,omitempty"`
	// Network is NetworkAllowed or NetworkRestricted.
	Network string `json:"network"`
	// AllowUnixSockets keeps AF_UNIX usable under NetworkRestricted: only
	// socket(2) of other families (and io_uring) is refused. The parent sets
	// it only when it could mask the dangerous sockets (container runtimes,
	// D-Bus) with bubblewrap; otherwise every connect/bind/listen/accept and
	// addressed send is refused, whatever the family.
	AllowUnixSockets bool `json:"allowUnixSockets,omitempty"`
	// DenyConnectPorts are TCP ports the command must not connect to while
	// the network is allowed (Pando's own listeners). Enforced with Landlock
	// network rules when the kernel has them (ABI >= LandlockNetABI): every
	// other port is granted LANDLOCK_ACCESS_NET_CONNECT_TCP. Ignored under
	// NetworkRestricted, which already refuses every TCP connection.
	DenyConnectPorts []int `json:"denyConnectPorts,omitempty"`
	// PolicyHash is sandbox.Policy.Hash(), for diagnostics only.
	PolicyHash string `json:"policyHash,omitempty"`
}

Spec is the resolved confinement the helper applies. The parent computes it from sandbox.Policy (which lives in a package that imports the config system, so it is not decoded here): the helper only turns lists into Landlock rules and a seccomp program.

func DecodeSpec

func DecodeSpec(data []byte) (Spec, error)

DecodeSpec parses and validates a spec read from the policy fd.

func (Spec) Encode

func (s Spec) Encode() ([]byte, error)

Encode serialises the spec for the policy fd.

func (Spec) RestrictsNetwork

func (s Spec) RestrictsNetwork() bool

RestrictsNetwork reports whether the network filter is requested.

Jump to

Keyboard shortcuts

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