Buzeli
buzeliSoluções Digitais
成本

每月342美元的不必要出口流量:如何通过Security Group审计识别绕过CDN的客户

发布于 2026年5月3日

背景:强制使用CDN的多租户平台

该平台在共享和专用EC2实例上托管数十个WordPress站点。安全和成本模型依赖于所有站点通过CDN接收流量——GoCache、Cloudflare或Akamai,取决于客户。每个实例的Security Group只允许使用中的CDN的IP范围访问80和443端口。

当DNS正确指向CDN且Security Group对齐时,EC2出口流量极少——内容由CDN从缓存提供,无需在每次请求时都访问源站。AWS数据传出费用只在源站被访问时产生,而不是在缓存命中时。

CDN配置正确 = EC2出口接近零。CDN已配置但DNS绕过了它 = EC2全量出口,CDN缓存费用,同一内容付了两次钱。

信号:2月份单个域名花了342美元

通过CloudWatch NetworkOut(比Cost Explorer在隔离实例方面更精确)进行的月度数据传输分析,在排名中发现了异常:

复制
# CloudWatch NetworkOut数据——2026年2月
# 基础设施总计:12,095 GB / $1,088.65

出口流量排名:
#1 cliente-loja.com.br       3,803 GB   $342.29   (占总量31%)
#2 clientportal.example.com       1,775 GB   $159.75
#3 ...
...
#66(最后一个实例)              0.1 GB    $0.01

第二名有1,775 GB。第一名有3,803 GB——是第二名的两倍多,占66个实例全部出口流量的31%。对于一个没有任何实例应该有这种流量的多租户平台来说,这个数字是最明确的信号,表明有问题。

确认:GoCache服务了0 GB

下一步检查了域名cliente-loja.com.br的GoCache分析数据:

复制
# GoCache分析API查询——2026年2月
# 域名:cliente-loja.com.br

GoCache提供的带宽:0.00 GB
CDN请求数:0
缓存命中率:N/A

零字节。该域名在GoCache上有配置——账号存在,规则都在——但整个月CDN没有提供过一个字节。所有流量都直接发往EC2,完全绕过了CDN。

两种可能的原因可以解释这种模式:要么DNS没有指向GoCache,要么实例的Security Group中除了CDN SG外还保留着World SG(0.0.0.0/0),使源站可以被直接访问。

根本原因:World SG与CDN SG并存

Security Group审计从分析每个实例的SG组合开始。平台上相关的SG:

复制
# 平台Security Groups
sg-0aaaa1111aaaa1111  "World"      → 对0.0.0.0/0开放80/443(不受限访问)
sg-0bbbb2222bbbb2222  "GoCache"    → 只允许GoCache IP访问80/443
sg-0cccc3333cccc3333  "Cloudflare" → 只允许Cloudflare IP访问80/443
sg-0dddd4444dddd4444  "Akamai"     → 只允许Akamai IP访问80/443

受CDN保护的实例的预期状态是:只有使用中的CDN SG,没有World SG。cliente-loja.com.br实例同时拥有两者:GoCache SG + World SG。有了World SG,互联网上的任何IP都可以直接访问EC2的443端口——由于DNS指向EC2(而非GoCache),所有流量都直接到达。

DNS验证确认了绕过:

复制
# 检查域名DNS
dig cliente-loja.com.br +short
# 返回:198.51.100.106  ← EC2公网IP,不是GoCache IP(198.51.100.x)

# GoCache IP以198.51.100.x开头
# 如果DNS解析为EC2 IP,则CDN被绕过

完整审计:22个带GoCache SG的实例

审计没有止步于一个实例。该流程被应用于所有22个配置了GoCache SG的实例,将SG与实际DNS解析进行交叉对比:

复制
# 列出所有带GoCache SG的实例
aws ec2 describe-instances   --filters "Name=instance.group-id,Values=sg-0bbbb2222bbbb2222"   --query 'Reservations[].Instances[].[InstanceId,PublicIpAddress,Tags[?Key==`Name`].Value|[0]]'   --output table

# 对每个实例,检查DNS是否指向GoCache或直接EC2 IP
for DOMAIN in $(域名列表); do
  DNS_IP=$(dig +short $DOMAIN | head -1)
  EC2_IP=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=*${DOMAIN}*"     --query 'Reservations[0].Instances[0].PublicIpAddress' --output text)

  if [ "$DNS_IP" = "$EC2_IP" ]; then
    echo "绕过:$DOMAIN → $DNS_IP(直接访问EC2)"
  elif echo "$DNS_IP" | grep -q "^198\.51\.100\."; then
    echo "正常:$DOMAIN → $DNS_IP(GoCache)"
  else
    echo "检查:$DOMAIN → $DNS_IP(需核实)"
  fi
done

22个带GoCache SG的实例审计结果:

19个实例: DNS指向GoCache(198.51.100.x)——配置正确。

3个实例: DNS指向Cloudflare(104.21.x.x,172.67.x.x),但SG配置为GoCache——错误的SG允许来自GoCache(实际上并不到达)的流量,而Cloudflare SG不存在。

除了CDN绕过,审计还发现了6个有冗余World SG的实例:

复制
# 有World SG + CDN SG的实例(冗余World SG)
# 验证DNS并确认CDN处于活跃状态后:

域名                       EC2 IP          DNS          操作
webserver-app01.example    203.0.113.99     GoCache      移除World SG
webserver-app02.example    203.0.113.117  GoCache      移除World SG
webserver-app03.example    203.0.113.184   GoCache      移除World SG
webserver-app04.example    203.0.113.70    GoCache      移除World SG
webserver-app05.example    203.0.113.52    GoCache      移除World SG
webserver-app06.example    203.0.113.236  Cloudflare   移除World SG + GoCache SG,添加Cloudflare SG

