跳到主要内容

SSRF 防护

AIDY 会在最终发起 upstream HTTP 请求时执行 SSRF 防护。它不是 route plugin,也不使用 egress_policy 命名,避免和已有 Egress Policy 插件混淆。

防护覆盖:

默认行为

如果静态配置和 tenant 都没有设置 SSRF 策略,系统内置默认值为:

[upstream.ssrf_protection]
enabled = true
allow_private_network = false
domain_list_mode = "blacklist"
domain_list = []
ip_list_mode = "blacklist"
ip_list = []
allowed_ports = []

含义:

  • 默认启用 SSRF 防护
  • 默认不允许访问私网、loopback、link-local、metadata 等非公网地址
  • 默认没有域名/IP 黑白名单
  • 默认允许所有端口
  • DNS 解析后的 IP 过滤始终开启,没有单独配置项

策略来源

SSRF 策略有两层来源:

  1. 静态配置 [upstream.ssrf_protection] 提供全局默认策略
  2. tenant 资源的 ssrf_protection 可以覆盖该 tenant 的整套策略

tenant 未设置 ssrf_protection 时继承静态默认。tenant 一旦设置 ssrf_protection,会整套覆盖静态默认,不做字段级 merge。

Provider 是 deprecated 兼容资源,不参与 SSRF 策略计算。

静态配置

[upstream.ssrf_protection]
enabled = true
allow_private_network = false
domain_list_mode = "blacklist"
domain_list = []
ip_list_mode = "blacklist"
ip_list = []
allowed_ports = []

字段说明:

字段说明
enabled是否启用 SSRF 防护;为 false 时跳过检查
allow_private_network是否允许访问私网、loopback、link-local、metadata 等非公网地址
domain_list_modeblacklistwhitelist
domain_list域名名单,支持精确域名和 *.example.com
ip_list_modeblacklistwhitelist
ip_listIP 名单,支持 IPv4、IPv6、单 IP 和 CIDR
allowed_ports允许的端口;空数组表示允许全部,支持单端口和 8000-8999 范围

Tenant 覆盖

tenant 资源上可以设置 core.SSRFProtectionConfig

{
"id": "tn_01ARZ3NDEKTSV4RRFFQ69G7AA0",
"name": "demo",
"ssrf_protection": {
"enabled": true,
"allow_private_network": true,
"domain_list_mode": "blacklist",
"domain_list": [],
"ip_list_mode": "blacklist",
"ip_list": ["169.254.169.254", "fd00::/8"],
"allowed_ports": ["80", "443", "8000-8999"]
}
}

注意:tenant 配置是整套覆盖。上例设置后,这个 tenant 不再继承静态配置里的 domain/IP/port 列表。

检查规则

当某个 upstream 没有生效代理时,AIDY 会按以下顺序检查最终请求目标:

  1. 只允许 httphttps
  2. 检查 host 和端口
  3. 域名请求先匹配 domain list,再解析 DNS
  4. 直接 IP host 和域名解析出的 IP 都进入 IP 检查
  5. 所有 DNS 解析结果都必须通过检查,任一 IP 不合规都会拒绝本次请求
  6. allow_private_network=false 时拒绝非公网地址
  7. allow_private_network=true 时允许非公网地址,但仍然受 IP 黑白名单和端口限制约束

非公网地址包括但不限于:RFC1918 私网、loopback、link-local、metadata 地址、保留地址、组播地址和 IPv6 ULA/link-local 等范围。

名单规则

Domain list

domain_list 支持两种写法:

domain_list = ["api.example.com", "*.trusted.example.com"]
  • api.example.com 只匹配这个精确域名
  • *.trusted.example.com 匹配其子域名,例如 a.trusted.example.com
  • 通配符只支持 *. 前缀形式

IP list

ip_list 支持单 IP 和 CIDR,IPv4 / IPv6 都可用:

ip_list = ["203.0.113.10", "10.0.0.0/8", "2001:db8::/32"]

Allowed ports

allowed_ports 为空时允许所有端口;非空时只允许列出的端口或范围:

allowed_ports = ["80", "443", "8000-8999"]

代理行为

如果 upstream 生效的 proxy_url 非空且不是 "-",AIDY 会跳过 SSRF 检查,由代理侧承担网络访问控制。

proxy_url: "http://proxy.internal:8080" # 跳过 SSRF 检查
proxy_url: "socks5://127.0.0.1:1080" # 跳过 SSRF 检查
proxy_url: "-" # 显式直连,仍执行 SSRF 检查
proxy_url: "" # 未配置代理,执行 SSRF 检查

拦截结果

运行时 upstream 请求被 SSRF 防护拦截时,AIDY 返回 403 Forbidden,body 会包含明确原因。例如:

ssrf target blocked; IP is not public; host=127.0.0.1; port=8080; resolved_ips=127.0.0.1; rule=private_network

同一条错误也会写入 request log 的 upstream_requests[].meta.error 字段,避免退化成泛化的 transport error。多 upstream 重试时,每个被拦截的 attempt 都会记录独立的 upstream request;如果所有候选都被拦截,最终响应会保留 SSRF 拦截原因。

Management API 的行为: