fix(sandbox): builder 容器 --user uid:gid + 离线模式,修 cap-drop 写权限
步骤 9: tool_manager build 迁移到 builder 容器验证通过。
问题: --cap-drop ALL 丢掉 CAP_DAC_OVERRIDE,容器内 root 无法写
bind mount 的非 root 目录(宿主 xiao:1000)。
这是 cap-drop 正确生效的安全行为,非 bug。
修复:
- builder 容器 --user uid:gid 以宿主用户身份运行,自然能写自己
的目录,同时保留 cap-drop ALL + no-new-privileges。
- Cargo.toml 改 CARGO_HOME=/tmp/cargo + chmod 777,使任意 uid
都能复用 serde_json 预缓存。
- cargo build --offline + CARGO_NET_OFFLINE=true,配合 --network
none 真正离线编译,杜绝恶意代码下载依赖。
端到端: tool_manager create_tool → builder 容器离线编译 →
产物运行成功,审计日志记录正确。
This commit is contained in:
@@ -1,10 +1,13 @@
|
|||||||
# ias-tool-builder — tool_manager 编译 Rust 工具的隔离环境
|
# ias-tool-builder — tool_manager 编译 Rust 工具的隔离环境
|
||||||
# 预缓存 serde_json(tool_manager 生成工具的唯一依赖),支持 --network none 离线编译。
|
# 预缓存 serde_json(tool_manager 生成工具的唯一依赖),支持 --network none 离线编译。
|
||||||
|
# CARGO_HOME=/tmp/cargo 并 chmod 777,使任意 uid(--user 运行)都能复用缓存。
|
||||||
# 运行时由 tool_manager 挂载 tool_dir 到 /work,cargo 在 /work 内编译,产物写回宿主。
|
# 运行时由 tool_manager 挂载 tool_dir 到 /work,cargo 在 /work 内编译,产物写回宿主。
|
||||||
FROM rust:1.88-slim
|
FROM rust:1.88-slim
|
||||||
|
ENV CARGO_HOME=/tmp/cargo
|
||||||
RUN cargo new /tmp/seed && cd /tmp/seed \
|
RUN cargo new /tmp/seed && cd /tmp/seed \
|
||||||
&& printf 'serde_json = "1.0"\n' >> Cargo.toml \
|
&& printf 'serde_json = "1.0"\n' >> Cargo.toml \
|
||||||
&& cargo build --release \
|
&& cargo build --release \
|
||||||
&& rm -rf /tmp/seed
|
&& rm -rf /tmp/seed \
|
||||||
|
&& chmod -R 777 /tmp/cargo
|
||||||
WORKDIR /work
|
WORKDIR /work
|
||||||
ENTRYPOINT []
|
ENTRYPOINT []
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ fn build_tool(tool_dir: &Path, tool_name: &str) -> Result<(), String> {
|
|||||||
.ok()
|
.ok()
|
||||||
.as_deref()
|
.as_deref()
|
||||||
== Some("1");
|
== Some("1");
|
||||||
|
let (uid, gid) = current_uid_gid();
|
||||||
|
|
||||||
// 启用 builder 容器时在隔离环境内离线编译,挂载 tool_dir 到 /work,产物写回宿主;
|
// 启用 builder 容器时在隔离环境内离线编译,挂载 tool_dir 到 /work,产物写回宿主;
|
||||||
// 否则本地 cargo(受 timeout 限制)
|
// 否则本地 cargo(受 timeout 限制)
|
||||||
@@ -138,11 +139,14 @@ fn build_tool(tool_dir: &Path, tool_name: &str) -> Result<(), String> {
|
|||||||
.arg("--pids-limit").arg("128")
|
.arg("--pids-limit").arg("128")
|
||||||
.arg("--cap-drop").arg("ALL")
|
.arg("--cap-drop").arg("ALL")
|
||||||
.arg("--security-opt").arg("no-new-privileges")
|
.arg("--security-opt").arg("no-new-privileges")
|
||||||
|
.arg("--user").arg(format!("{}:{}", uid, gid))
|
||||||
.arg("-v").arg(format!("{}:/work", tool_dir.display()))
|
.arg("-v").arg(format!("{}:/work", tool_dir.display()))
|
||||||
.arg("-w").arg("/work")
|
.arg("-w").arg("/work")
|
||||||
|
.arg("-e").arg("CARGO_NET_OFFLINE=true")
|
||||||
|
.arg("-e").arg("CARGO_HOME=/tmp/cargo")
|
||||||
.arg("ias-tool-builder")
|
.arg("ias-tool-builder")
|
||||||
.arg("timeout").arg(BUILD_TIMEOUT_SECS.to_string())
|
.arg("timeout").arg(BUILD_TIMEOUT_SECS.to_string())
|
||||||
.arg("cargo").arg("build").arg("--release")
|
.arg("cargo").arg("build").arg("--release").arg("--offline")
|
||||||
.output()
|
.output()
|
||||||
} else {
|
} else {
|
||||||
Command::new("timeout")
|
Command::new("timeout")
|
||||||
@@ -309,6 +313,21 @@ fn truthy(value: Option<&Value>) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 获取当前进程的 uid:gid,用于 builder 容器 --user,使容器内进程能写 bind mount 的宿主目录
|
||||||
|
fn current_uid_gid() -> (String, String) {
|
||||||
|
let uid = String::from_utf8_lossy(
|
||||||
|
&Command::new("id").arg("-u").output().map(|o| o.stdout).unwrap_or_default(),
|
||||||
|
)
|
||||||
|
.trim()
|
||||||
|
.to_string();
|
||||||
|
let gid = String::from_utf8_lossy(
|
||||||
|
&Command::new("id").arg("-g").output().map(|o| o.stdout).unwrap_or_default(),
|
||||||
|
)
|
||||||
|
.trim()
|
||||||
|
.to_string();
|
||||||
|
(uid, gid)
|
||||||
|
}
|
||||||
|
|
||||||
/// 追加审计日志到 tools 根目录下的 .tool_audit.log
|
/// 追加审计日志到 tools 根目录下的 .tool_audit.log
|
||||||
fn audit(
|
fn audit(
|
||||||
tools_root: &Path,
|
tools_root: &Path,
|
||||||
|
|||||||
Reference in New Issue
Block a user