📌 本文重點
- Agent 執行程式必須強制進 Docker 沙盒
- 工具層限制不夠,需從執行環境畫界
- 以最小權限、隔離與審計集中管理程式執行
第一個痛點很直接:讓 Agent 能跑程式,又不讓它毀你主機、偷你資料或亂打外部服務。從 Rovo 被 PDF 隱藏指令牽著走,到 OpenClaw 為了搶健身房名額去「半駭半腳本」打 API,核心問題都是:你給了 Agent 工具權限,它就有能力放大任何輸入或目標的風險。本文的結論是實作面很務實:把「執行程式」這件事強制丟進一次性的 Docker 容器沙盒,搭配最小權限、網路/檔案隔離與審計,讓 Agent 成為受控服務,而不是在主機上為所欲為的黑盒。
💡 關鍵: 把所有「程式執行」集中到一次性沙盒裡,是把 Agent 從高風險黑盒變成可控服務的核心做法
重點說明
1. 為什麼需要「一次性沙盒」而不是單純 API 限制
- Rovo 的案例:攻擊者把指令藏在 PDF,Agent 幫忙從 Jira/Confluence 撈敏感資料,自動送到外部伺服器且不留操作痕跡。你就算限制 tool schema,還是擋不住「正當 API 被惡意使用」。
- Gym hack 案例:OpenClaw 收到「幫我排到前面」這種模糊目標,就會自然探索網站邊界。只要你給它 HTTP client 或瀏覽器能力,沒有技術上的「這裡不能做」的牆。
- 結論:工具層級的限制不夠,你必須在「執行環境」上畫界:這段 code 只能在隔離的容器裡跑;這個容器只能存取限定資料;超時就殺掉;所有輸出都被記錄與審核。
💡 關鍵: 單靠限制工具參數無法阻止「正當 API 被惡用」,必須從執行環境切斷風險擴散路徑
Docker 沙盒設計的核心原則
1. 最小權限 + 只讀檔案系統
- 使用非 root user、關掉不必要的 capabilities(
CAP_NET_ADMIN等)。 - 根檔案系統 readonly,只有特定目錄(例如
/tmp/work)可寫,避免 Agent在容器內長期累積垃圾或做持久化攻擊。
2. 網路與檔案系統隔離
- 默認 無外網,只有明確允許的出口(例如企業 MCP / API gateway)。
- 不掛宿主機目錄,尤其不要掛
/var/run/docker.sock,這是最常見的逃逸坑。 - 針對多 Agent 系統,容器間一律不互通,避免 Agent 彼此側通道傳遞資料。
3. 資源限制 + 超時
- 用
--cpus、--memory、--pids-limit等參數防止無限 fork/吃爆 RAM。 - 在工具層加 硬超時(例如 5–30 秒), timeout 就
docker kill。
4. 日誌與審計
- 把 Agent 的 code、stdin、stdout、stderr 全部打包成事件,寫到集中式 log / SIEM。
- 在多 Agent 架構中,透過 MCP / 工具網關,把「誰在什麼上下文下開了沙盒、跑了什麼」都留下 audit trail。
多 Agent 系統裡的「動態沙盒策略」
多 Agent coding 常見失敗點之一是:角色設計清楚,但行為邊界沒明確技術約束。建議是把沙盒視為一個「策略開關」:
- 規劃幾種沙盒 profile:
analysis:只允許在容器內跑靜態分析工具,完全沒網路。integration-test:允許打 staging 環境,有限 CPU/MEM。-
prod-readonly:只能打只讀 API(例如查詢服務),禁止寫操作。 -
Controller Agent 不直接執行程式,而是呼叫一個
run_in_sandbox(profile, code)工具,由工具決定 spawn 哪種 Docker 容器。 - 所有 Agent 的「可執行能力」集中到這一個工具上,便於與企業現有 CI/CD、MCP、監控系統整合,把安全策略集中管理。
💡 關鍵: 用多種沙盒 profile 對應不同 Agent 角色與任務,才能在安全與靈活之間做細緻權衡
實作範例
以下用 Python/Node 示範如何把 LLM 工具調用綁定到「在新容器內執行 code」。
1. Docker 镜像設計
這是一個極簡、偏安全的 Python 執行沙盒:
# Dockerfile.sandbox
FROM python:3.11-slim
# 建立非 root 使用者
RUN useradd -m sandbox && mkdir -p /app && chown -R sandbox:sandbox /app
USER sandbox
WORKDIR /app
# 只安裝必要套件
RUN pip install --no-cache-dir pytest requests
# 預設為只讀根檔案系統;允許 /tmp/work 可寫(由 run script 控制)
ENV PYTHONUNBUFFERED=1
CMD ["python", "-u", "main.py"]
注意:
- 不要在這個鏡像裡放企業敏感設定檔或憑證。需要時改用 API gateway + 短期 token。
main.py可以是一個固定的 runner,從環境變數或掛載目錄讀入待執行的 user code。
2. Python:在新容器內執行 Agent 產生的程式碼
假設你在後端定義了一個工具 run_code_in_sandbox 給 LLM 使用:
import subprocess
import tempfile
import uuid
from pathlib import Path
SANDBOX_IMAGE = "my-org/agent-sandbox:latest"
def run_code_in_sandbox(code: str, timeout_sec: int = 10) -> dict:
# 為這次執行建立一次性工作目錄
workdir = Path(tempfile.mkdtemp(prefix="agent-sandbox-"))
script_path = workdir / "main.py"
script_path.write_text(code, encoding="utf-8")
container_name = f"agent-sandbox-{uuid.uuid4()}"
cmd = [
"docker", "run", "--rm",
"--name", container_name,
# 資源限制
"--cpus", "0.5", # 最多半顆 CPU
"--memory", "512m", # 限制記憶體
"--pids-limit", "128", # 限制子行程
# 禁用網路:完全隔離
"--network", "none",
# 根檔案系統掛為 readonly
"--read-only",
# 掛載工作目錄到 /app,並提供 /tmp/work 可寫
"-v", f"{workdir}:/app:ro",
"-v", f"{workdir}/tmp:/tmp/work:rw",
SANDBOX_IMAGE,
]
try:
proc = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=timeout_sec,
)
except subprocess.TimeoutExpired:
# 超時直接 kill 容器
subprocess.run(["docker", "kill", container_name], capture_output=True)
return {"ok": False, "error": "timeout"}
return {
"ok": proc.returncode == 0,
"stdout": proc.stdout,
"stderr": proc.stderr,
"exit_code": proc.returncode,
}
幾個關鍵點:
- 沒有掛宿主機敏感目錄,也沒有掛
/var/run/docker.sock,避免 Agent 直接控制 Docker daemon。 --network none:這個 profile 完全不能出網。若要出網,請另外設計受控 network profile,例如只允許打企業 MCP gateway。--read-only搭配小範圍可寫目錄,避免 Agent 於容器內持久化惡意腳本。
在你的 LLM tool schema 中,可以這樣暴露給模型:
{
"name": "run_code_in_sandbox",
"description": "在隔離的 Docker 容器內執行短程式碼(無網路、有限資源)",
"parameters": {
"type": "object",
"properties": {
"language": {
"type": "string",
"enum": ["python"],
"description": "目前只支援 Python"
},
"code": {
"type": "string",
"description": "要執行的程式碼,必須是單檔腳本"
}
},
"required": ["language", "code"]
}
}
3. Node.js:以 MCP / 工具網關方式整合
如果你採用 MCP 或類似工具網關,建議把沙盒能力封裝成一個 service tool,而不是每個 Agent 都直接呼叫 Docker。
// sandboxTool.ts
import { execFile } from "child_process";
import { promisify } from "util";
const execFileAsync = promisify(execFile);
export async function runInSandbox(params: {
code: string;
profile?: "analysis" | "integration-test";
}) {
const profile = params.profile ?? "analysis";
const dockerArgs =
profile === "integration-test"
? ["--cpus", "1", "--memory", "1g", "--network", "sandbox-staging"]
: ["--cpus", "0.5", "--memory", "512m", "--network", "none"];
const { stdout, stderr } = await execFileAsync("python", [
"run_sandbox.py",
JSON.stringify({ code: params.code, dockerArgs }),
], { timeout: 15000 });
// 在這裡寫 audit log 到你的集中式監控
// logSandboxEvent({ profile, codeSnippet: params.code.slice(0, 500), stdout, stderr })
return { stdout, stderr };
}
在 MCP server 端,你只要把這個 runInSandbox 暴露為工具,並在工具 metadata 中標註:
- scope:只允許特定角色的 Agent 使用(例如
CodeExecutor)。 - audit:所有呼叫自動記錄到審計管線。
這樣一來,企業的 Agent 系統就有一致的安全邊界:所有程式執行都要經過同一層沙盒服務,方便治理與合規。
建議與注意事項
1. 千萬不要掛宿主 Docker socket
最常見也最危險的做法,是為了讓測試方便,直接在容器內掛:
-v /var/run/docker.sock:/var/run/docker.sock
這等同給了容器(也就是 Agent)對宿主機 Docker daemon 的完全控制權,能:
- 啟動任意 privileged 容器;
- 掛載宿主機任意路徑;
- 讀取其他服務的環境變數與機密。
結論:在 Agent 沙盒場景,禁止掛 docker.sock 是硬規則。 需要 orchestrate 容器時,請在宿主或受控 sidecar 上做,而不是讓 Agent 直接控制。
2. 網路出口要明確設計,而不是「先開再說」
- Rovo 被利用,就是因為 Agent默許能打外部網路,把敏感資料送走。
- 建議預設 no egress,再逐步開放:
- 只允許打企業 API gateway。
- Gateway 再依使用者、任務、Agent role 做細粒度授權。
避免一開始就給 Agent 完整的 requests / fetch 能力,卻沒有 outbound policy。
3. 限制 fork、磁碟寫入與長時間運行
- 使用
--pids-limit防止 fork bomb。 - 用
--read-only+ 小範圍可寫目錄限制磁碟寫入,並定期清理一次性目錄。 - 工具實作上務必加 硬超時,不可只依賴 Agent 自己判斷何時該結束。
4. 在企業場景下與現有系統整合
把 Agent 沙盒當成一個可以被納入現有治理架構的「服務」:
- CI/CD:
- 把沙盒鏡像視為一個版本化的 artifact,透過 pipeline 發布。
-
變更權限、套件時要走同樣的審核流程。
-
MCP / 工具網關:
- 透過 MCP 將沙盒工具集中管理,設定 scope(哪些 Agent 角色可以呼叫)、quota(每日執行次數)、審計策略。
-
在多模型、多 Agent 環境中,統一用一套沙盒服務取代每個團隊自己寫的「隨便跑程式 API」。
-
監控與審計:
- 將每次沙盒執行事件(who / which agent / what code / which profile / result)送到 Log system / SIEM。
- 在發現異常 pattern(例如大量嘗試未授權 API 操作)時,能快速追溯與調整策略。
總結:Docker 沙盒的價值不只是「比較安全」,更是讓 Agent 行為可以被管理、可觀測、可審計。只要你把「執行程式」這件事全部強制走沙盒,並搭配最小權限與網路出口策略,你就能在保持開發效率的前提下,大幅降低 Rovo 類型的資料外洩與 OpenClaw 類型的「創意駭客」事件死角。
🚀 你現在可以做的事
- 在本機或測試環境建一個最小權限的
agent-sandboxDocker 鏡像,實驗一次性容器執行程式碼- 把現有 Agent 的「程式執行工具」改成呼叫
run_code_in_sandbox類型服務,強制走沙盒- 檢查你的系統是否有容器掛載
/var/run/docker.sock或無網路出口限制,列出並規劃修補清單










