Hermes parou de acreditar no dmarc=pass do próprio remetente


Você colocou o Hermes na sua caixa de entrada: o resumo diário chega, ele lê, resume e lembra. Depois de uma atualização, nada mais chega — nem mesmo de remetentes na sua allowlist. A única pista é uma linha WARNING no log, citando uma configuração que você nunca definiu.

O problema é a pergunta “quem de fato enviou isto?”. O Hermes verifica o header Authentication-Results para estabelecer a identidade do remetente, e esse header é texto simples que o próprio remetente pode escrever. Quando o seu servidor de recebimento não carimba a própria versão, uma linha injetada fica por cima e o Hermes costumava tratá-la como autoritativa. A correção é direta: sem pin, sem confiança.

Como a falsificação funcionava

Authentication-Results é o header que atesta “esta mensagem passou por SPF / DKIM / DMARC”. Normalmente o seu servidor de recebimento o adiciona depois que a mensagem chega — justamente porque o servidor fez a verificação, o veredito dele é o que vale a pena ler.

Mas é um header normal. Um remetente pode incluir a própria linha: attacker.self; dmarc=pass header.from=allowed.example. Se você roda um MTA auto-hospedado que não carimba um authserv-id próprio (estilo OpenDKIM / OpenDMARC), essa linha fica por cima. O comportamento antigo do Hermes lia o header do topo independentemente de quem dizia tê-lo escrito, então:

  • o atacante falsifica o From: para casar com um endereço na sua allowlist;
  • ele adiciona a própria linha dmarc=pass;
  • o Hermes considera o remetente autenticado e entrega.

O _verify_sender_authentication reescrito aceita apenas dois resultados — ele lê o Authentication-Results do topo e exige que seu authserv-id case com o seu exatamente:

if not authserv_id:  # without a pin the topmost header may be one the sender wrote
    return False, _MISSING_AUTHSERV_REASON
if clauses[0].strip().lower() != authserv_id:
    return False, _UNTRUSTED_AUTHSERV_REASON

Dois vizinhos também foram apertados: dois resultados dmarc em um único header agora produzem ambiguous dmarc result em vez de um palpite, e um dmarc=pass precisa ter todo header.from alinhado com o domínio do From: que o Hermes analisou.

O que de fato muda depois da atualização

Esta é uma mudança de comportamento, então o raio de alcance importa:

  • Quem é afetado: implantações com autenticação de remetente ativada e sem pin definido. A autenticação de remetente está ativa por padrão, incluindo configurações de acesso aberto como EMAIL_ALLOW_ALL_USERS / GATEWAY_ALLOW_ALL_USERS.
  • O que acontece: toda mensagem que precise de um From: autenticado é descartada, inclusive remetentes na allowlist, até que o operador faça uma de duas coisas — definir EMAIL_AUTHSERV_ID (ou a chave de configuração platforms.email.authserv_id), ou optar explicitamente por sair da autenticação de remetente.
  • O que você vê: cada conta sem pin registra um WARNING quando se conecta, dizendo que o authserv-id não está configurado e que o Hermes se recusa a confiar em Authentication-Results, além das duas saídas de emergência (definir o authserv-id, ou definir require_authenticated_sender: false / EMAIL_TRUST_FROM_HEADER=true). O aviso também dispara quando a primeira conexão de uma conta falha e o watcher de reconexão a traz depois.
  • Quem não é afetado: implantações com pin e implantações que já haviam optado por sair. Elas se comportam exatamente como antes.

Encontrando o valor para fixar

Não adivinhe. Abra uma mensagem que o seu servidor entregou, veja o código-fonte bruto (“Show original” no Gmail), encontre o header Authentication-Results do topo e pegue o token antes do primeiro ponto e vírgula.

  • No Gmail, é mx.google.com;
  • se o token muda entre mensagens, é um hostname por relay. A atitude certa então é fazer seu MTA carimbar um authserv-id estável (AuthservID do OpenDKIM, o equivalente no OpenDMARC) e fixar esse.

Qualquer uma das configurações funciona; a variável de ambiente é mais amigável para contêineres:

# .env ou ambiente do contêiner
EMAIL_AUTHSERV_ID=mx.google.com
# config.yaml
platforms:
  email:
    authserv_id: mx.google.com
    # mantenha o modo estrito padrão; só desative se você aceitar o risco de falsificação
    require_authenticated_sender: true

O valor é normalizado para minúsculas, então maiúsculas/minúsculas não importam, mas o domínio precisa casar — não amplie para um domínio pai só para economizar digitação.

Se você realmente precisa que o correio flua de novo hoje

Definir platforms.email.require_authenticated_sender: false (ou EMAIL_TRUST_FROM_HEADER=true) faz o Hermes aceitar remetentes não autenticados novamente. Esse é um downgrade deliberado: em um MTA auto-hospedado, significa que qualquer um pode falsificar um remetente na allowlist. A alternativa melhor é um authserv-id estável no seu MTA — configure uma vez e pare de pensar nisso.

Para outras correções de falsificação de identidade da mesma família, veja as três correções de spoofing de identidade de remetente; o restante da configuração da plataforma de e-mail está na referência de comandos do Hermes.

Resumindo

  • O Authentication-Results é um header que o remetente pode escrever; em um MTA auto-hospedado sem carimbo, ele pode se ordenar no topo;
  • o Hermes agora exige uma correspondência exata de authserv-id e descarta mensagens que precisem de um remetente autenticado até você definir um, registrando um WARNING;
  • copie o token antes do primeiro ponto e vírgula do Authentication-Results do topo (Gmail: mx.google.com) para EMAIL_AUTHSERV_ID / platforms.email.authserv_id;
  • a saída é require_authenticated_sender: false ou EMAIL_TRUST_FROM_HEADER=true, e isso significa aceitar o risco de falsificação.