Buzeli
buzeliSoluções Digitais
Custos

R$1.800/mês em egress desnecessário: como identificar clientes bypassando o CDN com auditoria de Security Groups

Publicado em 3 de maio de 2026

O contexto: plataforma multi-tenant com CDN obrigatório

A plataforma hospeda dezenas de sites WordPress em instâncias EC2 compartilhadas e dedicadas. O modelo de segurança e custo depende de todos os sites receberem tráfego pelo CDN — GoCache, Cloudflare ou Akamai, dependendo do cliente. O Security Group de cada instância libera as portas 80 e 443 apenas para os ranges de IP do CDN em uso.

Quando o DNS aponta corretamente para o CDN e o Security Group está alinhado, o egress EC2 é mínimo — o conteúdo é servido pelo CDN a partir do cache, sem sair da origin a cada requisição. O custo de data transfer out da AWS só aparece quando a origin é acessada, não no cache hit.

CDN corretamente configurado = egress quase zero no EC2. CDN configurado mas DNS bypassando = egress pleno no EC2, custo de cache no CDN, dois custos para o mesmo conteúdo.

O sinal: $342 de um único domínio em fevereiro

A análise mensal de data transfer via CloudWatch NetworkOut (mais precisa que o Cost Explorer para isolar instâncias) revelou uma anomalia no ranking:

Copiar
# Dados CloudWatch NetworkOut — Fevereiro 2026
# Total infra: 12.095 GB / $1.088,65

Ranking de egress:
#1 cliente-loja.com.br       3.803 GB   $342,29   (31% do total)
#2 clientportal.example.com       1.775 GB   $159,75
#3 ...
...
#66 (última instância)         0,1 GB     $0,01

O segundo colocado tinha 1.775 GB. O primeiro tinha 3.803 GB — mais que o dobro do segundo, e 31% de todo o egress de 66 instâncias. Para uma plataforma multi-tenant onde nenhuma instância deveria ter esse volume, o número era o sinal mais claro de que algo estava errado.

A confirmação: GoCache com 0 GB servido

A próxima verificação foi no painel de analytics do GoCache para o domínio cliente-loja.com.br:

Copiar
# Consulta via API GoCache Analytics — Fevereiro 2026
# Domínio: cliente-loja.com.br

Bandwidth servido pelo GoCache: 0,00 GB
Requisições via CDN: 0
Cache hit ratio: N/A

Zero gigabytes. O domínio estava configurado no GoCache — a conta existia, as regras estavam lá — mas o CDN não havia servido nenhum byte no mês inteiro. Todo o tráfego estava indo diretamente para o EC2, ignorando completamente o CDN.

Duas causas possíveis explicariam esse padrão: ou o DNS não apontava para o GoCache, ou o Security Group da instância ainda tinha o SG World (0.0.0.0/0) aberto junto com o SG do CDN, mantendo a origin acessível diretamente.

A causa raiz: SG World coexistindo com SG CDN

A auditoria de Security Groups começa com uma análise da combinação de SGs em cada instância. Os SGs relevantes na plataforma:

Copiar
# Security Groups da plataforma
sg-0aaaa1111aaaa1111  "World"      → abre 80/443 para 0.0.0.0/0 (acesso irrestrito)
sg-0bbbb2222bbbb2222  "GoCache"    → libera 80/443 apenas para IPs GoCache
sg-0cccc3333cccc3333  "Cloudflare" → libera 80/443 apenas para IPs Cloudflare
sg-0dddd4444dddd4444  "Akamai"     → libera 80/443 apenas para IPs Akamai

O estado esperado para uma instância protegida por CDN é: apenas o SG do CDN em uso, sem o SG World. A instância de cliente-loja.com.br tinha ambos: SG GoCache + SG World. Com o SG World presente, qualquer IP na internet conseguia acessar a porta 443 do EC2 diretamente — e como o DNS apontava para o EC2 (não para o GoCache), todo o tráfego chegava diretamente.

A verificação de DNS confirmou o bypass:

Copiar
# Verificar DNS do domínio
dig cliente-loja.com.br +short
# Retorna: 198.51.100.106  ← IP público do EC2, não IP GoCache (198.51.100.x)

# IPs GoCache começam com 198.51.100.x
# Se o DNS resolver para o IP do EC2, o CDN está sendo bypassado

A auditoria completa: 22 instâncias com SG GoCache

A auditoria não parou em uma instância. O processo foi aplicado a todas as 22 instâncias que tinham o SG GoCache configurado, cruzando o SG com a resolução DNS real:

