1. 项目概述:为什么我们需要为AI智能体穿上“隔离服”?

最近在折腾各种AI智能体(Agent)和代码生成工具,从Claude Code到各种自建的MCP服务器,一个越来越头疼的问题摆在了面前:安全。当你让一个AI智能体去读取你的项目代码、访问网络API、甚至执行系统命令时,本质上是在赋予一段由大语言模型生成的、充满不确定性的代码以相当高的权限。一次意外的 rm -rf 、一次对敏感环境变量的读取、或是一次向未知外部服务器的数据泄露,都可能造成严重后果。传统的做法要么是放任自流,在虚拟机上跑,要么是写一堆复杂的 seccomp-bpf 规则和 AppArmor 配置,门槛高且容易出错。

直到我遇到了 nono 。这个由Sigstore(软件供应链安全签名标准的核心创建者)团队带来的开源项目,直击了这个痛点。它不是一个笨重的虚拟机,也不是一个复杂的容器编排系统,而是一个 内核级强制的、能力(Capability)为基础的隔离沙箱 。你可以把它想象成给AI智能体穿上一件量身定制的“隔离服”,在它启动的瞬间,就由操作系统内核永久性地剥夺其所有不必要的权限,只留下你明确允许的那部分。更妙的是,它只是一个二进制文件,零基础设施依赖,几乎不引入性能开销。无论是个人开发者本地调试一个Claude Code会话,还是在生产环境大规模部署成百上千个AI工作流,nono都试图提供一套统一、简洁且强大的安全范式。接下来,我将结合官方文档和实际测试,深入拆解nono的核心机制、手把手演示如何将它集成到你的AI工作流中,并分享一些实战中积累的配置技巧和避坑经验。

2. 核心安全架构与设计哲学拆解

nono的设计哲学深深植根于“最小权限原则”和“零信任”安全模型。它不假设运行在其中的进程(你的AI智能体)是善意的,而是默认其不可信,必须通过显式声明来获取每一项能力。

2.1 内核强制隔离:Landlock 与 Seatbelt 的威力

nono的核心隔离能力并非自创,而是巧妙地利用了现代操作系统内核提供的安全模块。

  • 在Linux上 ,它主要依赖 Landlock 。Landlock是Linux内核的一个安全模块,允许进程在启动后为自己及其所有子进程创建一个不可逆的“监狱”。这个监狱的规则由“能力集”定义,比如可以访问哪些文件系统路径(只读或读写)、可以执行哪些操作等。一旦规则生效,即使是拥有 root 权限的进程也无法从内部突破这些限制。nono在启动目标AI智能体前,会先调用Landlock的API,施加这些限制,然后再执行你的AI进程。这意味着,从AI进程诞生的第一刻起,它就已经被关在了笼子里。
  • 在macOS上 ,对等的是 Apple的沙箱框架(Seatbelt) 。它通过一个描述性配置文件(.sb)来定义沙箱规则,原理类似。nono会生成并应用相应的沙箱配置文件。

这种做法的最大优势是 轻量 不可绕过 。它不像虚拟机(Hypervisor)那样需要虚拟化整个硬件和操作系统,也不像传统容器(如Docker)那样依赖一个常驻的守护进程和命名空间隔离(虽然也能结合使用)。它是进程本身属性的一部分,由内核直接强制执行,开销极低。

注意 :Landlock需要较新的Linux内核(通常5.13+)并启用相关配置。macOS的沙箱支持则比较成熟。你可以通过运行 nono setup --check-only 来快速检查当前系统的支持情况。

2.2 能力(Capability)模型:白名单即一切

nono的所有安全策略都围绕“能力”展开。一个能力代表一项具体的权限。在运行一个智能体前,你必须明确告诉nono:“我允许它做以下事情”。所有未明确允许的,默认都是禁止的。这些能力主要包括:

  1. 文件系统访问 :这是最核心的能力。你可以精确到路径级别,指定只读( --read )或读写( --write )权限。例如, --read /home/user/project 允许智能体读取你的项目代码,但无法修改; --write /tmp/scratch 允许它在临时目录中创建和修改文件。
  2. 网络访问 :nono通过一个内置的本地代理来实现网络过滤。你可以通过 --allow-host --allow-endpoint 来构建白名单。
    • --allow-host api.openai.com 允许访问该域名。
    • --allow-endpoint 'github:GET:/repos/*/issues/**' 这是一个更精细的L7过滤,允许向GitHub的特定REST API端点(获取issue列表)发起GET请求,同时阻止其他所有方法(POST, DELETE等)和路径。
  3. 环境变量与凭证注入 :AI智能体经常需要API密钥。nono的“凭证注入”功能可以确保密钥 永不进入沙箱内部 。它运行在一个“代理模式”下,智能体对密钥的请求会被nono拦截,并由nono从外部的安全存储(如系统密钥链、1Password、或指定的文件)中取出,代为完成认证。沙箱内的进程只能看到一个代理地址,而无法直接接触原始密钥。
  4. 进程与信号控制 :可以限制子进程的派生,或控制可以接收哪些系统信号。

