Skip to content

Latest commit

 

History

History
200 lines (158 loc) · 5.3 KB

File metadata and controls

200 lines (158 loc) · 5.3 KB

ZeroClaw 沙箱策略

⚠️ 状态:提案 / 路线图

本文档描述提议的实现方法,可能包含假设的命令或配置。 如需了解当前运行时行为,请参见 config-reference.zh-CN.mdoperations-runbook.zh-CN.mdtroubleshooting.zh-CN.md

问题

ZeroClaw 当前具有应用层安全(白名单、路径阻止、命令注入保护),但缺少操作系统级别的 containment。如果攻击者在白名单中,他们可以使用 zeroclaw 的用户权限运行任何允许的命令。

提议的解决方案

选项 1:Firejail 集成(Linux 推荐)

Firejail 提供用户空间沙箱,开销极小。

// src/security/firejail.rs
use std::process::Command;

pub struct FirejailSandbox {
    enabled: bool,
}

impl FirejailSandbox {
    pub fn new() -> Self {
        let enabled = which::which(\"firejail\").is_ok();
        Self { enabled }
    }

    pub fn wrap_command(&self, cmd: &mut Command) -> &mut Command {
        if !self.enabled {
            return cmd;
        }

        // Firejail 使用沙箱包装任何命令
        let mut jail = Command::new(\"firejail\");
        jail.args([
            \"--private=home\",           // 新的 home 目录
            \"--private-dev\",            // 最小化 /dev
            \"--nosound\",                // 无音频
            \"--no3d\",                   // 无 3D 加速
            \"--novideo\",                // 无视频设备
            \"--nowheel\",                // 无输入设备
            \"--notv\",                   // 无 TV 设备
            \"--noprofile\",              // 跳过配置文件加载
            \"--quiet\",                  // 禁止警告
        ]);

        // 追加原始命令
        if let Some(program) = cmd.get_program().to_str() {
            jail.arg(program);
        }
        for arg in cmd.get_args() {
            if let Some(s) = arg.to_str() {
                jail.arg(s);
            }
        }

        // 用 firejail 包装替换原始命令
        *cmd = jail;
        cmd
    }
}

配置选项:

[security]
enable_sandbox = true
sandbox_backend = \"firejail\"  # 或 \"none\", \"bubblewrap\", \"docker\"

选项 2:Bubblewrap(便携,无需 root)

Bubblewrap 使用用户命名空间创建容器。

# 安装 bubblewrap
sudo apt install bubblewrap

# 包装命令:
bwrap --ro-bind /usr /usr \
      --dev /dev \
      --proc /proc \
      --bind /workspace /workspace \
      --unshare-all \
      --share-net \
      --die-with-parent \
      -- /bin/sh -c \"command\"

选项 3:Docker-in-Docker(重量级但完全隔离)

在临时容器中运行代理工具。

pub struct DockerSandbox {
    image: String,
}

impl DockerSandbox {
    pub async fn execute(&self, command: &str, workspace: &Path) -> Result<String> {
        let output = Command::new(\"docker\")
            .args([
                \"run\", \"--rm\",
                \"--memory\", \"512m\",
                \"--cpus\", \"1.0\",
                \"--network\", \"none\",
                \"--volume\", &format!(\"{}:/workspace\", workspace.display()),
                &self.image,
                \"sh\", \"-c\", command
            ])
            .output()
            .await?;

        Ok(String::from_utf8_lossy(&output.stdout).to_string())
    }
}

选项 4:Landlock(Linux 内核 LSM,Rust 原生)

Landlock 提供文件系统访问控制,无需容器。

use landlock::{Ruleset, AccessFS};

pub fn apply_landlock() -> Result<()> {
    let ruleset = Ruleset::new()
        .set_access_fs(AccessFS::read_file | AccessFS::write_file)
        .add_path(Path::new(\"/workspace\"), AccessFS::read_file | AccessFS::write_file)?
        .add_path(Path::new(\"/tmp\"), AccessFS::read_file | AccessFS::write_file)?
        .restrict_self()?;

    Ok(())
}

实现优先级顺序

阶段 解决方案 工作量 安全收益
P0 Landlock(仅 Linux,原生) 高(文件系统)
P1 Firejail 集成 极高
P2 Bubblewrap 包装 极高
P3 Docker 沙箱模式 完全

配置模式扩展

[security.sandbox]
enabled = true
backend = \"auto\"  # auto | firejail | bubblewrap | landlock | docker | none

# Firejail 特定配置
[security.sandbox.firejail]
extra_args = [\"--seccomp\", \"--caps.drop=all\"]

# Landlock 特定配置
[security.sandbox.landlock]
readonly_paths = [\"/usr\", \"/bin\", \"/lib\"]
readwrite_paths = [\"$HOME/workspace\", \"/tmp/zeroclaw\"]

测试策略

#[cfg(test)]
mod tests {
    #[test]
    fn sandbox_blocks_path_traversal() {
        // 尝试通过沙箱读取 /etc/passwd
        let result = sandboxed_execute(\"cat /etc/passwd\");
        assert!(result.is_err());
    }

    #[test]
    fn sandbox_allows_workspace_access() {
        let result = sandboxed_execute(\"ls /workspace\");
        assert!(result.is_ok());
    }

    #[test]
    fn sandbox_no_network_isolation() {
        // 确保配置时网络被阻止
        let result = sandboxed_execute(\"curl http://example.com\");
        assert!(result.is_err());
    }
}