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:
# 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,01O 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:
# 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/AZero 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:
# 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 AkamaiO 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:
# 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 bypassadoA 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:
# 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
doneResultado 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:
# 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 CloudflareAs 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:
# 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"
donePara a instância com SG GoCache mas DNS Cloudflare, a correção foi trocar o SG GoCache pelo SG Cloudflare:
# 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-0cccc3333cccc3333Descoberta 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:
# 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 antigoIPs 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:
# 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 GoCacheA 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.
# 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
doneImpacto 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.
