Agent访问内网服务SSRF风险
Agent 程序如果部署在公网服务器上,同时又允许它访问内网服务,就很容易被攻击者利用形成 SSRF(服务端请求伪造)风险。
简单说,攻击者不需要直接控制你的内网机器,只需要诱导 Agent 替你发起请求,就可能探测内网端口、读取内网数据,甚至拿到内网服务的控制权限。
本文给出一套零基础可执行的网络层隔离策略,把 Agent 放进独立网络区域,从底层切断它主动访问内网服务的路径,并附上验证命令和避坑说明。
先搞清楚 Agent 的访问路径
要隔离风险,先要画出 Agent 的访问路径。
一般有两类:
- 正向代理型 Agent:Agent 对外提供服务,接收外部请求后再向后端内网服务转发。这种结构风险最大,因为外部输入会直接影响 Agent 的请求目标。
- 主动回调型 Agent:Agent 主动连接外部的管理端或数据源,通常不监听公网端口,但同样可能因为配置错误而访问内网地址。
不管是哪一类,核心问题都一样:Agent 所在主机的网络层没有限制目标地址范围。
所以网络层隔离策略的目标很明确——让 Agent 进程只能访问必要的目标,不能访问内网保留网段。
需要准备的东西不多:一台 Linux 服务器(本文以 Debian/Ubuntu 为例)、一个 Docker 环境(用于隔离运行 Agent)、以及 root 权限。
没有 Docker 也可以直接用 iptables/nftables 做限制,但隔离效果不如容器方案彻底。
用独立网桥把 Agent 和内网彻底分开
最常见的做法是把 Agent 放进一个自定义 Docker 网桥里,让它只能访问你指定的上游地址,而不是整个内网。
第一步:创建独立网桥
docker network create --driver bridge --internal agent-net
--internal 参数非常关键,它让这个网桥不连接宿主机外部网络,Agent 无法直接访问任何外网。
如果你的 Agent 必须访问外网 API,则不要加这个参数,改为下面这种方式:
docker network create --driver bridge --subnet=172.28.0.0/24 --gateway=172.28.0.1 agent-net
第二步:启动 Agent 时指定网络
docker run -d --name my-agent --network agent-net your-agent-image
第三步:确认 Agent 容器内无法访问内网地址
docker exec my-agent curl --connect-timeout 3 http://10.0.0.5
这条命令应该超时,而不是返回内网服务的内容。
如果超时,说明网络隔离已经生效。
如果 Agent 必须访问某个指定的内网服务(比如数据库 10.0.7.10:3306),不要直接开放整个内网段,而是在宿主机上用 iptables 做单点放行:
iptables -A FORWARD -i agent-net -o eth0 -d 10.0.7.10 -p tcp --dport 3306 -j ACCEPT
iptables -A FORWARD -i agent-net -o eth0 -j DROP
这样 Agent 只能触达明确放行的内网地址,就算被攻击,也没法横向扫描内网其他机器。
在宿主机网络层加一道出方向访问控制
如果 Agent 不是跑在 Docker 里,而是直接跑在宿主机上,可以在宿主机用 iptables 限制进程访问内网网段。
这里用 owner module 按用户锁定:
# 创建专用系统用户,不让 Agent 用 root 运行
useradd -r -s /usr/sbin/nologin agentuser
# 禁止该用户访问常见内网网段
iptables -A OUTPUT -m owner --uid-owner agentuser -d 10.0.0.0/8 -j DROP
iptables -A OUTPUT -m owner --uid-owner agentuser -d 172.16.0.0/12 -j DROP
iptables -A OUTPUT -m owner --uid-owner agentuser -d 192.168.0.0/16 -j DROP
iptables -A OUTPUT -m owner --uid-owner agentuser -j ACCEPT
注意规则顺序:DROP 规则必须放在 ACCEPT 之前,否则一眼就被放行,白写了。
不要忘记本地回环地址。 很多 Agent 被利用后会先访问 127.0.0.1 上的本地代理或管理端口,所以建议再加一条:
iptables -A OUTPUT -m owner --uid-owner agentuser -d 127.0.0.0/8 -j REJECT
如果你的 Agent 确实需要连接本机服务,这条可以去掉,但一定要确认本机没有敏感端口暴露在 loopback 上。
规避高频踩坑点
只限制端口不限制网段是没用的。
攻击者可以把请求打到内网其他端口,你限制了 3306,他还能扫 6379、9200、5432。
一定要按网段限制,而不是按端口限制。
不要依赖应用层 URL 校验。 很多 Agent 会做 ssrf-protection,但绕过方式太多,重定向跳转、DNS rebinding、IPv6 映射、八进制 IP 都能绕过。
应用层校验只做兜底,网络层隔离才是主防线。
Docker 网桥和宿主机 iptables 的联动要注意。 如果你用 --internal 网桥,但是启动 Agent 时手动 map 了端口,比如 -p 8080:8080,宿主机的端口映射仍然会把外部请求带进去。
这种情况下,需要配合防火墙限制宿主机入口流量,只放行必要的对外端口。
规则重启后会丢失。 iptables 规则默认不持久化,测试完一定要保存。
Debian/Ubuntu 上可以先装 iptables-persistent,然后:
apt install -y iptables-persistent
netfilter-persistent save
Docker 的 --internal 网桥不受影响,但 iptables 规则一定要保存,否则重启后 Agent 又恢复原状。
验证策略是否真的挡住内网访问
配置完成后按三步做验证:
第一步:测试内网阻断。
sudo -u agentuser curl --connect-timeout 3 http://10.0.0.1
sudo -u agentuser curl --connect-timeout 3 http://172.16.0.1
sudo -u agentuser curl --connect-timeout 3 http://192.168.1.1
三条命令全部超时才算正常,返回内容说明规则没生效。
第二步:验证 Agent 正常功能。 如果 Agent 还需要访问外网 API,确认 curl https://api.example.com 能正常返回。
这里注意,如果你把整个 OUTPUT 链默认策略改成 DROP,需要先放行回环和 DNS 解析:
iptables -A OUTPUT -m owner --uid-owner agentuser -d 127.0.0.53 -p udp --dport 53 -j ACCEPT
第三步:看日志确认没有异常流量。 在 iptables 里给 DROP 规则加上日志记录,再观察一段时间:
iptables -I OUTPUT -m owner --uid-owner agentuser -d 10.0.0.0/8 -j LOG --log-prefix "SSRF-DROP: " --log-level warn
然后查看 /var/log/kern.log 或 /var/log/syslog,
如果频繁出现 SSRF-DROP 记录,
说明有请求在尝试访问内网地址,
需要回头检查 Agent 的配置,
看是业务需要还是已经被攻击者利用。
按这套方案操作完,Agent 就算代码层面有 SSRF 漏洞,攻击者也拿不到内网服务的访问权限。
网络层隔离是最后一道防线,配置成本低,收益却很实在。
如果你正在处理 Agent 访问内网服务的 SSRF 风险,建议先按本文步骤完整执行,再根据自己的环境做微调;
遇到异常时优先回看避坑和高频问题部分。