Anatomia de um ponteiro solto no Exim: depurando o erro "transport not found" em ambientes cPanel com smarthost e SRS
Voltar para blog

Anatomia de um ponteiro solto no Exim: depurando o erro "transport not found" em ambientes cPanel com smarthost e SRS

16/10/2026 · 7 min · E-mail

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.

  1. Roteadores e transportes são instanciados em estruturas internas (router_instance e transport_instance).
  2. O Exim indexa os nomes dos transportes existentes em uma tabela de dispersão (hash table).
  3. 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#

  1. No menu do WHM, acesse Service Configuration -> Exim Configuration Manager.
  2. Clique na aba Advanced Editor.
  3. Localize o bloco demarcado como Section: TRANSPORTSTART.
  4. 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:

  1. 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.
  2. 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.
  3. 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:

CC BY-NC

Este post está licenciado sob CC BY-NC.

Comentários

Participe da discussão abaixo.

0 comentários