You've already forked agentic-coding-workflow
bugfix
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
//go:build linux
|
||||
|
||||
package sandbox
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// uidSandbox confines the agent child to a low-privilege UID/GID and applies OS
|
||||
// resource limits, while leaving proc.Group's Setpgid (tree-kill) intact.
|
||||
//
|
||||
// Tree-kill invariant: we ONLY add Credential to the existing SysProcAttr. We do
|
||||
// NOT touch Setpgid (proc.Group sets it after Apply via group.Prepare). The
|
||||
// negative-pgid SIGTERM/SIGKILL path keeps working because the new process group
|
||||
// leader is the same child — it simply runs as the low-priv uid now.
|
||||
//
|
||||
// Resource limits are applied by wrapping the real binary in `prlimit(1)`:
|
||||
// `prlimit --as=N --nproc=N --cpu=N --fsize=N -- <binary> <args...>`. prlimit
|
||||
// sets the limits on the child it execs, so they apply to the agent and (since
|
||||
// limits are inherited across fork) its whole subtree — exactly the tree the
|
||||
// pgid covers. Wrapping is additive to Credential/Setpgid and does not change
|
||||
// the process-group topology (prlimit execs the target in place, becoming the
|
||||
// group leader). If prlimit is unavailable, UID drop still applies and we log no
|
||||
// rlimits (best-effort; container mode is the hard boundary).
|
||||
//
|
||||
// Bind-mount masking (CLONE_NEWNS + remount /data with only HomeDir+WorktreeDir
|
||||
// visible) is intentionally NOT auto-enabled here: it requires the server to
|
||||
// hold CAP_SYS_ADMIN / a user namespace, which the rootless production container
|
||||
// (USER app) does not have by default. The robust isolation path is
|
||||
// ModeContainer. uidSandbox therefore provides UID drop + rlimits + egress proxy
|
||||
// as the minimum viable confinement, and documents that filesystem masking is a
|
||||
// container-mode guarantee.
|
||||
type uidSandbox struct{}
|
||||
|
||||
func (s *uidSandbox) Apply(cmd *exec.Cmd, spec Spec) (func(), error) {
|
||||
if spec.UID <= 0 {
|
||||
return func() {}, fmt.Errorf("sandbox uid: invalid target uid %d", spec.UID)
|
||||
}
|
||||
|
||||
// UID/GID drop — ADDITIVE to whatever proc.Group will set (Setpgid).
|
||||
if cmd.SysProcAttr == nil {
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
||||
}
|
||||
gid := spec.GID
|
||||
if gid <= 0 {
|
||||
gid = spec.UID
|
||||
}
|
||||
cmd.SysProcAttr.Credential = &syscall.Credential{
|
||||
Uid: uint32(spec.UID),
|
||||
Gid: uint32(gid),
|
||||
}
|
||||
|
||||
// Resource limits via prlimit wrapper (if available).
|
||||
if prlimit, err := exec.LookPath("prlimit"); err == nil {
|
||||
args := prlimitArgs(spec.Rlimits)
|
||||
if len(args) > 0 {
|
||||
// Re-point the command through prlimit, preserving the original
|
||||
// binary + args after the `--` separator.
|
||||
orig := append([]string{cmd.Path}, cmd.Args[1:]...)
|
||||
cmd.Path = prlimit
|
||||
cmd.Args = append(append([]string{prlimit}, args...), append([]string{"--"}, orig...)...)
|
||||
}
|
||||
}
|
||||
|
||||
// Egress allowlist: inject HTTP(S)_PROXY/NO_PROXY into the stripped child env.
|
||||
injectProxyEnv(cmd, spec)
|
||||
|
||||
// No ephemeral resources to tear down in uid mode (no bind mounts here);
|
||||
// return a no-op cleanup.
|
||||
return func() {}, nil
|
||||
}
|
||||
|
||||
// prlimitArgs builds the prlimit flags for the non-zero limits in l.
|
||||
func prlimitArgs(l Limits) []string {
|
||||
var args []string
|
||||
if l.AddressSpaceBytes > 0 {
|
||||
args = append(args, "--as="+strconv.FormatUint(l.AddressSpaceBytes, 10))
|
||||
}
|
||||
if l.NProc > 0 {
|
||||
args = append(args, "--nproc="+strconv.FormatUint(l.NProc, 10))
|
||||
}
|
||||
if l.CPUSeconds > 0 {
|
||||
args = append(args, "--cpu="+strconv.FormatUint(l.CPUSeconds, 10))
|
||||
}
|
||||
if l.DiskBytes > 0 {
|
||||
args = append(args, "--fsize="+strconv.FormatUint(l.DiskBytes, 10))
|
||||
}
|
||||
return args
|
||||
}
|
||||
Reference in New Issue
Block a user