资讯动态

NonoSandboxProvider 设计解读:用 Landlock 与 Seatbelt 为 AI Agent 构建内核级能力沙箱

发布时间:2026/9/19 0:25:18 来源:尧图企业网站定制
NonoSandboxProvider 设计解读用 Landlock 与 Seatbelt 为 AI Agent 构建内核级能力沙箱【免费下载链接】agent-governance-toolkitAI Agent Governance Toolkit — Policy enforcement, zero-trust identity, execution sandboxing, and reliability engineering for autonomous AI agents. Covers 10/10 OWASP Agentic Top 10.项目地址: https://gitcode.com/GitHub_Trending/ag/agent-governance-toolkit本篇技术指南以 NONO-SANDBOX-PROVIDER 设计提案 为核心骨架结合 Agent Governance Toolkit 仓库中agent-sandbox的源码、配置与测试完整剖析NonoSandboxProvider的设计理念与实现细节。读完你将掌握nono 能力沙箱的一次性执行模型如何满足会话式SandboxProvider契约、NonoConfig.from_sandbox_config如何映射超时/挂载/环境/网络控制、fail-closed 出口网络策略的强制逻辑以及运行时治理与静态代码扫描如何在sandboxed_exec之前完成拦截。NonoSandboxProvider是 Agent Governance Toolkit 中agent-sandbox模块agent-governance-python/agent-sandbox提供的五种可互换沙箱后端之一。它不依赖 daemon、hypervisor SDK、云账号或外部二进制而是直接驱动nono-py——一个基于 OS 原生内核原语Linux 的Landlock、macOS 的Seatbelt实现的能力沙箱库。本文将从设计提案出发逐层拆解其配置契约、能力集构建、网络代理、治理拦截与执行生命周期并用仓库内的单元测试与集成测试验证每个关键行为。一、设计概览能力沙箱与一次性执行1.1 内核原语与 nono-py 绑定提案开篇即定义了NonoSandboxProvider的技术路线通过nono-py使用 LandlockLinux和 SeatbeltmacOS实施沙箱每次执行都运行在一个全新的子进程中并携带一份显式声明的能力集capability set。从源码看nono-py暴露的底层原语被明确记录在 config.py 的模块 docstring 中nono_py.CapabilitySet文件系统授权读写模式 网络模式nono_py.start_proxy过滤型网络代理nono_py.sandboxed_exec一次性原语——fork→ 在子进程内套用沙箱 →exec命令 → 在未沙箱化的父进程中捕获 stdio。值得注意的是该提供程序对nono_py采用懒加载策略provider.py 在模块加载时用try/except ImportError将_nono_module置为None因此导入NonoSandboxProvider本身不需要nono-py已安装只有构造实例并查询is_available()时才会真正探测依赖与宿主支持情况。这一点在__init__.py的 docstring 与测试test_missing_nono_py_does_not_break_import中都有印证。1.2 会话模型会话即“持久化 Bundle”nono 的sandboxed_exec是**一次性one-shot**的fork、套沙箱、运行命令、子进程退出——没有 Docker 容器或 Hyperlight 微 VM 那种跨调用存活的常驻 guest。为了满足SandboxProvider抽象基类sandbox_provider.py的会话式契约实现将会话视为一个可持久化的 bundle见 provider.py解析后的NonoConfig策略评估器policy evaluator即可选的AgentControl原生治理会话每会话独立的工作目录 workspace可选的常驻网络代理 proxy。每次execute_code()/run()都基于该 bundle fork 一个全新的一次性沙箱。因此跨执行不保留 guest 状态唯一例外是会话的读写output/目录——它在宿主机上于多次调用之间保留代理在会话间共享并在destroy_session时关闭。_Session类provider.py正是这一 bundle 的数据结构用__slots__声明了config、evaluator、workspace、interpreter、proxy五个字段。二、配置契约NonoConfig 与 SandboxConfig 的映射2.1 NonoConfig 字段一览NonoConfig是一个类型化、零依赖的数据类config.py建模了 provider 关注的 nono 能力面。字段及其语义如下表字段类型默认值语义readonly_pathslist[str][]授予沙箱只读的宿主目录映射caps.allow_path(p, READ)readwrite_pathslist[str][]授予沙箱读写的宿主目录映射caps.allow_path(p, READ_WRITE)allow_outboundboolFalse是否允许出站网络True时启动 nono 过滤代理并绑定沙箱False时调用block_network()allowed_hostslist[str][]由代理强制执行的出站主机白名单仅在allow_outboundTrue时有意义allow_unrestricted_egressboolFalse显式选择无限制出站allow_outboundTrue且allowed_hosts为空映射代理allow_all_hostsTruetimeout_secondsfloat60.0传给sandboxed_exec的timeout_secs墙钟执行预算env_varsdict[str, str]{}暴露给沙箱进程的环境变量使用前经sanitize_env_vars清洗include_system_pathsboolTrue是否授予标准系统目录读权限见default_system_paths使解释器可加载__post_init__强制两条校验config.pytimeout_seconds必须为正数_check_egress()强制执行下文所述的 fail-closed 出口契约。对应测试test_nonpositive_timeout_rejected、test_outbound_without_hosts_rejected验证了这两个拒绝分支。2.2 from_sandbox_config 的映射规则提案指出“NonoConfig.from_sandbox_config映射 timeout、mounts、environment 和 network controls”。对照实现config.py映射逻辑为挂载mountsSandboxConfig.input_dir→readonly_paths只读SandboxConfig.output_dir→readwrite_paths读写两者在挂载前都会经validate_mount_path校验拒绝指向受保护系统目录如/usr。网络networknetwork_enabledTrue且network_allowlist非空 →allow_outboundTrueallowed_hostsnetwork_enabledTrue但 allowlist 为空且network_defaultallow→allow_unrestricted_egressTrue其余情况保持allow_outboundFalse。超时timeouttimeout_seconds直接透传并取max(0.001, ...)兜底。环境environmentenv_vars原样带入后续由sanitized_env()清洗。测试test_from_sandbox_config、test_mounts_and_egress、test_network_default_allow_enables_unrestricted、test_network_allowlist_takes_precedence_over_default等test_nono_sandbox.py逐一覆盖了这些映射与优先级规则。2.3 两个“明确拒绝”tool_allowlist 与资源限制提案强调了两条关键的 fail-closed 边界Nono 没有工具注册通道非空tool_allowlist直接拒绝。在 provider.py 的create_session中若base_cfg.tool_allowlist非空立即抛出ValueError(nono does not support tool allowlisting ...)而不是静默忽略——这与 MXC provider 的行为一致但与支持 in-sandbox 工具注册的 Hyperlight 形成对比见 README.md 的“Host configuration and native governance”表格。空列表则放行test_empty_tool_allowlist_is_allowed。CPU 与内存控制由操作系统负责。SandboxConfig中的memory_mb、cpu_limit在 nono 中不可表达from_sandbox_config直接丢弃交由 OS 治理docstring 明确注明。SandboxConfig的默认值本身为memory_mb512、cpu_limit1.0sandbox_provider.py但在 nono 路径下不产生任何实际约束。三、出口网络控制默认阻断、白名单代理、显式全开网络是 Agent 沙箱最敏感的边界之一。提案的核心承诺是网络访问默认被阻断过滤出口通过带allowed_hosts的代理实现无限制出口必须显式 default-allow。3.1 三态网络模型NonoConfig._check_egress()config.py将出口策略固化为三种状态状态条件代理配置能力绑定完全阻断allow_outboundFalse不启动代理caps.block_network()过滤出口allow_outboundTrue且allowed_hosts非空ProxyConfig(allowed_hosts[...])caps.proxy_only(proxy)无限制出口allow_outboundTrueallow_unrestricted_egressTrue显式ProxyConfig(allow_all_hostsTrue)caps.proxy_only(proxy)其中“过滤出口”是 fail-closed 的核心allow_outboundTrue时若allowed_hosts为空且未显式设置allow_unrestricted_egress配置构造直接抛ValueError——绝不允许隐式地“可达任意主机”。错误消息甚至给出了通过策略defaults.network_default: allow显式开启的途径。3.2 代理的生命周期代理的启动与关闭逻辑在 provider 中成对出现启动_start_proxy()provider.py根据 egress 策略构造ProxyConfigcreate_session中若allow_outbound为真则启动代理失败时清理 workspace 并抛RuntimeError。绑定_build_caps()中有代理时调用caps.proxy_only(session.proxy)否则caps.block_network()provider.py。环境注入_build_env()将代理的sandbox_env含HTTP_PROXY等合并进子进程环境使流量经代理路由provider.py。关闭destroy_session()在移除 workspace之前先关闭代理避免后台线程存活超出会话provider.py。测试test_network_blocked_by_default、test_proxy_started_for_egress、test_unrestricted_egress_uses_allow_all_hosts、test_proxy_shut_down_on_destroy完整验证了这一生命周期包括代理环境下HTTP_PROXY被写入沙箱环境。四、原生治理deny 必须先于 sandboxed_exec提案“Native governance”一节指出可选的AgentControl被包装在HostSession中运行时runtime与静态扫描static-scan的拒绝都发生在sandboxed_exec之前。这意味着策略拒绝根本不会触达 nono 沙箱。4.1 HostSession 包装_build_runtime_session()provider.py从agent_control_specification导入HostSession将调用方传入的runtimeACS 原生治理运行时与agent_id、session_id一并包装。create_session中仅在runtime is not None时才构建 evaluator。4.2 执行前的两道闸门execute_code()provider.py在 fork 任何沙箱之前依次执行治理门governance gate构造{agent_id, action: execute, code, ...}上下文调用evaluator.pre_tool_call(tool_namesandbox_execute, ...)。若决策为 transform 判决applies_transform直接抛PermissionError——因为沙箱无法改写即将执行的代码视为拒绝而非放行若permitsFalse同样抛PermissionError携带decision.message或reason。测试test_policy_deny_blocks_execution用一个返回 deny 判决的假 runtime 验证了该分支。静态扫描门static-scan gate调用enforce_no_subprocess_execution(code)来自 code_scanner.py。这是一个 Python AST 扫描器其_DANGEROUS_CALLS表覆盖subprocessPopen/run/call等、ossystem/exec*/spawn*/popen等、pty.spawn、shutil.which命中即抛SandboxCodeViolationPermissionError子类。测试test_code_scanner_blocks_subprocess与test_run_once_destroys_session_on_guard_violation验证了拦截与清理。4.3 非 Python 解释器的 fail-closed由于扫描器只理解 Pythonexecute_code会先校验解释器是否为 Python_is_python_interpreter匹配py|python|pypy系列provider.py若配置了node等非 Python 解释器execute_code直接抛ValueError并引导调用方改用run()显式命令——宁可拒绝也不执行未扫描的代码test_non_python_interpreter_fails_closed绝对路径如/usr/bin/python3则放行test_absolute_python_interpreter_path_allowed。五、能力集构建与执行生命周期5.1 CapabilitySet 的组装_build_caps()provider.py按以下顺序组装CapabilitySetinclude_system_pathsTrue时对default_system_paths()的每个路径授予READ缺失路径以FileNotFoundError静默跳过额外只读目录如解析出的解释器所在目录授予READcfg.readonly_paths→READcfg.readwrite_paths→READ_WRITE网络按第三节的三态模型绑定代理或阻断。其中default_system_paths()config.py返回 Unix 根目录集/usr、/bin、/sbin、/lib、/lib64、/opt macOS 专属/private、/Library/Frameworks、/dev、/System 当前解释器的 prefixsys.prefix、sys.base_prefix且只返回宿主上真实存在的路径同一列表在 Linux 与 macOS 上均安全。5.2 命令解析与环境隔离_resolve_command()provider.py在未沙箱化的父进程中将裸程序名解析为绝对路径shutil.which在宿主PATH上查找macOS 上常见的裸python回退到sys.executable因为沙箱内inherit_envFalse、没有PATH可解析解析出的程序目录随后被授予只读访问。_build_env()provider.py组装子进程环境config.sanitized_env()经sanitize_env_vars剥离LD_PRELOAD、PYTHONSTARTUP、NODE_OPTIONS等启动钩子见 _hardening.py加上可选的NONO_CONTEXTJSON 上下文不可序列化时告警丢弃有代理时再并入代理环境。5.3 _spawn 与结果处理_spawn()provider.py是执行中枢以会话output/目录为cwd相对写落盘到持久 workspace以timeout_secstimeout、inherit_envFalse调用nono.sandboxed_exec(caps, command, ...)超时判定基于 nono 约定退出码 124表示因超时被杀死_TIMEOUT_EXIT_CODEprovider.py此时killedTrue且kill_reason注明超时时长stdout/stderr 按流截断至 1 MiB_OUTPUT_MAX_BYTES超出部分追加[...output truncated at byte limit]标记结果封装为SandboxResultsuccess、exit_code、stdout、stderr、duration_seconds、killed、kill_reason。对应测试test_timeout_marks_killed、test_timeout_passed_through、test_output_truncated、test_sandboxed_exec_failure_surfaces、test_cwd_is_output_dir覆盖了这些行为。5.4 agent_id 校验_validate_agent_id()provider.py要求agent_id匹配^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,127}$因为它会被插值进日志与 workspace 目录名——先拒绝敌意字符杜绝路径穿越与控制字符注入test_invalid_agent_id_rejected。六、安装、可用性与实战用法6.1 安装与平台前提agt-sandbox将 nono 作为可选依赖发布pyproject.tomlnono-py0.10.1,0.11且注明仅 Linux/macOS无 Windows wheelpip install agt-sandbox[nono] # Linux/macOS only可用性探测由_compute_unavailable_reason()完成provider.pynono-py未安装 → 提示安装命令已安装但is_supported()为假 → 提示需要 Linux kernel 5.13Landlock或 macOS。is_available()返回探测结果在不可用宿主上调用create_session会抛RuntimeErrortest_unavailable_when_extension_missing、test_unavailable_when_unsupported_host。生产就绪性提示来自 README.mdnono-py上游 PyPI 分级为Alpha。内核级强制Landlock/Seatbelt结构上强于进程内守卫但项目仍在成熟中——建议先用于纵深防御、开发与 CI投入生产硬边界前自行完成安全评审Windows 或无 Landlock 的内核请改用DockerSandboxProvider等其他后端。6.2 一次性用法run_once每次 nono 沙箱都是全新 fork、运行后即退出的子进程因此最贴合的一次性用法是run_onceprovider.py内部创建会话 → 执行 → 销毁无需跟踪 session_id且失败路径同样保证清理test_run_once_cleans_up_on_failurefrom agent_sandbox import NonoSandboxProvider, SandboxConfig provider NonoSandboxProvider() if not provider.is_available(): raise SystemExit(nono not supported here (needs LinuxLandlock or macOS)) execution provider.run_once( agent-1, print(hello from nono), configSandboxConfig(timeout_seconds20, network_enabledFalse), ) print(execution.result.stdout)6.3 会话式用法create_session execute_code需要跨调用共享持久output/目录以及常驻网络代理时使用完整生命周期。create_session内部会创建scripts/只读授予沙箱存放待执行脚本与output/读写授予沙箱即持久输出区两个子目录provider.pyfrom agent_control_specification import AgentControl from agent_sandbox import NonoSandboxProvider, SandboxConfig provider NonoSandboxProvider() runtime AgentControl.from_path(str(manifest.yaml)) config SandboxConfig( timeout_seconds90, input_dir/data/agent-input, output_dir/data/agent-output, network_enabledTrue, network_allowlist[api.openai.com, *.github.com], ) handle provider.create_session(agent-1, runtimeruntime, configconfig) execution provider.execute_code( agent-1, handle.session_id, print(hello from nono session), ) print(execution.result.stdout) provider.destroy_session(agent-1, handle.session_id)6.4 低层命令执行runrun()provider.py直接以命令列表方式执行任意程序不经代码扫描适合非 Python 语言有会话时复用该会话的配置/workspace/代理否则构建临时一次性会话并在调用后清理。空命令返回失败结果test_run_empty_command。6.5 集成测试入口真实的端到端验证真实 OS 沙箱位于 test_nono_integration.py默认跳过需满足两个条件nono-py已安装且is_supported()为真、环境变量AGT_NONO_INTEGRATION1macOS 上若检测到已处于沙箱内也会跳过因为 Seatbelt 禁止嵌套沙箱pip install agt-sandbox[nono] export AGT_NONO_INTEGRATION1 pytest agent-governance-python/agent-sandbox/tests/test_nono_integration.py -v而 test_nono_sandbox.py 通过 monkeypatch 假nono_py模块保持测试环境的封闭性无内核沙箱、无网络可在任意宿主含不支持 nono 的 Windows运行覆盖配置校验、fail-closed 出口契约、生命周期、能力绑定、超时/截断、各类守卫等全部行为。七、设计要点的验证与小结回到提案本身其每一项设计声明都能在实现与测试中找到对应证据提案声明实现位置验证测试Landlock/Seatbelt经nono-pyprovider.py 模块 docstring、config.py docstringtest_available_when_supported每次执行全新子进程 显式能力集_spawn→nono.sandboxed_exec(caps, ...)test_session_paths_grantedfrom_sandbox_config映射 timeout/mounts/env/networkconfig.pytest_from_sandbox_config、test_mounts_and_egress网络默认阻断NonoConfig.allow_outboundFalse默认值 _build_caps的block_network()test_network_blocked_by_default过滤出口走allowed_hosts代理_start_proxycaps.proxy_onlytest_proxy_started_for_egress无限制出口需显式 default allow_check_egressfail-closedtest_outbound_unrestricted_requires_explicit_opt_in非空tool_allowlist拒绝create_session抛ValueErrortest_tool_allowlist_fails_closedCPU/内存归 OS 管理from_sandbox_config丢弃memory_mb/cpu_limittest_from_sandbox_config字段未透传AgentControl包装进HostSessiondeny 先于sandboxed_exec_build_runtime_sessionexecute_code治理门test_policy_deny_blocks_execution静态扫描先于sandboxed_execenforce_no_subprocess_execution(code)test_code_scanner_blocks_subprocessNonoSandboxProvider的价值在于以纯 Python 安装即可获得内核级强制的隔离能力无 daemon、无 hypervisor、无云账号同时把“显式优于隐式”的 fail-closed 原则贯彻到配置、网络、工具通道与执行前拦截的每一层。对 Agent Governance Toolkit 的落地场景而言它是本地开发与 CI 环境中兼具部署轻量与结构性强度的默认选择之一而对需要更强硬边界的生产环境则可通过同一SandboxProvider抽象无缝切换到 Docker、Hyperlight、MXC 或 Azure Container Apps 后端。相关源码与测试位于 agent-governance-python/agent-sandbox可继续深入阅读。【免费下载链接】agent-governance-toolkitAI Agent Governance Toolkit — Policy enforcement, zero-trust identity, execution sandboxing, and reliability engineering for autonomous AI agents. Covers 10/10 OWASP Agentic Top 10.项目地址: https://gitcode.com/GitHub_Trending/ag/agent-governance-toolkit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价