这些能力可以通过命令行参数直接赋予,也可以通过更结构化的 策略配置文件 来定义和管理。

2.3 原子快照与回滚:给每次尝试上保险

AI智能体的行为具有探索性,可能会把工作目录搞得一团糟。nono的 原子快照 功能就像游戏存档。在启动智能体前,你可以指定 --rollback 参数。nono会为允许写入的目录创建一个内容寻址的快照(基于SHA-256哈希)。如果智能体的运行结果不符合预期,你可以一键将目录状态回滚到快照点,所有更改都会被干净地撤销。

这个功能的底层使用了Merkle树来保证快照的完整性,防止篡改。这对于需要反复试验、又不希望手动清理残局的场景非常有用。

2.4 溯源与审计:不可篡改的行动日志

安全不仅是预防,也在于事后审计。nono会生成详细的、密码学可验证的审计日志,记录沙箱内进程的所有重要动作:文件读写、网络连接尝试(无论是否被允许)、进程创建等。这些日志可以使用 Sigstore 进行签名,形成一个不可篡改的溯源链。你可以知道某个输出文件是由哪个AI模型、在何种安全策略下、于何时生成的。这对于满足合规性要求、调试复杂问题或调查安全事件至关重要。

3. 从安装到实战:手把手配置你的第一个安全沙箱

理论说得再多,不如动手一试。我们以在macOS上使用Homebrew安装,并运行一个受限制的Claude Code为例。

3.1 安装与系统检查

安装非常简单:

brew install nono

对于其他系统,可以参考 官方安装指南 ,有二进制包、脚本等多种方式。

安装后,首先检查你的系统环境是否支持nono的核心功能:

nono setup --check-only

这个命令会输出一个报告,告诉你Landlock/Seatbelt是否可用,网络代理模式是否支持等。如果有任何警告,通常会给出解决建议。

3.2 使用内置策略文件快速启动

nono提供了一些针对常见AI智能体的预置策略文件(Profiles),例如 claude-code codex 等。这些策略已经定义了一套相对合理的安全能力集。

启动一个受沙箱保护的Claude Code会话最简单的方式是:

nono run --profile claude-code -- claude

解释一下这个命令:

  • nono run :核心运行命令。
  • --profile claude-code :应用名为“claude-code”的预置策略。
  • -- :分隔符,其后是要在沙箱内运行的命令。
  • claude :实际运行的命令(假设Claude Code CLI工具已安装且名为 claude )。

执行后,Claude Code会正常启动,但它已经运行在一个被严格限制的环境中。你可以尝试在Claude Code的会话中执行一些越权命令(比如 ls /etc/passwd 或尝试访问互联网),会发现这些操作被阻止,并记录在审计日志中。

3.3 自定义策略:构建专属的隔离环境

预置策略很好,但每个项目需求不同。我们需要学会自定义。假设我有一个Python AI智能体脚本 my_agent.py ,它需要:

  1. 只读访问我的项目目录 /Users/me/my_project
  2. 读写访问一个临时工作区 /tmp/agent_workspace
  3. 只能向 api.openai.com raw.githubusercontent.com 这两个域名发起HTTPS请求。
  4. 需要用到OpenAI的API密钥,但我不想把密钥硬编码在脚本或环境变量中。

我们可以通过命令行参数组合来实现:

export OPENAI_API_KEY="sk-..."
nono run \
  --read /Users/me/my_project \
  --write /tmp/agent_workspace \
  --allow-host api.openai.com \
  --allow-host raw.githubusercontent.com \
  --env OPENAI_API_KEY \
  -- python3 my_agent.py

这里, --env OPENAI_API_KEY 是一个关键参数。它告诉nono:“将当前shell环境中的 OPENAI_API_KEY 变量传递到沙箱内部”。请注意,此时密钥 会进入沙箱环境 。为了更安全,我们应该使用凭证注入。

