sandbox

package
v0.5.1 Latest Latest
Warning

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

Go to latest
Published: Sep 7, 2026 License: Apache-2.0 Imports: 17 Imported by: 0

Documentation

Overview

Linux 后端编排:施加序与探测子进程(真源 §2.1/§1.3)。

capability SID 派生(windows 后端的平台无关部分,真源 §4):按可写根 路径确定性派生 S-1-4-x-y(dsh workspace-sid.ts 蓝本,subauthority 30-bit)。 确定性 = 跨会话/跨进程重启稳定——工作区根的 standing ACE 只需授予一次 (codex cap.rs 的随机 SID 方案要持久化状态文件,丢档即孤儿 ACE)。SID 字符串本身不是秘密:权力完全由命名它的 ACE 定义,且只在本进程铸造的 restricted token 里携带。

DockerProvider Docker 一次性容器后端(dockerWrap 正规化迁移——既有 EINO_RUN_DOCKER env 魔法开关与「绕过 policy」优先级告警退役,策略翻译 进容器参数)。形态定位 = 真源 §5.3 的过渡形态:一次性容器(--rm 每命令 一弹);终局 = §0.2/§5.3 的每会话长驻容器(Manus 形态——懒启动/会话终 销毁、只挂工作区、凭证在围栏外),届时本 Provider 的 Wrap 改实现为 docker exec、容器生命周期归 Provider 内部,接口形状不变。

策略翻译(只收紧不放宽):

Mode readonly         → --read-only + 工作区 :ro 挂载
Mode workspace-write  → --read-only(容器根只读)+ 工作区 :rw
Mode danger-full      → 容器根可写(隔离仍在——容器即边界)+ 工作区 :rw
Network=false         → --network none;true = 默认 bridge
WritableRoots         → 同路径 :rw 追加挂载(缓存类目录逃生门,与 OS 档同义)
ProtectedReadOnly     → 同路径 :ro 子挂载(嵌套 bind 遮蔽——docker 按路径
                        深度解析覆盖顺序,Landlock 做不到的回盖此处可治)
Env                   → -e 逐条注入;容器面天然最小环境(不继承宿主 env,
                        EnvMode 对本后端无操作面)
Limit.NProc           → --pids-limit(FileSizeMB 无 docker 等价面,不翻译)

已知缝隙(真源 §5.3 缝隙②,随终局长驻形态治):docker run attached 形态 下杀 docker CLI 进程不终止容器——run_command 后台任务的 task_stop 只杀 CLI,容器残留至自身退出(--rm 缓解一次性,长任务仍有窗口)。

降级语义(审查 A-3 明示):daemon 不可达 → nil argv,调用方按姿态降级 (当前接线 = auto:命令裸跑于宿主进程环境,容器隔离失效——启动告警已 如实声明;fail-closed 拒跑属 require 姿态接线,真源 §10.4)。

re-exec 哨兵协议(真源 §2.1):run_command 侧以 [<exe>, "__einox-sandbox", "--", "sh", "-c", <cmd>] re-exec 产品自身,产品 main() 顶部挂 RunHelper 拦截哨兵子命令——helper 路径内施加策略(LockOSThread → NO_NEW_PRIVS → seccomp → Landlock → rlimit)后 syscall.Exec 真实命令。策略 JSON 经 env 传递(不污染 ps 输出);exe 路径 init 期 os.Executable() 固化(审查 C2: os.Args[0] 相对形态在子进程 cwd 下解析落空)。依赖产品 main 挂钩是本机制 唯一侵入点(一行)——未挂钩时探测报 unusable 而非静默裸跑(审查 C1)。

Landlock 文件系统围栏——dsh native/landlock-run main.c(C11)的 Go 直译 (第一参照)。UAPI 经 x/sys/unix(syscall 号/访问位常量 = 内核稳定契约, 不依赖内核头),属性结构自携最小形并显式传内核 size——x/sys 结构含 ABI4/6 字段(24 字节),旧内核自身 struct 只有 8 字节时 size 超限直接 E2BIG。规则只能授不能收(allow-list 联合语义):可写根内只读回盖 (ProtectedReadOnly,如 .git)做不到——探测面如实报 partial + 未覆盖项 (真源 §2.2)。

