Quem lida com servidores de e-mail e configurações personalizadas no Exim sabe que a flexibilidade do MTA tem um preço: quando combinamos regras de roteamento para serviços de relay externo (smarthost), reescrita de remetente (SRS) e plugins de segurança como o Imunify360, qualquer referência ausente em tempo de execução pode travar a entrega de mensagens legítimas.
Recentemente, ao analisar falhas de entrega em um servidor com cPanel, chamou a atenção um comportamento silencioso: e-mails externos legítimos enviados por grandes provedores (como o Google) eram temporariamente recusados com erro 451 durante a checagem de remetente (sender verify).
Neste artigo, vamos dissecar o caminho percorrido pelo e-mail, entender como o Exim avalia ponteiros de transporte na inicialização, por que os testes rotineiros no terminal mascararam a falha e como aplicar uma correção persistente no cPanel.
1. O sintoma nos logs e o impacto no protocolo SMTP#
A identificação do problema começou no arquivo principal de logs do Exim (/var/log/exim_mainlog). Ao filtrar conexões vindas dos servidores MX do Google, duas linhas revelaram a causa imediata do atraso:
2026-04-17 17:11:52 H=mail-oo1-f52.google.com [209.85.161.52]:60854 sender verify defer for <[email protected]>: transport "dkim_remote_forwarded_smtp" not found in smarthost_forwarded router
2026-04-17 17:11:52 H=mail-oo1-f52.google.com [209.85.161.52]:60854 X=TLS1.3:TLS_AES_128_GCM_SHA256:128 F=<[email protected]> temporarily rejected RCPT <[email protected]>: Could not complete sender verify
Como o sender verify causa a rejeição 451#
Durante a negociação SMTP, logo após o cliente enviar o comando RCPT TO:<[email protected]>, o Exim executa as regras da sua lista de controle de acesso (acl_check_rcpt). Se a diretiva verify = sender estiver ativa (prática comum para combater remetentes forjados), o Exim pausa temporariamente o diálogo com o servidor remoto.
Nesse intervalo, o MTA roda uma verificação interna de roteamento para saber se o endereço de origem ([email protected]) é roteável e aceitaria retornos.
O erro 451 (rejeição temporária) surge porque o processo de verificação interna abortou por um erro de configuração no próprio servidor local. Diante de uma resposta temporária, o servidor do Google segura o e-mail em sua própria fila e agenda novas tentativas com intervalos graduais de espera (backoff timer), provocando atrasos perceptíveis para quem aguarda a mensagem.
2. Sob o capô: como o Exim gerencia transportes e ponteiros em c#
Para entender a raiz da falha, vale observar como o binário do Exim processa suas diretivas a partir do arquivo /etc/exim.conf.
Leitura de configurações e a tabela de símbolos (readconf.c)#
Quando o serviço do Exim sobe ou recebe um sinal SIGHUP para recarregar as tabelas, a função readconf_main() lê o arquivo de configuração de forma sequencial.
- Roteadores e transportes são instanciados em estruturas internas (
router_instanceetransport_instance). - O Exim indexa os nomes dos transportes existentes em uma tabela de dispersão (hash table).
- O ponto que causa confusão: o Exim não valida, durante a inicialização, se nomes de transporte inseridos dentro de blocos condicionais dinâmicos (como expressões
${if ...}) realmente existem. Ele apenas armazena a string para avaliação tardia (lazy evaluation).
Avaliação tardia e a resolução de transportes em runtime (expand.c e transport.c)#
Ao realizar o sender verify, a função route_address() percorre a lista encadeada de roteadores até encontrar uma regra correspondente. Na configuração do servidor em questão, a mensagem casava com o roteador customizado smarthost_forwarded:
smarthost_forwarded:
driver = manualroute
domains = !+local_domains
condition = ${if and {{def:original_domain}{!def:authenticated_id}}{yes}{no}}
no_more
transport = dkim_remote_forwarded_smtp
route_list = * smtp-out.domain.com::587
O bloco também interagia com a macro do SRS presente no roteador de autenticação (smarthost_auth):
.ifdef SRSENABLED
transport = ${if eq {$local_part@$domain} \
{$original_local_part@$original_domain} \
{dkim_remote_smtp} {dkim_remote_forwarded_smtp}}
.endif
Quando a função expand_string() resolve essa condição, ela retorna o texto "dkim_remote_forwarded_smtp". Em seguida, o controle passa para o subsistema de transporte (transport.c), onde a função transport_find(uschar *name) busca esse nome na tabela de símbolos carregada na memória.
Como o transporte dkim_remote_forwarded_smtp não estava declarado na seção de transportes do arquivo de configuração, a função retornou um ponteiro nulo (NULL). Sem saber como despachar a mensagem no roteador, o Exim interrompe a verificação e registra no log: transport "dkim_remote_forwarded_smtp" not found.
3. Investigando o comportamento com ferramentas de linha de comando#
Para confirmar o estado dos transportes sem interromper o fluxo do servidor, utilizamos utilitários nativos do próprio Exim diretamente no terminal.
Verificando a presença do transporte na memória do Exim#
A flag -bP (Print Configuration) permite consultar o estado compilado do daemon:
exim -bP transports | grep "_smtp"
O comando retornou os transportes ativos:
remote_smtp_smart_regular transport:
remote_smtp transport:
dkim_remote_smtp transport:
Ao consultar especificamente o transporte apontado pelo roteador:
exim -bP transport dkim_remote_forwarded_smtp
A saída confirmou o diagnóstico:
transport dkim_remote_forwarded_smtp not found
O símbolo simplesmente não existia no mapa de transportes do processo.
Rastreando rotas com o modo de depuração ativado#
Para visualizar a ordem em que o Exim avaliava os roteadores, executamos o comando de teste de endereço com rastreamento detalhado de rotas e transportes:
exim -d+route+transport -bt [email protected]
No relatório de saída gerado pelo processo, foi possível acompanhar as consultas a arquivos locais:
search_open: lsearch "/etc/localdomains"
internal_search_find: file="/etc/localdomains" type=lsearch key="domain.com"
lookup failed
Mais adiante, o log mostrou o roteador de filtro de spam do Imunify360 assumindo o controle:
--------> imunifyemail_spamfilter_router router <--------
set transport 'imunifyemail_spamfilter_transport'
queued for imunifyemail_spamfilter_transport transport: local_part = sender
routed by imunifyemail_spamfilter_router router
Por que o teste comum com -bt escondeu o problema?#
Esse comportamento explica por que os testes manuais não apontavam falha inicialmente: quando você testa um endereço localmente com exim -bt, o roteador do Imunify360 intercepta o fluxo logo no início para enfileirar a mensagem em sua esteira de análise, antes que o Exim alcance os roteadores de smarthost posicionados mais abaixo.
No entanto, quando uma conexão externa do Google dispara o sender verify, o Exim avalia a cadeia completa de roteamento para validar a rota de retorno. É nesse momento que o roteador de smarthost entra em ação, tenta resolver o transporte inexistente e causa o travamento.
4. Descartando hipóteses secundárias e casos de borda#
Antes de editar arquivos de configuração, convém checar se não há interferências do sistema operacional que possam simular um comportamento semelhante.
Esgotamento de descritores de arquivos e leitura de chaves DKIM#
Se o processo do Exim atingisse o limite de descritores de arquivos abertos (file descriptors), a tentativa de ler chaves privadas em /var/cpanel/domain_keys/private/ poderia falhar silenciosamente, impedindo o transporte de se registrar.
Para descartar essa possibilidade, rastreamos as chamadas de sistema relacionadas a arquivos durante a execução:
strace -f -s 128 -e trace=open,openat,stat exim -bt [email protected] 2>&1 | grep domain_keys
A ausência de erros como EMFILE (Too many open files) ou ENOENT (No such file or directory) confirmou que o sistema de arquivos estava operando normalmente.
Compatibilidade de bibliotecas e handshake TLS com o smarthost#
Outra hipótese a verificar era uma eventual falha no aperto de mão TLS com a porta 587 do smarthost, provocada por incompatibilidade de cifras após atualizações do OpenSSL.
Testamos a comunicação direta com o relay configurado:
openssl s_client -connect smtp-out.domain.com:587 -starttls smtp
O handshake foi concluído com sucesso e a sessão segura foi estabelecida, confirmando que a camada criptográfica de rede estava íntegra.
5. Corrigindo o transporte de forma persistente no cPanel#
No cPanel, o arquivo /etc/exim.conf é gerado dinamicamente a partir de modelos. Alterações manuais feitas diretamente no arquivo são sobrescritas sempre que o sistema executa atualizações (upcp). Por isso, a definição do transporte deve ser incluída no editor avançado do WHM.
Adicionando o transporte no advanced editor do WHM#
- No menu do WHM, acesse Service Configuration -> Exim Configuration Manager.
- Clique na aba Advanced Editor.
- Localize o bloco demarcado como Section: TRANSPORTSTART.
- Insira a definição completa do transporte, garantindo as regras de interface, identificação de HELO e assinatura DKIM:
dkim_remote_forwarded_smtp:
driver = smtp
hosts_require_tls = *
interface = <; ${if > {${extract {size} {${stat:/etc/mailips}} }} {0} {${lookup {${lc:${perl{get_message_sender_domain}}}} lsearch{/etc/mailips} {$value} {${lookup {${if match_domain {$original_domain} {+relay_domains} {${lc:$original_domain}} {} }} lsearch{/etc/mailips} {$value} {${lookup {${perl{get_sender_from_uid}}} lsearch*{/etc/mailips} {$value} {} }} }} }} }
helo_data = ${if > {${extract{size}{${stat:/etc/mailhelo}}}} {0} {${lookup {${lc:${perl{get_message_sender_domain}}}} lsearch{/etc/mailhelo} {$value} {${lookup {${if match_domain {$original_domain} {+relay_domains} {${lc:$original_domain}} {} }} lsearch{/etc/mailhelo} {$value} {${lookup {${perl{get_sender_from_uid}}} lsearch*{/etc/mailhelo} {$value} {$primary_hostname} }} }} }} {$primary_hostname} }
dkim_domain = ${perl{get_dkim_domain}}
dkim_selector = default
dkim_private_key = /var/cpanel/domain_keys/private/${dkim_domain}
dkim_canon = relaxed
dkim_hash = sha256
Essa estrutura replica o comportamento nativo do cPanel para seleção dinâmica do IP de saída a partir do /etc/mailips e alinhamento do HELO com o /etc/mailhelo, além de assinar a mensagem com a chave DKIM correta durante o encaminhamento.
Reconstrução da configuração e reinício do serviço#
Após salvar as modificações no painel ou via terminal, reconstrua a configuração oficial e reinicie o serviço do Exim:
# Valida a sintaxe dos templates e compila o /etc/exim.conf definitivo
/scripts/buildeximconf
# Reinicia o serviço do Exim no Systemd
/scripts/restartsrv_exim
Para validar se o novo símbolo foi devidamente carregado na memória do daemon:
exim -bP transport dkim_remote_forwarded_smtp
A saída esperada deve listar todos os parâmetros declarados:
driver = smtp
dkim_canon = relaxed
dkim_domain = ${perl{get_dkim_domain}}
dkim_hash = sha256
dkim_private_key = /var/cpanel/domain_keys/private/${dkim_domain}
dkim_selector = default
hosts_require_tls = *
Boas práticas para manter a consistência entre rotas e transportes no Exim#
O erro transport not found durante a verificação de remetente evidencia uma característica importante do Exim: sua capacidade de expandir strings dinamicamente em tempo de execução pode adiar a percepção de erros estruturais até que uma condição específica seja acionada por um cliente externo.
Ao manter regras customizadas no servidor:
- Sempre valide a paridade entre roteadores e transportes: se uma condição no roteador aponta para um transporte condicional (como no uso conjunto de SRS e smarthosts), verifique se todos os nomes retornados estão formalmente declarados na seção
TRANSPORTSTART. - Não confie apenas em testes básicos de roteamento: lembre-se de que ferramentas intermediárias como antivírus e filtros de spam locais podem capturar mensagens nos primeiros estágios do roteamento, escondendo falhas que só aparecem em consultas completas de
sender verify. - Persista ajustes em templates gerenciados: em painéis como o cPanel, utilize sempre as seções dedicadas de configuração para que rebuilds automáticos e atualizações do sistema não removam suas definições customizadas.
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