执行的修复措施

对于5个有冗余World SG的实例(DNS指向GoCache,World SG不必要),批量移除:

复制
# 从CDN活跃的实例中移除World SG
for INSTANCE in i-0aaaa1111aaaa1111 i-0bbbb2222bbbb2222 i-0cccc3333cccc3333                 i-0dddd4444dddd4444 i-0eeee5555eeee5555; do
  # 列出当前SG,排除World SG
  NEW_SGs=$(aws ec2 describe-instances --instance-ids $INSTANCE     --query 'Reservations[].Instances[].NetworkInterfaces[0].Groups[].GroupId'     --output text | tr '	' '
' | grep -v 'sg-0aaaa1111aaaa1111')

  # 应用新SG列表(不含World SG)
  aws ec2 modify-instance-attribute --instance-id $INSTANCE     --groups $(echo $NEW_SGs | tr '
' ' ')

  echo "$INSTANCE:World SG已移除"
done

对于有GoCache SG但DNS指向Cloudflare的实例,修复方案是将GoCache SG换成Cloudflare SG:

复制
# 将GoCache SG换成Cloudflare SG
INSTANCE="i-0ffff6666ffff6666"

# 移除World SG(sg-0aaaa1111aaaa1111)和GoCache SG(sg-0bbbb2222bbbb2222)
# 添加Cloudflare SG(sg-0cccc3333cccc3333)
aws ec2 modify-instance-attribute --instance-id $INSTANCE   --groups sg-0cccc3333cccc3333

额外发现:GoCache SG中的过时IP

在审计过程中,还验证了GoCache SG本身的IP列表的时效性。结果揭示了第二层风险:

复制
# 比较SG中的IP与官方列表
SG中的IP数:     31
官方IP数:       25
差异:            6个额外IP未在GoCache官网列出

# 发现的额外IP(GoCache支持确认为过时)
198.51.100.13/32   — 旧的单独IP
198.51.100.152/29  — 旧的/29
198.51.100.72/29   — 旧的/29
198.51.100.64/26   — 旧的/26
198.51.100.192/29  — 旧的/29
198.51.100.24/29 — 旧的/29

CDN SG中的过时IP是一个隐患:如果其中一个IP块被重新分配给第三方,该范围内的任何IP都可以像CDN一样直接访问源站。GoCache支持确认这6个IP块确实已过时可以移除。对应的11条规则(6个IP × 80和443端口)被撤销:

复制
# 撤销GoCache SG中的过时规则
aws ec2 revoke-security-group-ingress   --group-id sg-0bbbb2222bbbb2222   --security-group-rule-ids sgr-xxx1 sgr-xxx2 sgr-xxx3 sgr-xxx4 sgr-xxx5                              sgr-xxx6 sgr-xxx7 sgr-xxx8 sgr-xxx9 sgr-xxx10 sgr-xxx11

# 验证最终数量
aws ec2 describe-security-groups --group-ids sg-0bbbb2222bbbb2222   --query 'SecurityGroups[0].IpPermissions | length(@)'
# 25 — 与GoCache官方列表一致

方法论:CloudWatch vs CDN分析数据

这里有效的检测模式适用于任何使用CDN的平台——Cloudflare、CloudFront、Fastly、GoCache:

1. 测量实际EC2出口流量: 按实例统计CloudWatch NetworkOut。Cost Explorer按账户汇总,不按实例——直接使用API获取精细数据。

2. 与CDN分析数据交叉对比: 对每个出口流量高的域名,检查同期CDN提供的带宽。CDN带宽0 GB + EC2高出口 = 绕过。

3. 通过DNS确认: 如果DNS解析为EC2 IP(而非CDN IP),则绕过已在DNS层面得到确认。

4. 检查Security Groups: 即使DNS指向CDN,开放的World SG也允许直接访问。机器人和扫描器总是测试直接IP——如果SG允许,它们就在不经过CDN的情况下提供流量。

复制
# 绕过检测脚本——CloudWatch vs CDN分析数据
# 适用于GoCache、Cloudflare、CloudFront

for DOMAIN in $(cdn域名列表); do
  EC2_EGRESS=$(get_cloudwatch_network_out $DOMAIN $MONTH)   # GB
  CDN_BANDWIDTH=$(get_cdn_bandwidth $DOMAIN $MONTH)         # GB

  RATIO=$(echo "$CDN_BANDWIDTH / $EC2_EGRESS" | bc -l)

  if (( $(echo "$RATIO < 0.1" | bc -l) )); then
    echo "绕过警告:$DOMAIN — CDN仅提供了$(echo "$RATIO * 100" | bc -l)%的流量"
    echo "  EC2出口:    ${EC2_EGRESS} GB"
    echo "  CDN带宽:${CDN_BANDWIDTH} GB"
  fi
done

财务影响与问题规模

对于cliente-loja.com.br,识别出的直接成本是每月342.29美元的不必要出口流量。在拥有数十个客户的平台上,乘数效应是显著的。即使不单独计算每个案例,审计中发现的3个SG配置错误的域名也代表类似的潜在流失。

没有正确DNS的CDN是双重成本:你为不提供服务的CDN付费,也为提供所有服务的EC2出口流量付费。Security Group审计是第二步——但第一步是将CloudWatch NetworkOut与CDN分析数据进行交叉对比。

多租户平台的经验教训

在相同安全模型下托管多个客户的平台需要定期审计DNS、CDN和Security Groups之间的对齐情况。这三个层面的决策是独立做出的——客户更换CDN,DNS被更新,但SG被遗忘。或者World SG为诊断目的临时添加后从未被移除。

建议频率: 每月进行一次DNS × SG × CDN分析对齐审计。识别一个绕过的成本是一小时工作。不识别的成本体现在下个月的账单上。