3.4 进阶:使用凭证注入与策略组

更安全的方式是使用nono的凭证代理和策略组(Policy Groups)。

首先,将你的API密钥存入系统密钥链(以macOS为例):

# 将密钥存入登录钥匙串
security add-generic-password -a "$USER" -s "openai-api-key" -w "sk-..."

然后,创建一个策略组配置文件,例如 my_agent.policy.yaml

version: 1
profiles:
  my-agent:
    read:
      - /Users/me/my_project
    write:
      - /tmp/agent_workspace
    network:
      allow_hosts:
        - api.openai.com
        - raw.githubusercontent.com
    credentials:
      - id: openai-key
        # 从系统钥匙串获取
        from: keychain://login/openai-api-key
        # 注入到沙箱环境变量中,但通过代理访问
        use_as: env_proxy
        env_var: OPENAI_API_KEY
        # 代理转发到目标主机
        proxy_to: api.openai.com

在这个配置中, use_as: env_proxy 是精髓。它告诉nono:不要将密钥值直接设置到 OPENAI_API_KEY 环境变量里,而是设置一个指向nono本地代理的地址(如 http://127.0.0.1:某个端口 )。当 my_agent.py 中的代码(比如 openai 库)尝试向 api.openai.com 发送请求时,请求会被nono的代理拦截。代理从钥匙串中取出真正的密钥,添加到请求头中,再转发给真实的OpenAI API。沙箱内的进程全程接触不到密钥明文。

使用策略文件运行:

nono run --policy-group my_agent.policy.yaml --profile my-agent -- python3 my_agent.py

3.5 分离式运行、快照与审计

对于长时间运行的智能体,可以使用分离模式:

# 启动一个分离的沙箱会话,并创建快照
nono run --detached --policy-group my_agent.policy.yaml --profile my-agent --rollback -- python3 my_agent.py
# 输出类似:Started detached session 7a6a652f7273fe60.

这会返回一个会话ID。你可以随时连接回去查看输出:

nono attach 7a6a652f7273fe60

查看当前所有运行中的沙箱会话:

nono ps

停止某个会话:

nono stop 7a6a652f7273fe60
# 停止后,如果之前有--rollback,可以选择丢弃或提交更改
nono stop 7a6a652f7273fe60 --discard # 回滚到快照点
nono stop 7a6a652f7273fe60 --keep    # 保留所有更改

审计日志默认输出到标准错误(stderr),你也可以通过 --audit-log <path> 指定文件。日志是结构化的(如JSON Lines格式),便于后续分析。

4. 集成与进阶应用场景

nono的灵活性使其能融入各种开发生命周期。

4.1 在CI/CD流水线中保障安全

在GitHub Actions中,你可以使用官方提供的 agent-sign Action 来运行受nono保护的AI任务。这能确保在自动生成代码、执行脚本时,不会意外破坏 runner 主机或泄露密钥。

一个简单的示例工作流片段:

jobs:
  ai-code-review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run secured AI agent
        uses: always-further/agent-sign@v1
        with:
          policy-profile: 'codex'
          command: 'codex review --diff'
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

agent-sign Action 内部会调用nono,根据指定的策略文件来安全地执行 codex 命令。

4.2 与容器(Docker/Kubernetes)结合

你可以在Dockerfile中安装nono,让你的容器镜像本身就具备启动沙箱的能力。在Kubernetes的Pod中,你可以将nono作为 initContainer 或直接作为主容器的入口点,在应用启动前施加隔离策略。nono最近新增的“便携式能力清单”功能( nono policy show --format manifest )可以导出完整的、解析后的沙箱配置,非常适合作为K8s的 ConfigMap ,实现安全策略的声明式管理。

4.3 作为库嵌入到你的Rust/Python/Go应用中

对于想要将安全隔离深度集成到自己应用中的开发者,nono提供了原生库和多种语言绑定。这意味着你可以在你的Rust、Python、Go或Node.js程序中,直接调用nono的API来沙箱化进程的某一部分。

例如,在Python中:

from nono import Sandbox, CapabilitySet

caps = CapabilitySet()
caps.allow_read("/data")
caps.allow_write("/tmp")
caps.allow_host("api.example.com")

# 施加沙箱限制,此后当前进程及其子进程都将受限
Sandbox.apply(caps)

# 在这之后,启动你的AI子进程或执行不受信任的代码
import subprocess
subprocess.run(["python", "untrusted_agent.py"])

这为构建具有内生安全性的AI应用平台提供了强大基础。

5. 常见问题、故障排查与实战心得

在实际使用中,你可能会遇到一些问题。以下是一些常见情况及解决思路。

5.1 权限错误与路径解析

问题 :运行时报错“Permission denied”或“Path not allowed”,即使路径看起来正确。 排查

  1. 符号链接 :nono的路径检查是基于内核级别的文件描述符。如果路径包含符号链接,沙箱可能会解析到链接指向的真实路径,而这个真实路径可能不在允许列表中。尽量使用绝对路径,并避免在策略中使用包含符号链接的路径。
  2. 工作目录 :沙箱化后,进程改变工作目录( chdir )的能力可能受限。确保你的命令或脚本不依赖于切换到一个未授权的目录。可以使用 --cwd <path> 参数预先设置好允许的工作目录。
  3. 递归权限 --read /home/user/project 通常只授权该目录本身及其下的文件。如果进程需要“遍历”该目录(列出文件),该目录本身需要 执行(execute) 权限。在Landlock中,读目录元数据需要遍历权。确保你的策略允许必要的目录遍历。

5.2 网络连接失败

问题 :沙箱内的进程无法连接到明明已白名单放行的主机。 排查

  1. 代理冲突 :nono为了实现网络过滤,会强制使用自己的本地代理( http://127.0.0.1:某个端口 )。如果沙箱内的进程本身配置了HTTP代理(如 http_proxy 环境变量),可能会产生冲突。nono通常会处理这个,但复杂情况可能需要检查。可以尝试在策略中明确设置沙箱内的代理环境变量。
  2. DNS解析 :网络过滤在连接阶段。DNS解析可能发生在过滤之前,或者智能体使用了自定义的DNS解析器。确保DNS解析能正常工作。有时需要额外允许访问系统DNS服务器(如 127.0.0.53:53 ),但这会扩大攻击面,需谨慎。
  3. L7端点过滤过严 :如果使用了 --allow-endpoint ,请仔细检查方法(GET/POST等)、主机名、路径模式是否完全匹配。一个多余的斜杠或错误的通配符都可能导致阻塞。建议先从宽松的 --allow-host 开始测试,再逐步收紧。

5.3 性能与兼容性考量

心得

  • 开销极小 :在我的测试中,对于典型的CLI AI工具,nono带来的性能开销几乎可以忽略不计(<1%)。主要的开销可能来自网络代理的额外跳转,但对于本地或高速网络,影响微乎其微。
  • 与现有工具的兼容性 :大多数纯计算和文件操作的CLI工具都能完美运行。问题常出现在那些需要特殊系统调用或试图直接访问硬件的工具。例如,某些需要 ptrace 系统调用来调试其他进程的工具会在沙箱内失败。此时,你需要评估是否真的需要给AI智能体这样的权限。
  • 逐步收紧策略 :不要一开始就追求最严格的策略。建议采用“白名单学习”模式:先以一个较宽松的策略(如允许读写当前目录,允许访问常用API域名)运行你的智能体,同时开启详细的审计日志( --audit-log-path debug.log )。运行完典型任务后,分析日志,看看智能体实际访问了哪些路径和网络地址。然后基于这个“实际行为记录”来编写你的最终白名单策略,移除所有不必要的权限。这是实现最小权限原则最有效的方法。

5.4 审计日志的分析

审计日志是金矿。默认的文本输出可读,但对于大量日志,建议使用 jq 等工具进行结构化分析。

# 将审计日志转换为JSON并按事件类型统计
cat audit.log | jq -r '.event_type' | sort | uniq -c | sort -nr
# 查找所有被拒绝的网络连接尝试
cat audit.log | jq 'select(.event_type == "network_denied")'

定期审查审计日志,可以帮助你发现智能体是否有异常行为,或者你的策略是否存在过紧或过松的问题。

nono的出现,为AI智能体的安全实践提供了一种优雅而强大的原语。它将内核安全能力、现代凭证管理和密码学审计结合在一个简单的工具中。无论是保护你的开发机免受实验性AI代码的意外破坏,还是为生产环境的AI工作流构建合规、可信的基线,nono都值得你深入探索并将其纳入工具箱。安全不再是一个可选项,而应是智能体架构中内置的一环。从今天开始,为你运行的每一个AI进程,套上这件合身的“隔离服”。

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