你的 Agent 正在沙盒里运行。它能写代码、能跑命令、能调用外部 API——这些能力看起来很正常,直到你发现一个问题:它发出的每一个网络请求,都是它自己决定要发的。一个公网上的 AI 助手,如果被提示注入诱导,完全可以向你的内部服务器发起请求,然后把你今天写的那段机密代码作为 POST body 传出去。

所以,当你把 Agent 关进沙盒,你真正要回答的问题不是“让不让它联网”,而是“允许它跟谁说话、用什么端口说”。这就是网络隔离。
断网是最安全的,也是最没用的
我最早接触沙盒是跟人聊天时,对方很自豪地说:“我们给 Agent 的网络权限是彻底隔离,它完全连不了外网。”我第一反应是:那它怎么查文档?怎么调 API?对方说,我们有内部知识库。过了几天他跑来问我,能否让 Agent 直接搜索百度百科。答案是:那就要打开一个口子。
现实中的 Agent 几乎绕不开联网。大模型要调用工具,工具要访问外部资源:要么是搜索引擎,要么是数据库,要么是第三方 SaaS。把网络完全封死,等于把 Agent 的手脚绑住。但全放开又等于把内部网络裸奔给一个可能被提示注入的 AI。所以“精细控制”不是一种优化,而是一种必需。
| 策略 | 安全性 | 可用性 | 适用场景 |
|---|---|---|---|
| 完全断网 | 极高 | 极低 | 纯内部处理,不依赖外部 |
| 完全开放 | 极低 | 极高 | 可信环境,或无敏感数据 |
| 精细白名单 | 中高 | 高 | 需要联网但来源不可全信 |
别看白名单只是几行配置,真要实现到“精细”级别,你会撞上比想象更多的墙。
从“允许 IP”到“允许域名”,这一步比你想象的曲折
最朴素的做法是在网络层堵。比如用 iptables 限制出站 IP 和端口:
iptables -A OUTPUT -d 203.0.113.0/24 -p tcp --dport 443 -j ACCEPT
iptables -A OUTPUT -j DROP
这条规则允许出口访问一个 IP 段上的 HTTPS 端口,其余端口一律拒绝。看起来挺干净,但问题在于,你根本不知道一个域名背后有多少个 IP。尤其是使用了 CDN 的域名,解析结果可能几百个,而且随时会变。你按 IP 开白名单,要么误伤,要么失效。
于是很多人转向代理方案。在沙盒里跑一个正向代理,把 Agent 的所有 HTTP/HTTPS 请求定向到代理,然后在代理层面做域名过滤。比如 Squid 的配置:
acl allowed_domains dstdomain .github.com .npmjs.org
http_access allow allowed_domains
http_access deny all
这样 Agent 只能访问 GitHub 和 npm,其他都被拒绝。而且代理可以记录日志,谁访问了什么一目了然。我后来在项目里用的就是这一套。
但这里有一个很容易踩的坑:Agent 不一定走你的代理。如果 Agent 直接发起 socket 连接,HTTP_PROXY 环境变量根本管不了。要让代理真正“透明”,你还需要把沙盒里发出的所有 TCP 出站流量重定向到代理端口,通常用 iptables 的 REDIRECT 或 TPROXY。这又是网络层的活。
如果你想更彻底地隔离,也可以借用 gVisor 这样的用户态内核,把网络栈也隔离出来,但代价是性能损耗。对于大多数场景,iptables + 透明代理已经够用。
端口只是表象,协议才是实质
在精细控制里,端口过滤是最容易实现也最容易被骗的一层。默认端口确实能提示协议:22 是 SSH,3306 是 MySQL,6379 是 Redis。但一个聪明的 Agent(或攻击者)完全可以在 443 端口上跑一条加密隧道。防火墙看到目标 IP 的 443 端口,觉得是 HTTPS 就放行了,实际里面可能是 SSH 的流量。
所以要控制端口,不能只看端口号,还要看里面跑的是什么“语言”。这就是协议识别。经典的 DPI(深度包检测)需要解密流量,成本高又有合规风险。而 TLS 的 SNI 提供了一个折中的方案:虽然 payload 是加密的,但在握手时,客户端会明文告诉服务器“我要连接的是哪个域名”。用 eBPF 或者代理在协议栈上提取 SNI,比解密流量轻量得多。
举个例子,如果 Agent 尝试访问一个不在白名单里的域名,比如 evil.example.com,即便它的 IP 是 192.0.2.1:443,TLS 握手中的 SNI 也会暴露这个域名。基于 SNI 的过滤器可以直接拒掉。
域名白名单也不是万能的——内容才是
假设你的白名单里允许了 api.sendgrid.com,Agent 确实只连接这个域名,但它可以往这个域名上的任意路径 POST 数据。如果这个 API 本身允许传任意内容,那 Agent 就能把内部数据打包传出去。域名白名单管住了“目的地”,却管不住“货物”。这也是为什么很多沙盒会加一层内容审计:限制请求大小、过滤敏感字段、给模型输出加价值观校验。
换句话说,网络隔离是边界,不是保险箱。真正的数据安全需要你与后续网关合作。
绕过手段:一个能发 DNS-over-HTTPS 的 Agent 会让你怀疑人生
你可能会想,如果我用 eBPF + SNI 过滤,是不是就万无一失了?不是。因为 TLS 握手本身也可能被绕过——如果 Agent 不走标准 TLS,而是用 DNS-over-HTTPS(DoH)把域名解析请求加密在正常的 HTTPS 流量里,那么 SNI 只暴露了 DoH 服务器的域名(比如 dns.google),而你根本不知道它实际要访问的目标是什么。DNS 解析被加密后,你看到的就是一个黑盒子。
要拦截这种绕行,你需要做 TLS MITM(中间人),在沙盒内部替换根证书。但这样做有两个问题:一是 Agent 可能内置证书固定,无视你的根证书;二是中间人解密会破坏端到端加密,可能违反数据安全政策。所以多数沙盒干脆放弃截断 DoH,只做“域名黑洞”式拦截——把已知的公开 DoH 服务器加入黑名单,强制 Agent 只能使用伴生 DNS。但这又陷入了猫鼠游戏。
说实话,没有一种方案是完美的。我现在的做法是分层设防。底层用网络命名空间加 iptables 封 IP 和端口,上层用透明代理做域名白名单,再叠加 eBPF 监控 SNI 异常。每一次 Agent 更新,我会检查它的网络行为是否有变化。你能做的是增加攻击者的成本,而不是消灭所有风险。
FAQ:你以为的“隔离” vs 实际的“隔离”
只要域名白名单够严,是不是就不会被攻击?
不是。域名白名单只约束了“连接谁”,没有约束“发什么内容”。一个合法的域名(比如你的私有云)上的 API,可能被 Agent 用来外传数据。所以还需要配合数据流过滤和审计,比如给外部 API 调用加内容长度限制、关键词检测,或者全部走一个透明代理来记录。
为什么不用 Docker 的 –network 直接指定一个网段?
Docker 的 --network 只能控制容器加入哪个虚拟网络,不能控制容器内进程访问哪些外部域名的细节。它更像是一种拓扑隔离,而精细到域名和端口的控制需要额外的网络策略组件,比如 Kubernetes NetworkPolicy 或 Calico。
沙盒里放一个 Agent,和放一个恶意程序,防护逻辑有区别吗?
有,而且很大。恶意程序的行为是固定的,你可以用静态分析。Agent 的行为是动态的、由大模型生成的,你无法预知它下一步会做什么。所以网络隔离要做得更保守,同时还要应对“提示注入”这种间接利用——它可以让 Agent 主动发起攻击。
如果 Agent 用 HTTPS 直连 IP,不解析域名,那域名白名单是不是就没用了?
对,这也是为什么要做网络层封堵。你可以在底层用 IP 白名单兜底,同时把 HTTPS 请求中的 SNI 作为第二道检查。如果 Agent 试图绕过 DNS 而直连 IP,它无法提供合法的 SNI,就会被默认策略拦下。
进阶阅读:关于 eBPF 与 SNI
eBPF 可以在内核态拦截网络事件,提取 TLS 明文 SNI,不需要改动业务进程。Cilium 和 Kata Containers 都支持相关能力。如果你关心底层实现,可以看 Linux 内核的 bpf_getsockopt 或者 Cilium 的 TLS inspector。
我的判断:精细控制是当前能做到的最好答案,但不是最终答案
回到开头的问题。Agent 沙盒的网络隔离,本质上是一套“最小通信权限”策略。它比断网更务实,比全开更安全。但它的边界也很清楚:它控制的是“连接”,不是“意图”。只要 Agent 还能向外部发送加密数据,你就没法 100% 确定它没有偷偷泄露信息。
未来可能会有更智能的隔离——比如基于语义理解来判断请求是否合理,或者在数据层面做差分隐私。但现在,如果你要部署一个联网 Agent,我的建议是:先用代理白名单把域名细化到可解释的粒度,再叠加 SNI 监控,最后把沙盒的日志全部留着。至少出问题时,你能知道它到底跟谁说过话。
原创文章,作者:guanweilu,如若转载,请注明出处:https://guanweilu.cn/article/491.html