Copiar
# Listar todas instâncias com SG GoCache
aws ec2 describe-instances   --filters "Name=instance.group-id,Values=sg-0bbbb2222bbbb2222"   --query 'Reservations[].Instances[].[InstanceId,PublicIpAddress,Tags[?Key==`Name`].Value|[0]]'   --output table

# Para cada instância, verificar se DNS aponta para GoCache ou para IP direto
for DOMAIN in $(lista-dominios); 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 "BYPASS: $DOMAIN → $DNS_IP (direto para EC2)"
  elif echo "$DNS_IP" | grep -q "^198\.51\.100\."; then
    echo "OK: $DOMAIN → $DNS_IP (GoCache)"
  else
    echo "CHECK: $DOMAIN → $DNS_IP (verificar)"
  fi
done

Resultado da auditoria das 22 instâncias com SG GoCache:

19 instâncias: DNS apontando para GoCache (198.51.100.x) — configuração correta.

3 instâncias: DNS apontando para Cloudflare (104.21.x.x, 172.67.x.x), mas SG configurado para GoCache — o SG errado estava permitindo tráfego GoCache que não chegava, enquanto o SG Cloudflare não estava presente.

Além do bypass do CDN, a auditoria revelou também 6 instâncias com SG World redundante:

Copiar
# Instâncias com SG World + SG CDN (SG World redundante)
# Após verificar DNS e confirmar que CDN está ativo:

Domínio                    EC2 IP          DNS          Ação
webserver-app01.example    203.0.113.99     GoCache      remover SG World
webserver-app02.example    203.0.113.117  GoCache      remover SG World
webserver-app03.example    203.0.113.184   GoCache      remover SG World
webserver-app04.example    203.0.113.70    GoCache      remover SG World
webserver-app05.example    203.0.113.52    GoCache      remover SG World
webserver-app06.example    203.0.113.236  Cloudflare   remover SG World + SG GoCache, adicionar SG Cloudflare

As correções executadas

Para as 5 instâncias com SG World redundante (DNS apontando para GoCache, SG World desnecessário), a remoção foi em lote:

