refactor(sandbox): 长期容器 + docker exec 替代一次性 docker run
容器策略从"每次 docker run 一次性容器"改为"长期容器 + docker exec": - 统一产物目录 tools/bin/:build.sh 和 tool_manager 编译后复制产物到 此目录,registry 解析路径优先用 tools/bin/<tool>。 - 长期容器 ias-sandbox:daemon 启动时 ensure(已运行则复用), tools/bin 只读挂载到 /tools,无网络/只读根/cap-drop ALL/资源限制。 - docker exec 调用:工具执行改为 docker exec -i ias-sandbox timeout -k 5 <secs> /tools/<name>,省去容器创建开销。 timeout -k 5 确保超时 SIGTERM→SIGKILL,无孤儿进程。 - 新增 src/tools/sandbox.rs:ensure_container + build_exec_cmd。 - executor 删除 build_docker_cmd/sandbox_enabled,docker 分支改用 sandbox::build_exec_cmd。 - env 按 spec 白名单 -e 注入(容器启动时仅有 PATH)。 验证:本地执行 hostname=debian,docker exec hostname=容器ID, 证明确实走长期容器 exec;25 测试通过。
This commit is contained in:
+5
-63
@@ -145,11 +145,12 @@ pub async fn execute_loop(
|
||||
}
|
||||
|
||||
// ── spawn ──
|
||||
// 执行隔离边界:启用沙箱时按 sandbox.profile 选择一次性 Docker 容器,
|
||||
// 否则本地 spawn(env_clear + 白名单注入)
|
||||
let use_docker = sandbox_enabled() && spec.sandbox.profile != "local";
|
||||
// 执行隔离边界:启用沙箱且工具 profile 非 local 时,通过 docker exec
|
||||
// 在长期容器内执行;否则本地 spawn(env_clear + 白名单注入)
|
||||
let use_docker =
|
||||
crate::tools::sandbox::enabled() && spec.sandbox.profile != "local";
|
||||
let mut cmd = if use_docker {
|
||||
build_docker_cmd(spec, binary_path)
|
||||
crate::tools::sandbox::build_exec_cmd(spec)
|
||||
} else {
|
||||
build_local_cmd(spec, binary_path)
|
||||
};
|
||||
@@ -347,14 +348,6 @@ pub async fn execute_loop(
|
||||
}
|
||||
}
|
||||
|
||||
/// 全局沙箱开关:`IAS_DOCKER_SANDBOX=1` 启用一次性容器隔离
|
||||
fn sandbox_enabled() -> bool {
|
||||
std::env::var("IAS_DOCKER_SANDBOX")
|
||||
.ok()
|
||||
.as_deref()
|
||||
== Some("1")
|
||||
}
|
||||
|
||||
/// 本地执行:env_clear + PATH + spec.env 白名单注入
|
||||
fn build_local_cmd(spec: &ToolSpec, binary_path: &str) -> Command {
|
||||
let mut cmd = Command::new(std::path::Path::new(binary_path));
|
||||
@@ -377,54 +370,3 @@ fn build_local_cmd(spec: &ToolSpec, binary_path: &str) -> Command {
|
||||
}
|
||||
cmd
|
||||
}
|
||||
|
||||
/// Docker 隔离执行:一次性容器,默认无网络、只读根、cap-drop ALL、
|
||||
/// no-new-privileges,仅挂载工具二进制(ro)+ tmpfs /tmp。
|
||||
/// 供 tool_manager 产出的不受信任工具使用(profile: stdio)。
|
||||
fn build_docker_cmd(spec: &ToolSpec, binary_path: &str) -> Command {
|
||||
let image = match spec.sandbox.profile.as_str() {
|
||||
"builder" => "ias-tool-builder",
|
||||
_ => "ias-stdio-runner",
|
||||
};
|
||||
let mut cmd = Command::new("docker");
|
||||
cmd.arg("run")
|
||||
.arg("--rm")
|
||||
.arg("--network")
|
||||
.arg(if spec.sandbox.network == "default" {
|
||||
"default"
|
||||
} else {
|
||||
"none"
|
||||
})
|
||||
.arg("--memory")
|
||||
.arg(&spec.sandbox.memory)
|
||||
.arg("--cpus")
|
||||
.arg(&spec.sandbox.cpus)
|
||||
.arg("--pids-limit")
|
||||
.arg(spec.sandbox.pids_limit.to_string())
|
||||
.arg("--cap-drop")
|
||||
.arg("ALL")
|
||||
.arg("--security-opt")
|
||||
.arg("no-new-privileges");
|
||||
if spec.sandbox.read_only {
|
||||
cmd.arg("--read-only");
|
||||
}
|
||||
// 工具二进制只读挂载到 /tool
|
||||
cmd.arg("--mount")
|
||||
.arg(format!("type=bind,source={binary_path},target=/tool,readonly"));
|
||||
// 可写临时目录
|
||||
cmd.arg("--mount")
|
||||
.arg("type=tmpfs,target=/tmp");
|
||||
// env:容器内固定 PATH + spec 声明的白名单
|
||||
cmd.arg("--env")
|
||||
.arg("PATH=/usr/local/bin:/usr/bin:/bin");
|
||||
for key in &spec.env {
|
||||
if let Ok(v) = std::env::var(key) {
|
||||
cmd.arg("--env").arg(format!("{key}={v}"));
|
||||
}
|
||||
}
|
||||
cmd.arg(image).arg("/tool");
|
||||
if let Some(c) = &spec.command {
|
||||
cmd.arg("--command").arg(c);
|
||||
}
|
||||
cmd
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user