进程组治理(沙箱形态进程组杀的前提——修 stopTask 只杀单进程瑕疵的 沙箱路径部分,真源 §2.1)。

rlimit 资源限额(helper 内 exec 前 setrlimit,真源 §2.4):NPROC 默认 512 ——内核按有效 uid 计数且含线程,容器形态下服务进程 Go 运行时线程与同 uid 任务共享同一预算,256 会被「服务高线程 + go build -p 并行 fork」打穿 且报 EAGAIN 不命中拒绝签名;FSIZE 默认 1GB 防写满挂载卷;CORE=0。跳过 RLIMIT_AS——Go 工具链运行时/链接器 GB 级虚拟地址预留会被误杀,内存硬 限额归 cgroup/容器层。

Package sandbox 提供 run_command 执行面的 OS 级沙箱机制(Phase S): 策略模型(三档/网络开关/可写根/限额,平台无关)+ re-exec 哨兵协议 (Landlock/seccomp/rlimit 必须 fork 后 exec 前在子进程内施加,Go self re-exec 惯例,codex helper 同构)+ 后端探测三态如实上报(dsh 式)。 机制归基座、选择归业务:应用装配层决定用不用/用哪档/参数(PM 默认关 opt-in),基座承载实现与安全不变量。设计 = 2026-08-26 沙箱设计定案 (含同日独立审查修订)。

Seatbelt profile 纯构造(darwin 后端的平台无关部分,真源 §3):组装 顺序对齐 codex seatbelt.rs——base(deny default 起手)→ file-read 段 → file-write 段 → 网络段 → 保护子路径 require-not 排除 → 锚点 deny。路径 全部经 -D KEY=VALUE 参数注入、profile 内 (param "KEY") 引用——路径不拼进 policy 字符串(转义/注入面,codex 同款纪律)。本文件零 fs 调用(纯字符串 构造,任意平台可单测);路径规范化(嵌套 symlink 拒绝/顶层别名解析)在 seatbelt_darwin.go 运行时侧。

seccomp BPF 断网围栏(手写经典 BPF,x/sys/unix 原语)。deny 清单逐条照抄 codex linux-sandbox/landlock.rs L179-216:recvfrom 故意放行(socketpair+ 子进程管理的工具兼容性,codex 注释原话照搬);connect 无条件 deny 连 AF_UNIX 也断——防 docker.sock 类逃逸的故意设计(codex 同款,真源 §2.3 D3 披露:docker CLI / psql -h /sock 类本地 socket 客户端同断)。 default allow + 命中返回 EPERM;audit arch 不符(32 位兼容模式号表错位) KILL_PROCESS fail-closed。号表仅 x86_64/aarch64(x/sys per-arch 常量), 其余架构由 backend_stub 运行时桩报 unusable(审查 B2)。

Linux 分支 Wrap 装配:re-exec 哨兵形(真源 §2.1)。arch 桩平台同样编入 本文件(不可达——unsupported-arch 探测 unusable 已在 Wrap 短路)。

Index

Constants

View Source
const (
	DefaultNProc      = 512
	DefaultFileSizeMB = 1024
)

默认限额。

Variables

This section is empty.

Functions

func ArgvEnv

func ArgvEnv(pol *Policy, workspace, cmdLine string) (argv []string, env []string)

ArgvEnv 纯构造(不探测——argv/env 形态测试与特殊装配用)。

func AttachToken

func AttachToken(*exec.Cmd, *Policy, string) error

AttachToken unix 平台 no-op(windows 专用侧挂:restricted token 进 SysProcAttr;unix 侧进程属性经 SetGroupLeader 完成)。

func DenialHint

func DenialHint(output string) string

DenialHint 命中沙箱拒绝签名时返回提示行(空 = 未命中)。仅沙箱生效的 执行路径调用(裸跑降级路径命令输出与沙箱无关,不标注)。

func KillGroup

func KillGroup(p *os.Process)

