通过防火墙为模型提供商的 IP 地址段设置白名单
“允许所有目标地址的出站 443 端口”,这是每个人起步时的默认规则,但审计时没人愿意为它背书。要把规则收紧到某个特定供应商,有些厂商做起来很简单,有些则根本做不到,区别完全在于对方是否发布并承诺维护 IP 网段。那么,谁发布了什么?
Anthropic 发布了双向的固定 IP 地址,并声明不会在没有通知的情况下变更。在其 IP 地址文档页面上,列出了入站 IPv4 段 160.79.104.0/23 和入站 IPv6 段 2607:6bc0::/48——这些是请求到达 Claude API 时使用的地址——以及一个出站 IPv4 段 160.79.104.0/21,用于 Anthropic 自己的服务主动回调你的场景,例如 MCP 连接器或网页抓取工具调用。该页面还列出了应从现有规则中移除的逐步淘汰地址,这正是你应该亲自去读文档、而不是照抄旧操作手册的理由。上述 CIDR 是撰写本文时 Anthropic 发布的地址。这类页面恰恰是最容易在无声无息中过期的——往防火墙里粘贴任何内容之前,先去看厂商页面本身;这段文字只是给你指个方向,不能代替官方文档。
AWS 发布的是 ip-ranges.json,每条前缀都带有 ip_prefix、region、service 和 network_border_group 字段。AWS 自己明确提到过两点,这里也很要紧:它发布这些网段是“供客户常用于出站过滤的服务”用的,并非覆盖所有服务;而且它自己的出站过滤建议是放行 AMAZON 列表——这个放行范围非常宽,因为实际上等于整个 AWS。AWS 还提醒,可能需要按区域创建多个安全组才能容纳这些规则。
Azure 发布的是服务标签(service tags),可以下载每周更新的 JSON 文件,也可以用 az network list-service-tags --location eastus 查询。Microsoft 表示,新加入服务标签的 IP 地址至少一周内不会在 Azure 中实际使用,这给了你一个真正的时间窗口,在变更引发故障之前接住更新——三家里面,这是对运维最友好的承诺。如果平台支持,在 NSG 规则里直接按名称引用服务标签,比把 CIDR 挨个展开要好得多。
还有几家供应商什么都不发布。
如果厂商的服务挂在一个大型 CDN 后面,它的 IP 地址会跟互联网上相当大一部分网站共用。把这些 IP 地址写死在规则里,得到的规则既太宽泛,又很脆弱。建议直接跳到下文的主机名方案。
把已发布的列表变成规则
不要直接复制粘贴。应该用脚本去获取、过滤、改写,这样以后重新生成规则只是一条命令,而不是折腾一个下午:
curl -sS -O https://ip-ranges.amazonaws.com/ip-ranges.json
# every prefix for one service in one Region
jq -r '.prefixes[]'
通过防火墙将模型提供商的 IP 地址段加入白名单
| select(.region=="us-east-1" and .service=="AMAZON")
| .ip_prefix' ip-ranges.json | sort -u,然后应用结果。
这里有一条重要的纪律:应用步骤必须幂等且可做 diff——你要能看出两次运行之间发生了什么变化,因为正是这些变化会给你带来故障。
aws ec2 authorize-security-group-egress \
--group-id sg-0egress \
--ip-permissions 'IpProtocol=tcp,FromPort=443,ToPort=443,IpRanges=[{CidrIp=160.79.104.0/23,Description="anthropic api"}]'
注意规则数量。AWS 的默认配额是每个安全组 60 条入站规则和 60 条出站规则,IPv4 和 IPv6 分别计算;并且每个安全组的规则数乘以每个网络接口关联的安全组数不能超过 1000。如果把提供商的地址列表逐条罗列为独立规则,任何规模的列表都会很快耗尽配额。
前缀列表优于粘贴 CIDR
客户托管前缀列表(customer-managed prefix list)是一组有名字的 CIDR,你可以从安全组规则或路由表中引用它。它解决了规则数量问题,更重要的是,它把一次更新从多次操作变成了一次操作。AWS 将前缀列表设计为带版本的——最多保存 1000 个版本,新增时会丢弃最旧的——这意味着一次错误的更新可以通过回滚解决,而不是重新构建。
aws ec2 create-managed-prefix-list \
--prefix-list-name anthropic-api \
--address-family IPv4 \
--max-entries 20 \
--entries Cidr = 160.79.104.0/23,Description = "claude api inbound range"
aws ec2 authorize-security-group-egress \
--group-id sg-0egress \
--ip-permissions 'IpProtocol=tcp,FromPort=443,ToPort=443,PrefixListIds=[{PrefixListId=pl-0abc123}]'
有一个容量细节容易踩坑:AWS 规定,当前缀列表被某个资源引用时,该列表的最大条目数会计入该资源的配额——所以一个用 --max-entries 100 创建的前缀列表,即使里面只放了两条,也会占用 100 条安全组规则。max-entries 应该设成一个实际够用的上限,而不是一个宽松的上限。
当没有列表可固定时
如果提供商不发布任何地址列表,IP 白名单就不是正确的控制手段;强行这么做,总有一天会在没有人做任何改动的情况下引发故障。替代方案是过滤名称。
AWS Network Firewall 可以将出站连接的 TLS SNI 与域名列表进行匹配;Squid 这类正向代理也可以用显式的目标白名单实现同样的效果。无论哪种方式,规则读起来都是“只允许这些主机名”,而这正是安全需求在被翻译成 IP 地址之前实际想表达的意思。这里有一个必须正视的取舍:SNI 不经过认证,一个坚决的内部人员可以伪造它,因此基于 SNI 的规则防范的是意外和配置错误,而不是有动机的攻击者。对大多数团队来说,这本来也就是他们的威胁模型——你要防止的是某个依赖悄悄调用一个没人审查过的端点,而不是间谍活动。把这个写进设计文档,直说无妨,别让这条控制被误认为具备更强的防护能力。
无论你使用哪种机制,都要把它固定到一个具体的出口点上。如果工作负载通过 NAT 网关出网,防火墙就应该部署在这条路径上,而网关的 Elastic IP 就是你对外的稳定身份——参见“为出站调用设置 NAT 网关”。
规则会过时,以及如何发现
任何根据已发布列表构建的白名单都是一份副本,而副本会以最糟糕的方式悄悄过期:在提供商扩容到新网段之前它一直有效;一旦提供商启用了新的 IP 段,你的请求就会开始一部分失败、其余仍然成功。这种部分失败比完全失败更难排查,因为看起来像网络抖动,而不是配置问题。
按计划重新拉取源列表——每天一次并不过分——然后与你的前缀列表做比对。AWS 对 ip-ranges.json 的变更提供 SNS 通知;Azure 的文件则每周更新,并且内置了一周的提前通知。对差异发出告警,而不是盲目应用;提供商范围的意外扩大值得人工看一眼。
同时为你预期的故障模式设置监控:如果到提供商的连接超时与一小部分解析出来的地址有相关性,这就是白名单过期的特征,应当与一般的提供商故障区分开,单独设置告警。把规则的来源信息记录在描述字段里。
六个月后,“这条 CIDR 是从哪来的”就成了一个很现实的问题,而答案应该就写在规则里。CIDR(无类别域间路由)是表示 IP 段的标准写法。你维护的出站规则数量,会跟着你直接调用的服务商数量一起增长;而且每家服务商都有自己的 IP 发布策略、变更节奏和故障特征。通过一个组件来转发模型流量,防火墙就只需要对一个目标地址做判断——这也是像 Multigrid 这样的网关,在加白名单时比五家供应商简单得多的架构原因,更别说它还有其他功能了。
相关阅读: