mirror of
https://github.com/Twigpine/zero.git
synced 2026-11-10 13:11:31 -10:00
* feat(sandbox): unify command execution and enforcement * test(sandbox): make platform contracts portable * fix(sandbox): close execution lifecycle races Bound retained interactive output, preserve interruption state, and start workspace observation before process launch. Protect active runtime roots with cross-platform leases, capture transient protected-path creation events, and restore process attributes after PTY fallback. * fix(sandbox): restore strict macOS isolation Remove Seatbelt host-local network allowances after native validation showed localhost filters can reach host interfaces. Track process reap completion so retained sessions cannot signal a stale numeric PID.
54 lines
1.8 KiB
Go
54 lines
1.8 KiB
Go
//go:build linux
|
|
|
|
package sandbox
|
|
|
|
import (
|
|
"fmt"
|
|
"unsafe"
|
|
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
// ApplyUnixSocketBlock installs a seccomp BPF filter on the CURRENT thread/process
|
|
// that denies socket(AF_UNIX, ...) with EPERM, closing the Unix-socket gap that
|
|
// the Linux helper's filesystem/network isolation leaves open. It must be called
|
|
// in the child, after fork and before exec, so the inner helper stage applies it
|
|
// and then execs the real command. It first sets NO_NEW_PRIVS (required to load a
|
|
// filter without CAP_SYS_ADMIN), then loads the program.
|
|
//
|
|
// Not yet verified on real Linux — see seccomp.go. Callers should degrade
|
|
// gracefully (run without the filter, with a warning) if this returns an error,
|
|
// since the kernel may not support seccomp.
|
|
func ApplyUnixSocketBlock() error {
|
|
return applySeccompFilter(unixSocketBlockFilter(), "unix socket block")
|
|
}
|
|
|
|
func ApplyLinuxNetworkDeny() error {
|
|
return applySeccompFilter(networkDenySeccompFilter(), "network deny")
|
|
}
|
|
|
|
func ApplyLinuxIsolatedNetworkGuard() error {
|
|
return applySeccompFilter(isolatedNetworkGuardFilter(), "isolated network guard")
|
|
}
|
|
|
|
func applySeccompFilter(filters []sockFilter, name string) error {
|
|
if err := unix.Prctl(unix.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); err != nil {
|
|
return fmt.Errorf("seccomp: set no_new_privs: %w", err)
|
|
}
|
|
kernelFilters := make([]unix.SockFilter, len(filters))
|
|
for i, f := range filters {
|
|
kernelFilters[i] = unix.SockFilter{Code: f.Code, Jt: f.Jt, Jf: f.Jf, K: f.K}
|
|
}
|
|
if len(kernelFilters) == 0 {
|
|
return fmt.Errorf("seccomp: empty filter for %s", name)
|
|
}
|
|
prog := unix.SockFprog{
|
|
Len: uint16(len(kernelFilters)),
|
|
Filter: &kernelFilters[0],
|
|
}
|
|
if err := unix.Prctl(unix.PR_SET_SECCOMP, uintptr(unix.SECCOMP_MODE_FILTER), uintptr(unsafe.Pointer(&prog)), 0, 0); err != nil {
|
|
return fmt.Errorf("seccomp: load %s filter: %w", name, err)
|
|
}
|
|
return nil
|
|
}
|