Copiar
# Remover SG World de instâncias onde CDN está ativo
for INSTANCE in i-0aaaa1111aaaa1111 i-0bbbb2222bbbb2222 i-0cccc3333cccc3333                 i-0dddd4444dddd4444 i-0eeee5555eeee5555; do
  # Listar SGs atuais, excluindo o SG World
  NEW_SGs=$(aws ec2 describe-instances --instance-ids $INSTANCE     --query 'Reservations[].Instances[].NetworkInterfaces[0].Groups[].GroupId'     --output text | tr '	' '
' | grep -v 'sg-0aaaa1111aaaa1111')

  # Aplicar nova lista de SGs (sem o SG World)
  aws ec2 modify-instance-attribute --instance-id $INSTANCE     --groups $(echo $NEW_SGs | tr '
' ' ')

  echo "$INSTANCE: SG World removido"
done

Para a instância com SG GoCache mas DNS Cloudflare, a correção foi trocar o SG GoCache pelo SG Cloudflare:

Copiar
# Trocar SG GoCache por SG Cloudflare
INSTANCE="i-0ffff6666ffff6666"

# Remover SG World (sg-0aaaa1111aaaa1111) e SG GoCache (sg-0bbbb2222bbbb2222)
# Adicionar SG Cloudflare (sg-0cccc3333cccc3333)
aws ec2 modify-instance-attribute --instance-id $INSTANCE   --groups sg-0cccc3333cccc3333

Descoberta adicional: IPs obsoletos no SG GoCache

Durante a auditoria, foi verificada também a atualidade da lista de IPs dentro do próprio SG GoCache. O resultado revelou uma segunda camada de risco:

Copiar
# Contar IPs no SG GoCache vs lista oficial
IPs no SG:       31
IPs oficiais:    25
Diferença:        6 IPs extras não listados no site oficial GoCache

# IPs extras encontrados (confirmados como obsoletos pelo suporte GoCache)
198.51.100.13/32   — IP avulso antigo
198.51.100.152/29  — /29 antigo
198.51.100.72/29   — /29 antigo
198.51.100.64/26   — /26 antigo
198.51.100.192/29  — /29 antigo
198.51.100.24/29 — /29 antigo

IPs obsoletos no SG do CDN são um risco silencioso: se um desses blocos for reatribuído a um terceiro, qualquer IP nesse range consegue acessar a origin diretamente como se fosse o CDN. O suporte do GoCache confirmou que os 6 blocos eram de fato obsoletos e podiam ser removidos. As 11 regras correspondentes (6 IPs × portas 80 e 443) foram revogadas:

Copiar
# Revogar regras obsoletas do SG GoCache
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

# Verificar contagem final
aws ec2 describe-security-groups --group-ids sg-0bbbb2222bbbb2222   --query 'SecurityGroups[0].IpPermissions | length(@)'
# 25 — bate com lista oficial GoCache

A metodologia: CloudWatch vs analytics CDN

O padrão de detecção que funcionou aqui é aplicável a qualquer plataforma com CDN — Cloudflare, CloudFront, Fastly, GoCache:

1. Medir o egress real no EC2: CloudWatch NetworkOut por instância. O Cost Explorer consolida por conta, não por instância — use a API diretamente para dados granulares.

2. Cruzar com analytics do CDN: Para cada domínio com egress alto, verificar o bandwidth servido pelo CDN no mesmo período. 0 GB no CDN + alto egress no EC2 = bypass.

3. Confirmar via DNS: Se o DNS resolve para o IP do EC2 (não para o IP do CDN), o bypass está confirmado no nível de DNS.

4. Verificar Security Groups: Mesmo com DNS no CDN, um SG World aberto permite acesso direto. Bots e scanners sempre testam o IP direto — e se o SG deixa passar, eles servem tráfego sem passar pelo CDN.

Copiar
# Script de detecção de bypass — CloudWatch vs CDN analytics
# Aplica-se a GoCache, Cloudflare, CloudFront

for DOMAIN in $(lista-dominios-com-cdn); do
  EC2_EGRESS=$(get_cloudwatch_network_out $DOMAIN $MES)     # GB
  CDN_BANDWIDTH=$(get_cdn_bandwidth $DOMAIN $MES)           # GB

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

  if (( $(echo "$RATIO < 0.1" | bc -l) )); then
    echo "ALERTA BYPASS: $DOMAIN — CDN serviu apenas $(echo "$RATIO * 100" | bc -l)% do tráfego"
    echo "  EC2 egress:    ${EC2_EGRESS} GB"
    echo "  CDN bandwidth: ${CDN_BANDWIDTH} GB"
  fi
done

Impacto financeiro e a escala do problema

Para cliente-loja.com.br, o custo direto identificado foi $342,29/mês em egress desnecessário. Em reais (câmbio ~R$5,25), isso representa aproximadamente R$1.800/mês saindo da conta AWS para um único domínio que deveria estar com tráfego servido pelo CDN.

Em uma plataforma com dezenas de clientes, o efeito multiplicador é relevante. Mesmo sem calcular cada caso individualmente, os 3 domínios com SG incorreto identificados na auditoria representavam potencial de vazamento similar.

CDN sem DNS correto é um custo duplo: você paga pelo CDN que não serve, e paga pelo egress EC2 que serve tudo. A auditoria de Security Groups é o segundo passo — mas o primeiro é cruzar CloudWatch NetworkOut com as analytics do CDN.

Três instâncias pendentes após a auditoria

A auditoria identificou 3 instâncias com DNS Cloudflare mas SG GoCache — a origin estava acessível pelos IPs GoCache (que não chegavam), mas não pelos IPs Cloudflare (que chegavam). O comportamento resultante depende de como o GoCache e Cloudflare se sobrepõem nos IPs, mas o risco é duplo: tráfego Cloudflare pode estar chegando diretamente se houver overlap de CIDRs, ou o site pode estar sem proteção adequada de SG.

A correção para esses 3 casos é trocar o SG GoCache pelo SG Cloudflare — após confirmar que o DNS realmente está apontando para Cloudflare e não para ambos os CDNs em DNS round-robin.

A lição para plataformas multi-tenant

Plataformas que hospedam múltiplos clientes sob o mesmo modelo de segurança precisam de auditoria periódica do alinhamento entre DNS, CDN e Security Groups. As três camadas derivam de decisões independentes — um cliente troca de CDN, o DNS é atualizado, mas o SG fica para trás. Ou um SG World é adicionado temporariamente para diagnóstico e nunca é removido.

Frequência recomendada: auditoria mensal de alinhamento DNS × SG × analytics CDN. O custo de identificar um bypass é uma hora de trabalho. O custo de não identificar é visível na fatura do mês seguinte.