Dissecando um erro 521 da Cloudflare no CloudLinux: de handshakes TCP a segfaults no Apache#
Quando colocamos sites atrás de uma rede como a Cloudflare, erros de conexão costumam parecer falhas simples de roteamento ou regras bloqueadas no firewall. Mas em alguns cenários o problema está escondido em camadas bem mais profundas do sistema operacional.
Recentemente passei por um caso em um servidor com cPanel e CloudLinux onde o firewall primário (CSF) estava desligado. A meta era permitir o tráfego da Cloudflare sem gargalos, repassando o IP real do visitante para o Apache. Em vez disso, o domínio começou a devolver 521 Web Server is Down, falhas na resolução de VirtualHosts e quedas de processos (segmentation faults) registradas pelo kernel.
Abaixo documento a investigação completa, desde chamadas de sistema e tabelas de rede até o módulo C do Apache responsável pelo colapso.
O desafio do tráfego híbrido sem CSF e o controle via iptables raw#
O objetivo inicial era permitir que o tráfego roteado pela Cloudflare alcançasse o Apache e que o IP real do visitante fosse repassado corretamente, mantendo a convivência com conexões diretas. Como o CSF estava inativo, precisei trabalhar diretamente nas tabelas raw e filter do iptables, além de configurar o mod_remoteip no EasyApache 4.
Controle de rede na camada 3 com iptables raw#
Sem o daemon LFD gerenciando conjuntos de IP via ipset, apliquei regras no topo da chain INPUT e na tabela raw para otimizar o tráfego e eliminar o overhead do rastreamento de conexões (conntrack) para os nós da Cloudflare:
# Permissao estrita na chain INPUT (Filter)
for ip in $(curl -s https://www.cloudflare.com/ips-v4); do
iptables -I INPUT -p tcp -m multiport --dports 80,443 -s $ip -j ACCEPT
done
# Bypass do conntrack na tabela raw para aliviar sobrecarga
for ip in $(curl -s https://www.cloudflare.com/ips-v4); do
iptables -t raw -I PREROUTING -s $ip -p tcp --dport 80 -j NOTRACK
iptables -t raw -I PREROUTING -s $ip -p tcp --dport 443 -j NOTRACK
done
A tabela raw é avaliada pelo kernel antes de mangle e nat. Ao utilizar a target NOTRACK, instruí o módulo nf_conntrack do Linux a ignorar esses pacotes. Isso impede o esgotamento da tabela de estados em /proc/sys/net/netfilter/nf_conntrack_max sob volume intenso de requisições vindas dos proxies da Cloudflare.
Camada de aplicação e a arquitetura do mod_remoteip#
No EasyApache 4, o processamento de cabeçalhos de proxy ocorre na fase post-read-request do ciclo de vida do Apache. O módulo substitui o ponteiro de endereço do cliente na struct interna pelo valor entregue no cabeçalho CF-Connecting-IP.
Ajustei a configuração global em /etc/apache2/conf.d/includes/pre_main_global.conf:
<IfModule mod_remoteip.c>
RemoteIPHeader CF-Connecting-IP
# Faixas oficiais da Cloudflare
RemoteIPInternalProxy 103.21.244.0/22 108.162.192.0/18 190.93.240.0/20 188.114.96.0/20 197.234.240.0/22 198.41.128.0/17 162.158.0.0/15 104.16.0.0/13 104.24.0.0/14 172.64.0.0/13 131.0.72.0/22
RemoteIPInternalProxy 127.0.0.1
</IfModule>
A escolha de RemoteIPInternalProxy em vez de RemoteIPTrustedProxy é intencional. O parâmetro InternalProxy remove o IP da CDN da cadeia de proxy e o trata como rede interna confiável. No CloudLinux, esse detalhe é indispensável para que o módulo do kernel kmodlve contabilize os limites de Entry Processes (EP) por IP real do visitante, evitando que todas as requisições somem contra a cota da conta como se fossem originadas por um único endereço da Cloudflare.
O sintoma do erro 521 e o isolamento dos sockets#
Mesmo com as portas abertas no firewall, acessos ao domínio através da Cloudflare resultavam em 521 Web Server is Down. Ao mesmo tempo, requisições diretas via arquivo /etc/hosts local funcionavam sem falhas.
Comecei isolando cada etapa da conexão.
Teste de socket e cabeçalhos com curl local#
Simulei a requisição da CDN localmente para descartar interferências externas de roteamento:
curl -v -H "CF-Connecting-IP: 1.1.1.1" --resolve domain.com:80:127.0.0.1 http://domain.com
Retorno obtido:
* Connected to domain.com (127.0.0.1) port 80 (#0)
> GET / HTTP/1.1
> CF-Connecting-IP: 1.1.1.1
< HTTP/1.1 200 OK
< Content-Type: text/html
<html><head><META HTTP-EQUIV="refresh" CONTENT="0;URL=/cgi-sys/defaultwebpage.cgi"></head><body></body></html>
O socket TCP abriu normalmente, mas o Apache entregou a tela padrão do cPanel (defaultwebpage.cgi). Esse comportamento acontece quando o Apache não casa o cabeçalho Host: domain.com com o bloco VirtualHost correto, normalmente por divergência entre o IP público onde o vhost escuta e o endereço de loopback testado.
Verificação da tabela de conexões com netstat#
Em seguida, monitorei o estado dos sockets gerados pelas redes da Cloudflare:
netstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -n | grep -E "(103.21.244|108.162.192|190.93.240)"
A saída foi completamente vazia. O tráfego externo da Cloudflare não mantinha conexões no estado ESTABLISHED. Algo estava derrubando a sessão logo após o pacote SYN inicial.
Descartei três hipóteses de rede:
- Apache sem escuta na porta 80: validei com
ss -tulpn | grep :80. O processo escutava em0.0.0.0:80. - Regras residuais de descarte: conferi
iptables -L INPUT -ne confirmei ausência deDROPouREJECT. - Incompatibilidade de MTU: testei com
ping -M do -s 1472 103.21.244.1, sem retorno de Packet Too Big por ICMP.
A causa raiz: analisando segfaults no buffer do kernel#
A ausência de conexões ativas no netstat somada ao erro 521 indicava que os workers do Apache estavam sofrendo encerramento abrupto no momento do accept().
Consultei as mensagens do kernel via dmesg:
dmesg | tail -n 50
O registro exibiu falhas recorrentes:
[1821374.907238] httpd[3338551]: segfault at 199c ip 0000149c9f07fa71 sp 0000149c9c781720 error 4 in libbrotlienc.so.1.0.9[149c9f001000+a3000]
[1821374.907252] httpd[3338556]: segfault at 199c ip 0000149c9f07fa71 sp 0000149c9bd7c720 error 4 in libbrotlienc.so.1.0.9[149c9f001000+a3000]
Dissecando a mensagem de erro da falha de página#
A linha do dmesg descreve o colapso:
httpd[3338551]: o processo worker do Apache que tentou atender a conexão.segfault at 199c: tentativa de leitura em endereço de memória não mapeado (0x199c). Esse valor baixo indica que um ponteiro base em C continhaNULLe o código tentou ler o membro localizado no deslocamento199c.ip 0000149c9f07fa71: ponteiro de instrução (Instruction Pointer no x86_64) onde a falha ocorreu no binário.error 4: no código de Page Fault da arquitetura x86, o bit 2 ativo (valor 4) indica que o erro ocorreu no espaço de usuário (user space) durante uma tentativa de leitura.in libbrotlienc.so.1.0.9: a biblioteca de compressão Brotli.
A mecânica do erro 521 ficou evidente. Quando a Cloudflare se conecta à origem, ela inclui o cabeçalho Accept-Encoding: br. O Apache enviava os dados da resposta para o filtro de saída do mod_brotli. Por conta de uma falha de ponteiro na biblioteca libbrotlienc.so, o worker recebia um sinal SIGSEGV do kernel e morria antes de concluir a troca de dados HTTP.
Para o nó da Cloudflare, a interrupção prematura do socket gerava um TCP RST, resultando na tela de erro 521.
Removi o pacote instável e reiniciei o servidor web:
yum remove ea-apache24-mod_brotli -y
systemctl restart httpd
Em casos onde é necessário manter o binário para gerar relatórios detalhados, uma alternativa é anexar o strace ao processo principal com strace -f -p $(cat /var/run/apache2/httpd.pid) -e trace=memory,network ou liberar core dumps com ulimit -c unlimited para depuração via gdb.
Complicações de SNI e validação do AutoSSL#
Após sanar os segfaults, verifiquei o handshake TLS com o OpenSSL:
openssl s_client -connect 127.0.0.1:443 -servername domain.com
A saída apontou outra inconsistência:
0 s:CN=server.host.net
i:C=US, O=Let's Encrypt, CN=R13
O servidor apresentava o certificado do hostname da máquina em vez do certificado do domínio do usuário.
Na Cloudflare, quando o modo SSL/TLS está definido como Full (Strict), o nome apresentado no SNI precisa corresponder exatamente ao certificado instalado no backend. Se o Apache exibe o certificado genérico da máquina como fallback, a borda recusa a conexão.
Executei a verificação do AutoSSL para regenerar o certificado do domínio:
/usr/local/cpanel/bin/autossl_check --user usuario_cpanel
Com o certificado instalado no VirtualHost correto, a validação de SNI foi restabelecida.
Regras residuais de WAF e listas de IP no Imunify360#
Mesmo com o CSF desligado e sem bloqueios no /etc/hosts.allow, ambientes CloudLinux podem executar rotinas de filtragem via Imunify360 por meio de conjuntos do ipset.
Para garantir que nenhum nó da Cloudflare ficasse retido em quarentena ou desafios, consultei as listas cinzas do agente:
imunify360-agent graylist ip list | grep -E "103.21.244|108.162.192|172.64"
Em seguida, adicionei as redes oficiais na lista de permissões permanente:
for range in $(curl -s https://www.cloudflare.com/ips-v4); do
imunify360-agent whitelist ip add "$range" --comment "Cloudflare edge"
done
Reconstruindo a telemetria do LVE e processamento de logs#
Com o Apache estável, o mod_remoteip ativo e os certificados alinhados, as requisições passaram a fluir normalmente. Para consolidar as métricas no cPanel sem aguardar o cronjob noturno, disparei as rotinas de atualização manuais.
Sincronização de métricas do LVE#
O serviço lvestats lê as informações do módulo do kernel em /proc/lve/list. Reiniciei o daemon para forçar a gravação dos dados no banco SQLite:
systemctl restart lvestats
# Inspecao de processos e memoria por usuario
lveinfo --user usuario_cpanel --show-columns "EP,LCPU,MEM"
Processamento imediato dos domlogs com AWStats#
Para que as estatísticas de acesso mostrassem os IPs reais traduzidos pelo mod_remoteip:
/scripts/runweblogs usuario_cpanel
tail -f /usr/local/cpanel/logs/cpanellogd_log
Da borda aos registros do kernel: aprendizados práticos#
Um erro 521 na Cloudflare nem sempre significa que o Apache caiu ou que um IP foi colocado em lista de bloqueio. Neste incidente, a conexão TCP abria perfeitamente, mas o worker do servidor web colapsava milissegundos depois ao tentar comprimir o corpo da resposta com uma biblioteca corrompida.
Três práticas fizeram a diferença durante o diagnóstico:
- Olhar o
dmesgdo sistema operacional sempre que conexões TCP sumirem da tabela donetstatsem gerar logs nos arquivos de acesso do Apache. - Usar
RemoteIPInternalProxyno Apache para preservar a separação de recursos no CloudLinux, impedindo que o tráfego da CDN sobrecarregue cotas de processos concorrentes. - Checar o SNI retornado pelo backend com
openssl s_clientpara assegurar que configurações estritas de SSL na CDN não falhem por entrega de certificado de fallback.
Ter visibilidade sobre o caminho da requisição, desde as regras de rede na tabela raw até os módulos compilados do servidor web, evita conclusões precipitadas e resolve o problema na causa raiz.
Este artigo foi útil?
Deixe uma reação rápida para apoiar o conteúdo:
Este post está licenciado sob CC BY-NC.



Comentários
Participe da discussão abaixo.
0 comentários