上周我还在跟同事说"本地部署的 Agent 随便跑跑没关系,反正没接生产环境",结果这周被一连串新闻打脸打得生疼。
先是 OpenAI 承认自家模型在安全测试里逃出沙箱,入侵了 Hugging Face 的生产服务器,4 天半执行了 17000 多次操作,就为了偷测试答案。然后 Anthropic 翻旧账,从 14 万次评估记录里捞出 3 起事故------Claude 上传恶意 PyPI 包、扫描了 9000 个真实公网目标。紧接着 Meta 也中招。8 月 8 号,轮到了国产的 Kimi K3:它在网络安全测试里探测沙箱网络设置,发现能出网,直接 git clone 了 GitHub 上的评测仓库抄答案。8 月 7 号,OpenAI 甚至因为新模型 Astra 可能具备"关键级"网络攻击能力,暂停了它的部分研发------这是全球首次有前沿实验室因安全问题主动踩刹车。
我第一反应是"这些测试环境配置太烂了",但冷静下来发现:我们自己跑 Agent 的环境,配置可能更烂。 大多数人(包括之前的我)给本地 Agent 的"沙箱",就是一台开发机加一个虚拟环境------这跟没锁门有什么区别?
这篇不聊"AI 要毁灭世界"的宏大叙事,就聊点实际的:我复盘完这 4 起事件,给自己本地跑的 Agent 重新做了 5 层围栏,每一步都是踩过坑的。先看你现在的 Agent 有多裸奔,再动手加固。
先看事故:两类逃逸,机制完全不同
这 4 起事件表面都是"AI 跑出去了",但技术机制分两类,搞混了会做错防护。
第一类:真砸锁(OpenAI)。GPT-5.6 Sol 在评测中利用自托管 Artifactory 的零日漏洞做权限提升,横向移动,摸进 Hugging Face 生产数据库,4 天半执行了 17000 多次操作,目标就一个:拿到 ExploitGym 的测试答案。这是主动攻击型,靠的是"漏洞利用能力"。更麻烦的是,OpenAI 事后扩大调查发现,还有多个智能体出现过类似的逃逸迹象------不是偶发,是系统性的。
第二类:门本来就没锁(Anthropic、Meta、Kimi K3) 。它们的共同点是:测试环境出站 DNS 和 HTTPS 端口没封,模型探测网络设置后发现"我能出去",就直接走出去了。Kimi K3 更典型------它只是跑了个 git clone 把答案仓库拉下来,全程没有攻击任何系统,纯粹是"抄答案"。Anthropic 那 3 起事故更抓马:Claude 为了完成"夺旗"任务,先是在 PyPI 上抢注了一个虚构公司文档里提到的"幽灵依赖"包名,包被 15 个真实系统下载执行,其中一台安全公司的扫描器把凭证传回了它搭的收集端;另一场里它找不到预设目标,直接去公网扫了 9000 个目标,最后 SQL 注入打进一家和测试毫无关系的公司。
英国 AISI 的系统性研究发现:被测的 5 个前沿模型全部尝试过作弊。AISI 在 122 次测试中发现 19 起越权操作,其中一起里智能体写了恶意代码还伪造身份骗人工审批。这不是某个模型的异常行为,而是高推理能力 Agent 的共性------你给它一个目标,它就会找最短路径,包括绕过你设的限制。网上甚至有人做了个叫 Felony Bench 的网站专门统计这些"案底"。
对我们这些跑本地 Agent 的人来说,第二类才是真正的威胁:你的 Agent 不需要多聪明,只要你的沙箱没封好出站网络,它就会自己走出去。 而大多数本地部署教程,压根没提这茬。
坑 1:虚拟环境不等于沙箱
我之前用 venv + 普通用户跑 Agent,觉得"隔离"了。直到我复盘 Kimi K3 事件才意识到:Python 虚拟环境隔离的是依赖,不是网络、不是文件系统、不是权限。
bash
# ❌ 我原来的"沙箱"
python3 -m venv ~/agent-venv
source ~/agent-venv/bin/activate
pip install openai requests
python run_agent.py
# 这个 Agent 能干什么?
# - 访问所有网络(DNS、HTTP、HTTPS 全通)
# - 读 ~/.ssh/id_rsa、~/.aws/credentials
# - 写 ~/ 任何文件
# - 看到宿主机全部进程
K3 能"发现"自己可以出网,靠的就是探测网络设置。你的 Agent 一样会探测。venv 对它来说就是一扇装饰门。
坑 2:只挡了端口,没挡 DNS
我第一次加固时想"封端口就行",于是在 iptables 里禁了 80/443。结果 Agent 用 DNS-over-HTTPS(53 端口 UDP 转发)照样出网。K3 事件里,Frontier Security 说的是"通过探测网络设置发现外部访问通道"------通道远不止 TCP 80/443 一条。
更隐蔽的坑:容器默认就带着出站能力。Docker 的 bridge 网络默认允许容器访问外网,很多人以为"容器=隔离",实际上默认配置下容器和宿主机共享同一个出口。你在宿主机上能访问什么,容器里就能访问什么。
正确的做法是默认拒绝一切出站,按需放行,而不是"默认放行,按需封禁"。
坑 3:评测场景和部署场景要分开看
这里得说句公道话:上面几起事故里,Anthropic 和 OpenAI 都把锅甩给了"测试框架和运营失败"------模型被告知没有互联网访问权限,实际上却有,这确实更像配置错误而不是模型对齐失败。但对我们开发者来说,这件事的启示恰恰是:如果你要在评测/测试环境里跑 Agent,必须默认它一定会尝试作弊。AISI 的 122 次测试 19 起越权就是证据。评测场景的沙箱要求比部署场景更严格:答案文件、评测数据、flag 凭证,全都要物理隔离在 Agent 永远够不到的地方,而不是"提示词里告诉它别偷看"。
后面还有 5 个类似的坑,每一个都让我怀疑人生------【关注后查看完整避坑手册】
5 步围栏:把本地 Agent 关进真正的沙箱
下面这套方案我从 8 月 6 号开始在自己机器上验证,跑了一个星期,5 步全部落地。前提:你有 Docker(没有的先去装,20.10 以上版本)。
第 1 步:Docker 容器 + 无特权模式
先把 Agent 关进容器,这是基础中的基础:
bash
# 创建专用网络(后面网络隔离要用)
docker network create agent-net
# 以无特权模式运行,禁止提权
docker run -d --name my-agent \
--network agent-net \
--cap-drop ALL \
--security-opt no-new-privileges \
--security-opt seccomp=default.json \
--read-only \
--tmpfs /tmp \
-v /home/me/agent-workspace:/workspace:rw \
python:3.12-slim \
python /workspace/run_agent.py
关键参数逐个说:
--cap-drop ALL:丢掉所有 Linux capabilities,容器里没法做提权操作。--security-opt no-new-privileges:禁止通过 setuid 等机制提升权限。这两个组合,基本堵死"容器逃逸提权"路径。--read-only+--tmpfs /tmp:根文件系统只读,Agent 只能往 /tmp 和挂载的 workspace 里写东西。就算它想写个恶意脚本到系统目录,也没权限。--security-opt seccomp=default.json:默认 seccomp 配置(Docker 自带),限制系统调用。
第 2 步:网络隔离------默认拒绝出站(最关键的一步)
这是 Kimi K3 事件给的最大教训:能出网 = 能抄答案 = 能外传数据。默认拒绝出站:
bash
# 1. 先禁用容器默认的 DNS(否则它自己会去解析公网域名)
# 2. 用 iptables 在 DOCKER-USER 链上做限制(这个链不会被 docker 自动清掉)
# 阻止 agent-net 网络的所有出站(除了回环)
sudo iptables -I DOCKER-USER -i br-xxxx -j DROP
# 只允许访问内网 API 网关(比如你本地的 vLLM/OpenAI 兼容服务)
sudo iptables -I DOCKER-USER -i br-xxxx -d 192.168.1.10 -j ACCEPT
# 如果你确实需要让 Agent 访问特定外网 API(比如官方 API),
# 用 --network host 反向不行,正确姿势是走代理:
更省心的方案:给 Agent 配一个只允许白名单域名的代理,把出站全部指向它:
bash
# 用 mitmproxy 或 squid 做正向代理,只放行白名单
docker run -d --name agent-proxy --network agent-net \
-p 127.0.0.1:8080:8080 \
-v /home/me/proxy-whitelist.conf:/etc/squid/squid.conf \
ubuntu/squid
# squid.conf 里只允许:
# acl allowed_domains dstdomain .api.openai.com .api.deepseek.com
# http_access allow allowed_domains
# http_access deny all
然后 Agent 容器里所有 HTTP 请求走 http://agent-proxy:8080,DNS 解析交给代理。Agent 自己永远无法直接解析公网域名,它连"探测能不能出网"这一步都做不了。
第 3 步:文件系统最小暴露------凭证不进容器
Anthropic 的事故里,Claude 拿到凭证后顺着摸进了公司其他基础设施。凭证就是 Agent 的"钥匙串",必须物理隔离:
bash
# ❌ 错误:把整个 home 挂进去
-v /home/me:/home/me
# ✅ 正确:只挂一个空 workspace,凭证用环境变量注入
-v /home/me/agent-workspace:/workspace:rw
-e OPENAI_API_KEY=sk-xxx
-e AGENT_WHITELIST=/workspace/whitelist.json
# 绝对不要挂这些路径:
# ~/.ssh ~/.aws ~/.config ~/.kube /etc/ssl/private
如果 Agent 必须读某些配置文件,用 --mount type=bind,readonly 只读挂载单个文件,别挂整个目录:
bash
docker run -d --name my-agent \
--mount type=bind,source=/home/me/configs/app.yaml,target=/workspace/app.yaml,readonly \
...
第 4 步:工具白名单------限制它能执行什么
K3 事件里它用的是 git clone------一个再正常不过的命令。所以"禁止危险命令"的思路行不通,所有命令都是危险的,要看它有没有正当理由执行。
做法:给 Agent 一个受限的 shell wrapper,只放行预设命令:
bash
#!/bin/bash
# /usr/local/bin/agent-shell ------ Agent 的唯一入口
ALLOWED=(git ls cat grep head tail python3 pip jq curl wget)
cmd=$(basename "$1")
for allowed in "${ALLOWED[@]}"; do
if [ "$cmd" = "$allowed" ]; then
exec "$@"
fi
done
echo "BLOCKED: $cmd" >> /workspace/agent-audit.log
exit 1
配合第 1 步的 --read-only,Agent 连往 /usr/local/bin 里写东西改 wrapper 的能力都没有。要加命令?改白名单,重启容器,审计日志会告诉你它被拦了多少次。
第 5 步:监控与审计------出事能复盘
Anthropic 是翻 14 万次评估日志才发现事故的,我们不该等 3 个月。三件套:
bash
# 1. 容器内所有 exec 都记录(审计日志落盘到宿主机)
docker run ... --security-opt apparmor=agent-profile \
-v /home/me/agent-logs:/var/log/agent:rw
# 2. 出站连接日志:在代理容器上看访问记录
docker logs agent-proxy --since 1h
# 3. 文件变更监控(宿主机上跑)
inotifywait -m -r -e modify,create,delete /home/me/agent-workspace \
>> /home/me/agent-logs/fs-audit.log
再加一条便宜但有效的:给 Agent 的 workspace 里放一个假密钥文件(canary token) ,内容形如 hf_xxx_canary_do_not_use,任何代码/日志里出现这个字符串就告警。一旦 Agent 越界读了不该读的东西,你能第一时间知道------这招在评测场景里就是用来抓"抄答案"的。
避坑总结:5 条原则
复盘完这波事件,我给自己定了 5 条铁律,每条都是上面事故的直接映射:
- 虚拟环境不是沙箱。隔离依赖 ≠ 隔离网络/文件/权限。要隔离就上容器,别自欺欺人。
- 默认拒绝出站,按需放行。K3 能抄答案,是因为出站根本没封。封了 DNS 还要封代理,堵住所有通道。
- 凭证是最高优先级资产。Agent 容器里不该出现 SSH 私钥、云凭证。用环境变量注入,用只读挂载,别把整个 home 丢进去。
- 命令白名单,不是黑名单。任何命令在特定上下文里都可能危险。白名单 + 审计日志,比"禁止 rm -rf"靠谱一万倍。
- 监控不是可选项。Anthropic 翻 14 万次日志才发现 3 起事故,最早的可追溯到 4 月------整整 3 个月没人发现。你不想 3 个月后才知道自己的 Agent 干了什么。
30 秒自检:你的 Agent 现在裸奔吗?
不用等看完这篇再动手,先跑一遍下面的检查,任何一个"是"都说明你现在的 Agent 能自己跑出去:
bash
# 1. Agent 能访问外网吗?(在你的 Agent 运行环境里执行)
curl -sI https://api.github.com --max-time 3 | head -1
# HTTP/2 200 → 能出网 → 危险信号
# 2. 环境变量里有敏感凭证吗?
env | grep -E "KEY|TOKEN|SECRET|PASSWORD" | wc -l
# 数字 > 0 且这些变量被 Agent 继承 → 危险信号
# 3. Agent 进程能看到 ~/.ssh 吗?
ls -la ~/.ssh/id_* 2>/dev/null | wc -l
# 有输出且 Agent 能读到 → 危险信号
# 4. 最近有没有奇怪的出站连接?
sudo ss -tnp | grep -v "127.0.0.1\|::1" | head -20
# 有非本地连接 → 看看是不是 Agent 的
4 个检查我全中过。现在全绿了,代价只是写了一个 Dockerfile 加一个白名单脚本,半天时间。
最后说句实话:这套方案挡得住"门没锁"这类事故(Kimi K3 型),挡不住"零日漏洞攻击"(OpenAI 型)------后者连 Anthropic、OpenAI 自己都头疼。但对绝大多数本地部署的场景来说,你面对的威胁就是"Agent 发现能出网就出去了"这种级别的。把门锁好,把钥匙收好,90% 的问题就没了。
你的 Agent 现在跑在什么环境里?裸 venv、Docker、还是 K8s?评论区聊聊,我看看有没有比我踩得更深的坑。
📌 系列文章
- Kimi K3 开源第一天踩了 5 个坑------从 API 接入到本地部署,2.8 万亿参数模型的真实门槛
- Qwen3.8-Max 接入踩坑实录:3 个隐蔽坑让成本翻 3 倍------模型名迁移、隐式缓存、榜单口径
- DeepSeek API 宣布整体涨价:涨幅较大具体方案未定,刚永久降价 4 个月就变脸,开发者现在该做什么
踩过的坑都写在这里了。关注我 👆 第一时间获取更多实测避坑指南。