Skip to content
← 全部文章

你的 AI Agent 大概率正裸奔在公网上

自部署 Agent 网关的默认绑定是为方便设计的,不是为安全。这篇讲暴露是怎么发生的、为什么没有任何东西会提醒你,以及怎么花几分钟查一遍自己的部署。

5 分钟读完 安全自部署

把 Agent 暴露在公网上,不会有任何东西坏掉。问题恰恰就在这里。

数据库配错了会抛连接错误,部署挂了会返回 500。而一个监听在 0.0.0.0、没有鉴权的 Agent 网关,行为和一个配置正确的网关一模一样——直到别人开始跟它说话为止。没有报错,没有告警,响应时间也不会变差。这种故障的表现形式是「安静」。

这篇讲这件事是怎么发生的、为什么这么容易被忽略,以及怎么检查你自己的部署。

一个笔记本项目是怎么变成公开端点的

几乎没有人是主动决定把 Agent 暴露出去的。它是分三步发生的,而每一步看起来都很合理。

第一步:你在本地开发。 网关绑在 localhost,或者你把它设成 0.0.0.0,因为你想用同一个 wifi 下的手机访问它。在家用路由器后面的笔记本上,0.0.0.0 是无害的——网络外面的东西根本路由不到它。你由此学到「这个设置能用」,然后就翻篇了。

第二步:你把它挪到服务器上。 Agent 挺好用,不该在笔记本合盖的时候就死掉。于是你把配置复制到 VPS 上。那份配置里还写着 0.0.0.0。在 VPS 上,0.0.0.0 的意思是「所有网络接口」,而其中一个接口上挂着一个公网 IP,前面没有任何路由器。

第三步:什么都没发生。 你的 Agent 工作正常,你用了好几个星期。日志里也看不出异常——因为陌生人给你的 Agent 发一条消息,产生的日志行和你自己发一条一模一样。

第二步和第三步之间的那个缝,很多部署一待就是好几个月。

为什么默认值是这个朝向

把锅甩给软件很容易,但这些默认值之所以如此,有一个站得住脚的理由:一个只绑 localhost 的网关开发起来很烦,任何采用这个默认值的项目都会收到源源不断的「我从另一台机器连不上」的 issue。

结果就是,Agent 运行时把默认值优化给了「笔记本场景」——软件第一次被跑起来的地方,而不是「服务器场景」——软件最终待着的地方。对项目来说这是个合理选择,对你个人来说这是个糟糕结果。

规模上也该说实话。关于暴露在公网的 Agent 与 LLM 周边部署——开放的 Ollama 端点、无鉴权的向量数据库、Agent 网关——在全网扫描数据里定期都能翻出来,数量级是数以万计。不论这个月的确切数字是多少,形状是一致的:很多人正处在第三步,而且不知道。

一个暴露的 Agent 到底会让你付出什么

「别人能跟我的 Agent 说话」这个说法太轻了。Agent 不是聊天机器人,它是一个长了手的聊天机器人。

你的模型额度。 最常见的后果,描述起来最便宜,收到时最恼火:有人发现了一个开放端点,把它当免费推理用。他们的 token,你来付钱。这已经算好情况了。

你的工具。 Agent 是配了工具的——shell 访问、文件操作、HTTP 请求,有时还有别的服务的凭据。任何能给这个 Agent 发消息的人,都可以在你的工具配置和提示词允许的范围内,让它去用这些工具。爆炸半径不是那次对话,而是这个 Agent 被允许碰到的一切。

你的数据。 会话历史、工作区文件、以及你喂给 Agent 的任何上下文。Agent 读得到的东西,调用者通常都能让它复述出来。

最终还有你的信誉。 一个有出网能力、且受他人控制的 Agent,就是一台以你的名义、从你的 IP 发出流量的机器。

严重程度是由你的工具配置决定的,不是由「暴露」这件事本身决定的。这也正是为什么「有没有暴露」和「它能干什么」这两件事必须一起查。

查一遍你自己的

几分钟的事。在跑 Agent 的那台机器上做。

1. 它到底在监听什么?

ss -tlnp | grep -E 'LISTEN'

找你的 Agent 网关用的那个端口。如果本地地址那一列显示的是 0.0.0.0:端口*:端口,说明它绑在了所有接口上。127.0.0.1:端口 才是只绑本机——除非你是有意为之,否则这才是你想要的。

2. 那个端口从外面能不能连上?

绑在 0.0.0.0 不等于一定可达——前面可能还有防火墙或云安全组挡着。从一台不是这台服务器、也不在同一个网络里的机器上查:

curl -m 5 -sv http://你的服务器IP:端口/ 2>&1 | tail -20

如果是连接被拒绝或者超时,说明有东西挡住了。如果拿到了 HTTP 响应,那它就是对公网开放的。

3. 它会不会问你是谁?

这是最要紧、也最容易被跳过的一问。一个前面立着可靠鉴权的开放端口,和一个没有鉴权的开放端口,是两种完全不同的处境。如果一个未经认证的请求得到的响应不是「拒绝」,那你就是没有鉴权。

4. 一个调用者能够到什么?

去看你 Agent 的工具配置和工作区范围。假设此刻有个不受信任的人正在给它发消息——他们能让它做什么?这份清单上任何一条让你不舒服的,都是一条发现,无论今天这个端点最终是不是暴露的。

怎么修

大致按「收益从大到小」排:

绑到 localhost。 如果你就是在同一台机器上访问它,127.0.0.1 直接消灭整整一类问题。这份清单上其它的都是缓解,只有这一条是根除。

前面架一个带鉴权的反向代理。 如果你确实需要远程访问,别把网关直接暴露出去。在 nginx 或 Caddy 上终结 TLS 并完成鉴权,再转发到本机的网关。

用私有网络,而不是公网。 WireGuard 或 Tailscale 能给你远程访问,同时不需要一个公网可路由的端口。对单人使用的场景,这通常比听起来省事,而且严格优于「在公开端点上加个密码」。

加一条防火墙规则。 云安全组和 ufw 是粗糙但有效的兜底,而且当某次配置变更重新打开了你以为关着的东西时,是它在保护你。

缩小工具面。 这和访问控制是两件事:一个跑不了任意 shell 命令的 Agent,在真有人进来的时候是个小得多的麻烦。限定工作区范围,删掉你根本没在用的工具。

在服务商那里设消费上限。 这不是安全控制,但它给最可能发生的那种后果封了个顶。

不会自己修好的那部分

上面每一条都是一次性动作,而且每一条都可能被悄悄撤销:一次配置还原、一次用旧镜像重建容器、一次为了「让手机能连上」而做的善意改动。检查本身不难,难的是记得重复做——而这是件很不适合交给记性的事。

这正是 GambaOS 里安全体检存在的原因:它跑同一类检查——网关绑定、鉴权方式、渠道开放策略、工作区范围——给出评分,并把每条发现链到能改它的那个页面。默认部署第一次跑通常落在五十几分,这个数字比听起来有用:它说明「能用」和「安全」之间存在落差是常态,不是你个人的失职。

如果你在服务器上跑着 Agent,现在就去把第一步跑一遍。三十秒的事,而且它很有可能告诉你一些你没料到的东西。

上线时第一时间拿到 GambaOS

这篇文章里说的那个面板很快开放下载。留个邮箱,第一批进来。

上线时发一封邮件,随时可以退订。