KillGroup 进程组杀(负 pid SIGKILL 整组终结——后台任务/超时通道同款)。 未组化(同调用方进程组,负 pid 无组可寻 ESRCH)或已死则回退单进程杀。

func RunHelper

func RunHelper(args []string)

RunHelper 产品 main() 顶部挂接:命中哨兵子命令进入 helper 路径永不返回, 未命中原样返回(正常产品流程)。

func SetGroupLeader

func SetGroupLeader(cmd *exec.Cmd)

SetGroupLeader 设置进程组组长(子进程独立进程组——组杀锚点)。

func WorkspaceWriteSID

func WorkspaceWriteSID(root string) string

WorkspaceWriteSID 可写根 → 确定性 capability SID(S-1-4-x-y)。

func Wrap

func Wrap(pol *Policy, workspace, cmdLine string) (argv []string, env []string)

Wrap 探测后构造沙箱化执行参数(OSProvider 的包级出口——应用直用 runcommand/sandbox 不经 engine 时的既有入口,行为不变)。

Types

type Backend

type Backend string

Backend 后端姿态(探测不可用时的处置,真源 §1.3)。

const (
	BackendOff     Backend = "off"     // 不沙箱(Policy=nil 即此态,PM 默认)
	BackendAuto    Backend = "auto"    // 探测落位;不可用裸跑+告警(不隐式 fail-closed)
	BackendRequire Backend = "require" // 探测不可用即拒跑(fail-closed)
)

type DockerProvider added in v0.2.0

type DockerProvider struct {
	Image string
	// contains filtered or unexported fields
}

DockerProvider Docker 一次性容器后端(Image 空 = alpine:3.20,与既有 dockerWrap 缺省一致)。零值可用;探测进程级缓存一次。

func (*DockerProvider) Probe added in v0.2.0

func (d *DockerProvider) Probe() Status

Probe docker CLI/daemon 可达性探测(进程级一次缓存;daemon 不可达 = unusable——Wrap 返回 nil argv,调用方按姿态降级:当前接线 = auto 裸跑 + 本告警;fail-closed 拒跑属 require 姿态,接线随首个消费者,真源 §10.4)。

func (*DockerProvider) Wrap added in v0.2.0

func (d *DockerProvider) Wrap(pol *Policy, workspace, cmdLine string) ([]string, []string)

Wrap 一次性容器执行参数(daemon 不可达 → nil argv,调用方按姿态降级—— 与 OSProvider 同款 auto 裸跑语义,容器隔离失效面如实告警)。env 返回 nil(CLI 进程继承宿主环境——DOCKER_HOST/凭证面服务于 daemon 通信,容器 内环境不继承宿主、只收 -e 显式注入)。

type Enforcement

type Enforcement string

Enforcement 执行力三态(真源 §1.2,dsh 式如实上报)。

const (
	EnforcementFull     Enforcement = "full"     // 目标策略全量生效
	EnforcementPartial  Enforcement = "partial"  // 生效但有未覆盖项(旧 ABI 少位等)
	EnforcementUnusable Enforcement = "unusable" // 后端不可用(未挂钩/内核不支持/平台未实现)
)

type EnvMode added in v0.2.0

type EnvMode string

EnvMode 围栏内环境继承档(凭据外泄面治理:inherit 是 denylist——记住要剥 谁,对未知凭据名结构性失效;minimal 是 allowlist——业务所需环境一律经 Policy.Env 显式注入,缓存重定向既有惯例不变)。

const (
	EnvInherit EnvMode = ""        // 缺省零值 = 现状:全继承(− LLM_* − 策略载荷)
	EnvMinimal EnvMode = "minimal" // 白名单:PATH/HOME/TMPDIR(windows 另含 TEMP/SystemRoot 等基础键)+ Policy.Env
)

type Limit

type Limit struct {
	NProc      int // RLIMIT_NPROC;0 = 默认 512(内核按有效 uid 计数含线程,256 会被服务高线程+go build -p 打穿)
	FileSizeMB int // RLIMIT_FSIZE;0 = 默认 1024;单文件写上限,大产物场景调大
}

