Hermes dejó de creer el dmarc=pass del propio remitente


Pones a Hermes en tu bandeja de entrada: el resumen diario llega, lo lee, lo resume y lo recuerda. Tras una actualización, no llega nada en absoluto — ni siquiera de remitentes que están en tu lista de permitidos. La única pista es una línea WARNING en el log, que menciona un ajuste que nunca has configurado.

El problema es la pregunta “¿quién envió esto realmente?”. Hermes revisa el encabezado Authentication-Results para establecer la identidad del remitente, y ese encabezado es texto plano que el remitente puede escribir él mismo. Cuando tu servidor receptor no estampa su propia versión, una línea inyectada se coloca arriba y Hermes solía tratarla como autoritativa. La corrección es contundente: sin pin, no hay confianza.

Cómo funcionaba la falsificación

Authentication-Results es el encabezado que avala que “este mensaje pasó SPF / DKIM / DMARC”. Normalmente tu servidor receptor lo añade después de que llega el mensaje — precisamente porque el servidor ha hecho la verificación, su veredicto es el que vale la pena leer.

Pero es un encabezado normal. Un remitente puede incluir su propia línea: attacker.self; dmarc=pass header.from=allowed.example. Si ejecutas un MTA autoalojado que no estampa un authserv-id propio (estilo OpenDKIM / OpenDMARC), esa línea se queda arriba. El comportamiento antiguo de Hermes leía el encabezado más alto sin importar quién afirmara haberlo escrito, así que:

  • el atacante falsifica From: para que coincida con una dirección de tu lista de permitidos;
  • añade su propia línea dmarc=pass;
  • Hermes considera al remitente autenticado y entrega.

El _verify_sender_authentication reescrito acepta solo dos resultados — lee el Authentication-Results más alto y exige que su authserv-id coincida con el tuyo exactamente:

if not authserv_id:  # sin un pin, el encabezado más alto puede ser uno que escribió el remitente
    return False, _MISSING_AUTHSERV_REASON
if clauses[0].strip().lower() != authserv_id:
    return False, _UNTRUSTED_AUTHSERV_REASON

También se apretaron dos vecinos: dos resultados dmarc en un mismo encabezado ahora producen ambiguous dmarc result en lugar de una conjetura, y un dmarc=pass debe tener cada header.from alineado con el dominio del From: que Hermes analizó.

Qué cambia realmente tras la actualización

Esto es un cambio de comportamiento, así que el radio de impacto importa:

  • A quién afecta: despliegues con autenticación de remitente activada y sin pin configurado. La autenticación de remitente está activada por defecto, incluidos los setups de acceso abierto como EMAIL_ALLOW_ALL_USERS / GATEWAY_ALLOW_ALL_USERS.
  • Qué ocurre: cada mensaje que necesita un From: autenticado se descarta, incluidos los remitentes de la lista de permitidos, hasta que el operador haga una de dos cosas — configurar EMAIL_AUTHSERV_ID (o la clave de configuración platforms.email.authserv_id), o excluirse explícitamente de la autenticación de remitente.
  • Qué ves: cada cuenta sin pin registra un WARNING cuando se conecta, diciendo que el authserv-id no está configurado y que Hermes se niega a confiar en Authentication-Results, además de ambas vías de escape (configurar el authserv-id, o poner require_authenticated_sender: false / EMAIL_TRUST_FROM_HEADER=true). El aviso también se dispara cuando la primera conexión de una cuenta falla y el vigilante de reconexión la levanta más tarde.
  • A quién no afecta: despliegues con pin y despliegues que ya se habían excluido. Se comportan exactamente igual que antes.

Encontrar el valor que fijar

No lo adivines. Abre un mensaje que tu servidor entregó, mira el código fuente en bruto (“Mostrar original” en Gmail), encuentra el encabezado Authentication-Results más alto y toma el token antes del primer punto y coma.

  • Para Gmail ese es mx.google.com;
  • si el token cambia entre mensajes, es un hostname por relay. La jugada correcta entonces es hacer que tu MTA estampe un authserv-id estable (AuthservID de OpenDKIM, el equivalente en OpenDMARC) y fijar eso.

Cualquiera de las dos configuraciones funciona; la variable de entorno es más amigable para contenedores:

# .env o entorno del contenedor
EMAIL_AUTHSERV_ID=mx.google.com
# config.yaml
platforms:
  email:
    authserv_id: mx.google.com
    # mantén el modo estricto por defecto; desactívalo solo si aceptas el riesgo de falsificación
    require_authenticated_sender: true

El valor se normaliza a minúsculas, así que las mayúsculas no importan, pero el dominio debe coincidir — no lo amplíes a un dominio padre para ahorrar teclas.

Si de verdad necesitas que el correo vuelva a fluir hoy

Configurar platforms.email.require_authenticated_sender: false (o EMAIL_TRUST_FROM_HEADER=true) hace que Hermes vuelva a aceptar remitentes no autenticados. Eso es una degradación deliberada: en un MTA autoalojado significa que cualquiera puede falsificar un remitente de la lista de permitidos. La mejor alternativa es un authserv-id estable en tu MTA — configúralo una vez y deja de pensar en ello.

Para otras correcciones de falsificación de identidad de la misma familia, consulta las tres correcciones de suplantación de identidad del remitente; el resto de la configuración de la plataforma de correo vive en la referencia de comandos de Hermes.

La versión corta

  • Authentication-Results es un encabezado que el remitente puede escribir; en un MTA autoalojado sin estampado puede colocarse arriba;
  • Hermes ahora exige una coincidencia exacta de authserv-id y descarta el correo que necesita un remitente autenticado hasta que configures uno, registrando un WARNING;
  • copia el token antes del primer punto y coma del Authentication-Results más alto (Gmail: mx.google.com) en EMAIL_AUTHSERV_ID / platforms.email.authserv_id;
  • la exclusión es require_authenticated_sender: false o EMAIL_TRUST_FROM_HEADER=true, y significa aceptar el riesgo de falsificación.