⚠️ 状态:提案 / 路线图本文档描述提议的实现方法,可能包含假设的命令或配置。 如需了解当前运行时行为,请参见 config-reference.zh-CN.md、operations-runbook.zh-CN.md 和 troubleshooting.zh-CN.md。
ZeroClaw 当前具有应用层安全(白名单、路径阻止、命令注入保护),但缺少操作系统级别的 containment。如果攻击者在白名单中,他们可以使用 zeroclaw 的用户权限运行任何允许的命令。
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\"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\"在临时容器中运行代理工具。
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())
}
}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());
}
}