Documentation
¶
Overview ¶
本文件是进程托管层:Manager 负责进程启动(沙箱包装由 sandbox_*.go 提供)、 取消与期限(ctx)、输出分流、在跑进程登记与 Close 统一回收。
Native processes enforce an ordered file/network policy snapshot. Cwd grants no access. Unsupported restrictions fail before execution.
Package vbox 是原生子进程沙箱库:规则模型 + 纯判定 + OS 落地(Seatbelt / bubblewrap / 受限令牌 + ACL)+ 进程生命周期。
它只做三件事:
- 规则模型:FS 与 Net 各一张有序表,按 level + 原表序求值:高 level 优先, 同 level 首命中生效。行首可用 ! 增加 level,随后是效果前缀:文件域 ro:/rw:/deny:,网络域 allow:/deny:(host:port)。Class 只用于调用方拼接, 不推导 level,也不参与判定。
- 判定:纯函数匹配(MatchPath / Match / MatchNoFollow / NetRuleSet.Resolve), 不访问宿主文件系统,也不读配置。
- 落地:把编译好的快照交给内核机制执行;当前平台无法表达的规则在启动前拒绝 (fail-closed),绝不静默降级为裸跑。RunProcess 是唯一进程入口。
配置解析、默认规则源、会话授权、grant 生命周期与审批策略都属调用方(宿主集成层); vbox 每次调用接收一份编译好的快照,不维护授权状态。Windows 侧仅为拒绝 ACE 的 幂等对账保留一份状态文件(默认 `{UserConfigDir}/vbox/sandbox/deny_acl_state.json`, 可用 Manager.SetStateDir 改)。
Index ¶
- func Canonical(p string) string
- func CanonicalNetHost(host string) string
- func CanonicalNoFollow(p string) string
- func CanonicalPattern(pat string) string
- func CheckFSGrantTarget(string) error
- func DedupClean(roots []string) []string
- func DualForms(p string) []string
- func DualList(paths []string) []string
- func EntryMatch(r, q Entry) bool
- func ExpandVars(s string) (string, bool)
- func HostPathToOS(p string) string
- func IneffectiveRows(rules []Rule) []int
- func IsUnsupported(err error) bool
- func JoinPattern(root, sub string) string
- func MatchPattern(pattern, cpath string) bool
- func NormalizeHost(host string) string
- func ScrubEnv(env []string) []string
- func SetSysProcAttr(cmd *exec.Cmd)
- func ValidateEntries(list []string) error
- func ValidateFSGrantTarget(abs string) error
- func ValidateFSRules(list []string) error
- func ValidateTargetRules(list []string) error
- func ValidateUpstreamProxy(raw string) error
- type Class
- type Decision
- type DeniedError
- type Effect
- type Entry
- type FSRuleSet
- type FileOp
- type Manager
- type NetCaps
- type NetRowLanding
- type NetRule
- type NetRuleSet
- type Policy
- type Resolution
- type Rule
- type StartOptions
- type UnsupportedError
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Canonical ¶
Canonical 展开符号链接到真实文件系统身份:EvalSymlinks 逐级向父目录回退 (目标不存在时展开最近存在祖先,剩余路径原样拼接——写新文件场景)。 输出为 /c/ 规范形(posix 为 posix 形)。 递归到文件系统根基(/ 或 C:\)时直接返回原路径:根基无可展开项,且 TrimSuffix 根基分隔符得空串/盘符,继续递归会退化成相对路径。
func CanonicalNetHost ¶ added in v0.3.4
CanonicalNetHost 归一化主机名,并把 localhost、.localhost 子名和回环 IP 归入同一个 loopback 类。规则模式 *.localhost 保留为该类的通配形式。
func CanonicalNoFollow ¶
CanonicalNoFollow 展开父目录符号链接、保留末段字面形(unlink/rename 语义: 系统调用作用于链接本身,判定必须同口径)。父链复用 Canonical(含最近存在 祖先回退);末段为根/退化形态时退回跟随版。输出同为 /c/ 规范形。
func CanonicalPattern ¶
CanonicalPattern 对 glob 模式的字面前缀段(首个含通配符段之前)做 canonical 展开:/var/tmp/x/** → /private/var/tmp/x/**(macOS /var symlink)。 纯字面模式(无通配符):整串展开;** 开头的模式无前缀可展开(原样返回)。
func CheckFSGrantTarget ¶ added in v0.3.1
CheckFSGrantTarget(linux):恒 nil——bwrap 在挂载层落实可写根(bindIfExists), 不存在「能不能授上写权限」这一前置条件;不存在的根本就被静默跳过。
已知不对称:unix 上给一个当前用户不可写的目录 grant fs 同样是空授权(工具 层与子进程都写不了),但判据与 Windows 不同——unix 上不存在「能改 ACL 就能 在沙箱内写」的等价权限,且「目录不可写、其中文件可写」的授权仍有意义 (改文件内容不需要目录写权限),故不在此做硬判定。
func DualForms ¶
DualForms 返回路径的 canonical 形与原始字面形(去尾斜杠;相同则只给一个)。 沙箱(seatbelt)按系统调用实际传入的路径串匹配规则:macOS 的 /tmp、/etc、 $TMPDIR(/var/folders/…) 都是 symlink 前缀,只留 canonical 形会让字面拼写的 访问(含路径解析的 metadata 读)被拒(2026-09-22)。
func ExpandVars ¶
ExpandVars 展开模式中的 ~(~ 与 ~/ 前缀)与环境变量:$VAR/${VAR}(unix)、 %VAR%(windows 形态,跨平台都展开——用户 cfg 条目可能引用任一形态)、特殊记号 $UserConfigDir(Go 的 os.UserConfigDir,无对应环境变量)。 任一变量的展开失败(未定义变量、无家目录)使整条模式失配(ok=false)—— 初始名单已按平台分表,此规则只防御用户 cfg 条目(写了本平台不存在的变量时宁缺毋滥)。 例外:HOME 与 ~ 同义(unix 惯例变量,Windows 原生只有 USERPROFILE,GUI/schtasks 启动的进程没有 HOME)——缺失/为空时回退 os.UserHomeDir(),否则内置规则表在 Windows 上整表编译失败(2026-10-04 win 实机:rule N: cannot expand "$HOME/<内置条目>/**")。
func HostPathToOS ¶
hostPathToOS 把 /c/ 规范形(及旧输入形)转回 OS 原生路径(windows;posix 恒等——"C:" 是合法相对文件名,不做盘符归一)。windows 上 /tmp 是虚拟别名: cygwin 式映射 os.TempDir()(与宿主路径口径同源—— /tmp 查询 canonical 后落进临时目录便利根,无需字面 /tmp 规则行)。 导出供沙箱名单派生(OS 边界单一转换点)与嵌入方使用。
func IneffectiveRows ¶
IneffectiveRows 返回【永不生效】的行下标:一条更高优先级的 rw 行覆盖 deny/ro 行的全部匹配路径时,后者永远轮不到求值。优先级为高 level 在前, 同 level 才比较原表序。最常见的踩法是把 deny 写在 rw 之后——执行端按优先级放行(Windows 只留一句 "deny ACE skip rw-hole" 日志),使用者很难察觉自己的 deny 形同虚设。
判定保守(宁可漏报不误报),只在能静态确定包含关系时报告:
- 两行模式相同;
- 外层以 /** 结尾:内层等于其前缀或落在其下(** 含零段,故含根目录);
- 外层无通配元字符 `* ?`(裸路径覆盖子树):内层落在其子树下。
外层模式自身含通配元字符且非 /** 尾形态时不判定(包含关系不可静态确定)。 编译不因这些行失败——整表仍合法,只是内层永不命中;调用方可在装配配置时 用它提示或拒绝(见 README「规则表」)。
func IsUnsupported ¶ added in v0.3.4
IsUnsupported reports whether err describes a backend capability gap.
func JoinPattern ¶
JoinPattern 拼接「根 + 子模式」(根为 "/" 时不产生 "//"——matchPattern 按 / 分段, 双斜杠会引入空段使模式整体失配)。空 root 返回空串(调用方丢弃)—— 2026-09-22:GOCACHE/XDG_CACHE_HOME 未设置 → 空缓存根曾把名单退化成全匹配。
func MatchPattern ¶
MatchPattern 判定 canonical 路径是否命中 glob 模式(自 fsauth match.go 移植, 语义不变):
- 支持跨段 `**`(匹配零或多段);段内 `*`(任意非分隔符字符)、`?`(单字符); `[` 按字面匹配(不支持字符类;与 CanonicalPattern 的字面前缀检测集同口径);
- 裸路径模式(无通配符)覆盖子树:pattern == path 或 path 在 pattern/ 之下;
- 斜杠语义:模式与路径统一 filepath.ToSlash(windows 反斜杠归一);
- 大小写按平台折叠:darwin/windows 文件系统默认不敏感 → fold,linux 敏感;
- 与 rg 的 glob 语义独立实现、不共代码(rg 明确不支持 `**`——两者勿混用)。
func NormalizeHost ¶
NormalizeHost 归一化主机名:小写、去尾点、IP 经 netip 归一。
func SetSysProcAttr ¶
SetSysProcAttr 设置子进程属性:独立进程组(killEntry 按进程组终止)。
func ValidateEntries ¶
ValidateEntries 批量校验具体目标(不接受通配或 level)。
func ValidateFSGrantTarget ¶
ValidateFSGrantTarget 校验 grant fs 目标(具体绝对路径,非模式)—— 与规则行同护栏:全域/家根/盘根不可授。
func ValidateFSRules ¶
ValidateFSRules validates the complete compilation, including variable expansion.
func ValidateTargetRules ¶
ValidateTargetRules 批量校验完整规则行(加载/入表即报错)。
func ValidateUpstreamProxy ¶ added in v0.3.4
ValidateUpstreamProxy reports whether raw is a supported explicit upstream proxy URL.
Types ¶
type Class ¶
type Class uint8
Class 规则行类别——仅参与调用方的行序拼接(§9.10:temp → cfg/permanent → builtin deny → 便利根),判定时不看。
type Decision ¶
type Decision struct {
Allow bool // 本操作是否放行
Matched int // 命中行序号(0 起);-1 = 未命中(兜底生效)
Effect Effect // 生效效果(命中行效果或兜底)
Level uint8 // 命中行 level;兜底为 0
// Reason 动态行(Rule.Resolve)或快照错误给出的说明(拒绝原因等);
// 普通静态命中行与兜底判定的 Reason 为空,调用方自建文案。
Reason string
}
Decision 判定结果。
type DeniedError ¶
type DeniedError struct{ Reason string }
DeniedError 策略明确拒绝(对应协议层 state=rejected):沙箱无法落实规则时 启动前拒绝执行(fail-closed),不产生裸跑。调用方按类型识别以保持拒绝语义。
func (*DeniedError) Error ¶
func (e *DeniedError) Error() string
type Effect ¶
type Effect uint8
Effect 规则行效果。
func ParseEffect ¶
ParseEffect 解析效果词(deny/ro/rw),非法返回 ok=false。
func ParseFSRule ¶
ParseFSRule 解析一条 fs 规则行:剥取行首 level(!)与效果前缀 (deny:/ro:/rw:),再执行全域模式禁写护栏。level 超过 255 直接报错。
type Entry ¶
Entry 归一化目标条目(Host 小写/IP/loopback 归一,Port 数字串或 *)。 规则模式侧允许最左侧完整标签通配(*.example.com)。
func ParseEntry ¶
ParseEntry 解析一个具体目标:禁止通配、level、空白与控制字符。 bare host 的端口为 *;user@ 前缀用于 SSH 目标。多冒号形态必须是合法 IPv6。
func ParseRuleEntry ¶ added in v0.3.4
ParseRuleEntry 解析权限表中的目标模式。只有最左侧完整标签可以是 *; 不接受 level 或效果前缀,那些由 ParseNetRule 处理。
func ParseTargetRule ¶
ParseTargetRule 解析具体目标规则,不接受 level 或通配。
type FSRuleSet ¶
type FSRuleSet struct {
Rules []Rule // 原表序在同 level 内保留;Class 不改变优先级
DefaultWrite Effect // 未命中写兜底(EffDeny | EffRW = fs_policy);读默认开放
// Err 是快照来源的配置错误。非 nil 时读写都 fail-closed,且原生进程
// 必须在任何 OS 规则落地前拒绝启动;不能用空规则代替这个状态。
Err error
}
FSRuleSet 文件规则集:高 level 优先,同 level 按原表序首命中。
func NewFSRuleSet ¶
NewFSRuleSet 构造规则集并对每行模式做 CanonicalPattern 归一(符号链接前缀 展开)。直接字面构造结构体时模式必须自行保证 canonical——查询路径在 Match 入口被 canonical 化,模式若不是 canonical,在 macOS /home(firmlink)、/tmp 等 symlink 前缀下两侧错位(规则形同虚设)。grant/cfg 来源的行经 CompileFSRule 编译已含此归一;程序化构造的表请用本函数。
func (FSRuleSet) MatchNoFollow ¶
MatchNoFollow 是 unlink/rename 语义的判定入口:末段符号链接不跟随(rm/mv 作用于链接本身,不触达目标),父链展开、规则表判定不变。rm/mv 类操作必须 走此入口——否则可写根内的外向链接会被无关目标路径的策略拒掉 (2026-09-22 实测 .venv 删除被阻的修复,迁移不得丢)。
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
func NewManager ¶
func NewManager() *Manager
func (*Manager) RunProcess ¶
func (m *Manager) RunProcess(ctx context.Context, opts StartOptions, stdin io.Reader, stdout, stderr io.Writer) (int, error)
RunProcess runs exactly one child process under the caller's context. stdio 全接通:stdin 供管道/载荷输入,stdout/stderr 分流写入各自 writer (双流日志契约——stderr 不混入 stdout;nil 表示丢弃对应流)。 运行期限与取消全部由 ctx 承担(exec 外层墙钟/取消句柄)。
func (*Manager) SetNoSandbox ¶
func (*Manager) SetStateDir ¶
SetStateDir 指定沙箱持久状态目录(Windows 的 deny ACE 对账状态文件所在目录; 默认 `{UserConfigDir}/vbox/sandbox`)。该状态是机器级的(同一台机器只有一份 ACE 对账记录),进程内多次设置以后者为准;只影响之后的对账写入与读取。
type NetCaps ¶ added in v0.3.4
NetCaps describes the network enforcement capabilities of one backend.
type NetRowLanding ¶ added in v0.3.2
type NetRowLanding struct {
Rule NetRule
Kernel bool // 由内核逐条规则落实
Proxy bool // 由出站代理按完整快照落实
Absorbed bool // 死行、冗余行,或省略后只会收窄的规则
Reserved bool // 内部 transport 保留项,不对应调用方规则
Reason string
}
NetRowLanding 描述一条原始规则在启动计划中的落地方式。
func CheckNetPolicy ¶ added in v0.3.2
func CheckNetPolicy(platform string, s NetRuleSet) ([]NetRowLanding, error)
CheckNetPolicy 用平台的真实 NetCaps 预检完整快照,返回与原始行序一致的 Kernel / Proxy / Absorbed / Reserved 诊断。语法错误与 UnsupportedError 分开; 一个仍然有效的 deny 无法落实时拒绝启动,allow 省略后只会收窄时可以吸收。
darwin 的 loopback 投影按 level + 原表序求每个具体端口及其他端口代表集的 真实胜者;所有胜者进入内核计划,非胜者吸收。静态预检和 Seatbelt 输出共用 compileNetPlan,不再把混合拼写或冲突本身当作错误。
type NetRule ¶
NetRule 网络规则行。HostPort 形如 "example.com:443",端口可 *。
func ParseNetRule ¶ added in v0.3.2
ParseNetRule 解析完整 net/ssh 规则:行首 ! 增加 level,随后是 allow:/deny: 与目标模式。level 最多 255;旧配置不做兼容解析。
type NetRuleSet ¶
type NetRuleSet struct {
Rules []NetRule
Default bool // 未命中兜底 = net_policy open/deny
// UpstreamProxy is the optional explicit upstream HTTP/SOCKS5 proxy URL.
// It is part of the policy snapshot; ambient process proxy variables are ignored.
UpstreamProxy string
}
NetRuleSet 网络规则集:高 level 优先,同 level 按原表序首命中。
func (NetRuleSet) Match ¶
func (s NetRuleSet) Match(hostport string) bool
Match 判定目标是否放行。非法目标或规则行均 fail-closed。 端口匹配取保守交集语义:规则为 * 或相等即命中;查询为 * 时重叠即命中。
type Policy ¶
type Policy struct {
FS FSRuleSet
Net NetRuleSet
}
Policy 是一次原生进程调用的完整权限快照。规则首命中生效; 配置、会话授权和默认规则由调用方组合,平台后端只负责落实。
type Resolution ¶ added in v0.3.0
type Resolution struct {
// Effect 生效效果。
Effect Effect
// Reason 可选:给调用方的说明(如拒绝原因)。判定不看它,只用于报错/
// 日志——静态行的说明由调用方自建,动态行的情形只有回调知道(例如
// 「公开包是发布物,不可写且 grant 不可授」与「本人私有包,写需 grant」
// 的区别)。
Reason string
// Handled false = 回调不表态,落回 Rule.Effect。
Handled bool
}
Resolution 是动态行回调(Rule.Resolve)的判定结果。
type Rule ¶
type Rule struct {
Pattern string
Effect Effect
Class Class
Level uint8 // 高 level 优先;同 level 保持原表序,Class 不推导 Level
// Resolve 可选:行命中时由回调按本次「路径 + 操作」现算效果,把「随目标
// 数量增长的一次性枚举行」换成「一行 + 按需判定」(如云 /skills/{id} 的
// 可见性:只查当前这一个 id,不枚举全库)。Handled=false = 回调不表态,
// 落回 Effect(静态部分仍可读,诊断/表渲染不受影响)。行序语义不变:前面
// 的行仍先命中、且命中本行时不再查后续行。
//
// 仅进程内匹配(Match/MatchPath/MatchNoFollow)调用回调;OS 级沙箱计划
// 只认静态 Effect,带 Resolve 的行交给计划生成会被显式拒绝
//(validateProcessPolicy)——动态判定无法下推到 seatbelt/bwrap/ACL。
Resolve func(path string, op FileOp) Resolution
}
Rule 规则行。Pattern:段内 *(任意非分隔符字符)、跨层 **(零或多段)、 ?(单字符);裸路径(无通配符)覆盖子树。Class 判定时不看。
type StartOptions ¶
type StartOptions struct {
Workdir string // 进程 cwd(空 = 继承),不授予目录权限
Exec []string // argv:Exec[0] = 程序名
// Argv0 changes argv[0] only. Confined Unix launchers reject overrides.
Argv0 *string
// Env 追加/覆盖环境变量(KEY=VALUE,同名以本字段为准):在沙箱 env
// 清洗之后应用——skill service 的 SKILLPROC_SOCKET 等协议变量经此
// 下发,保证沙箱内外都存活。ReplaceEnv=false 时 nil = 继承现状。
Env []string
// ReplaceEnv makes Env authoritative, including an intentionally empty environment.
ReplaceEnv bool
// RawOutput preserves bytes and write errors without line buffering or
// Windows code-page conversion. Protocol pipes and binary tools require it.
RawOutput bool
// NoSandbox 是免沙箱执行标记(契约见 aic-pod/docs/reference/host-sandbox.md),合法来源:
// - 内部管控调用方(ssh/scp、browser,由执行环境自身管控);
// - 外部请求显式携带 nosandbox(发送前审批;审批本身不免沙箱,
// 仅放行该标记);
// - 全局 no_sandbox 配置(Manager.NoSandbox)。
NoSandbox bool
Policy Policy // 文件与网络权限的唯一输入
}
type UnsupportedError ¶ added in v0.3.4
type UnsupportedError struct{ Reason string }
UnsupportedError reports a valid policy that the selected backend cannot enforce faithfully. It is distinct from a rule syntax error.
func (*UnsupportedError) Error ¶ added in v0.3.4
func (e *UnsupportedError) Error() string