Limit 资源限额(0 = 默认;helper 内 exec 前 setrlimit,真源 §2.4)。

type Mode

type Mode string

Mode 策略三档。

const (
	ModeReadOnly         Mode = "read-only"          // 全盘只读
	ModeWorkspaceWrite   Mode = "workspace-write"    // 全盘读 + 工作区/临时目录/WritableRoots 写
	ModeDangerFullAccess Mode = "danger-full-access" // 全盘读写(仅资源限额与可选断网生效)
)

type Policy

type Policy struct {
	Mode              Mode     // 三档
	Network           bool     // 默认 false 断网;LLM 调用在服务进程内不受影响
	WritableRoots     []string // workspace-write 档追加可写根(HOME 缓存类目录逃生门)
	Env               []string // 附加环境变量 "K=V"(缓存重定向载体,注入点 = cmd.Env)
	EnvMode           EnvMode  // 环境继承档(缺省 inherit 零行为变化;minimal = 白名单)
	ExcludeTmpdir     bool     // true = $TMPDIR 不计入可写根(默认 false = 计入,审查 A1)
	ExcludeSlashTmp   bool     // true = /tmp 不计入可写根(默认 false = 计入)
	ProtectedReadOnly []string // 可写区内希望只读的子路径(Landlock 做不到——探测报 partial)
	Limit             Limit
}

Policy 沙箱策略(对齐 codex legacy SandboxPolicy 形状 + 缓存重定向载体, 审查 A2 修订)。静态一次装配(真源 §7.5——PM 会话工作区固定,per-call 留演进)。

func (*Policy) Validate

func (p *Policy) Validate() error

Validate 构造期 fail-fast(模式名唯一硬校验——其余字段语义自洽)。

type Provider added in v0.2.0

type Provider interface {
	// Wrap 一次命令的沙箱化执行参数(argv[0] 为可执行名,PATH 解析归调用方)。
	Wrap(pol *Policy, workspace, cmdLine string) (argv, env []string)
	// Probe 后端可用性(缓存策略归实现自担;OSProvider 维持进程级一次)。
	Probe() Status
}

Provider 沙箱后端面:把 Policy 翻译成一次命令的执行参数(默认 OSProvider = 平台内建,构建标签分流;应用注入容器/gVisor 等自定义后端—— engine.Options.SandboxProvider / runcommand.Config.SandboxProvider)。 名称不叫 Backend:该名已被姿态类型占用(上方 off/auto/require,真源 §1.3)。

义务:①Probe 如实上报三态(Enforcement 语义,dsh 哲学),策略映射不全时 报 partial + Uncovered;②只许收紧不许放宽——宽于 Policy 的翻译即后端实现 违约(readonly 档不得给出可写挂载);③不修改 pol(并发共享的静态单份)。

nil argv = 本次不可沙箱(调用方按姿态降级:auto 裸跑已告警;require 拒跑 接线留待首个 fail-closed 消费者,真源 §10.4)。容器类后端形态兼容:一次性 容器 Wrap 即 docker run;真源 §0.2/§5.3 的每会话长驻终局 = Wrap 实现 docker exec、容器生命周期归 Provider 内部,接口形状不变。

var OSProvider Provider = osProvider{}

OSProvider 平台内建后端(默认 Provider:Linux Landlock+seccomp+rlimit / darwin Seatbelt / windows restricted token,构建标签分流——wrapOSBackend 平台实现)。ProtectedReadOnly 告警在 Linux 分支发——Landlock 做不到可写区 内回盖(审查 B-2),darwin/windows 后端可真回盖(require-not 排除 / deny ACE),不发该告警。

type Status

type Status struct {
	Enforcement Enforcement
	Uncovered   []string // 如 protected-readonly/refer/truncate/ioctl-dev
	Detail      string   // 人类可读诊断(含不可用原因)
}

Status 后端探测结果:三态 + partial 时的未覆盖项清单 + 诊断说明。

func Probe

func Probe() Status

Probe OSProvider 的包级出口(既有入口,行为不变)。

Jump to

Keyboard shortcuts

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