Отклонённые письма не определяются

Я пытаюсь разобраться, как заставить Discourse очищать списки электронной почты, в частности, обрабатывать письма с ошибками доставки (bounce). Сайт использует частный SMTP-сервер для отправки писем, но адрес для получения ответов настроен на Gmail-аккаунт, к которому Discourse получает доступ по протоколу POP.

Таким образом, я вижу письма с ошибками доставки в панели управления «Полученные» (Received).

Однако в панели управления «Письма с ошибками доставки» (Bounced) ничего нет.

При просмотре журналов ошибок Discourse я вижу, что система обнаруживает письмо с ошибкой доставки:

Email can not be processed: Email::Receiver::BouncedEmailError

Delivered-To: xxx.yyy@gmail.com
Received: by 2002:xxx:7022:911:xx:73:xxx:f96 with SMTP id xxx7csp1115046dlb;
Thu, 25 Jan 2024 12:03:33 -0800 (PST)
X-Google-Smtp-Source: AGHT+IEagzW8QOUgAyfOxU9wYaox/wuiL/wNqWhvftUB4uO/85r9H/55+FnfT6NrSTkLI5kfj+Vy
X-Received: by 2002:xxx:620a:4xxx:b0:xxxx:9265 with SMTP id br34-xxx8ba09265mr280168qkb.77.17062130xxx;
Thu, 25 Jan 2024 12:03:33 -0800 (PST)
Return-Path: <>
Received: from xxx.com (xxx.com. [207.xxx.xxx.xxx])
by mx.google.com with ESMTPS id bi4-xxx0b00783c84e9ef8si590747qkb.206.xxx.01.xx.12.03xxx
for xxx.yyy@gmail.com
(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
Thu, 25 Jan 2024 12:03:33 -0800 (PST)
Received-SPF: none (google.com: xxx.com does not designate permitted sender hosts) client-ip=207.xxx.xxx.xxx;
Authentication-Results: mx.google.com;
dkim=pass header.i=@xxx.co.nz header.s=alpha header.b=biFVvXep;
arc=fail (signature failed);
spf=none (google.com: xxx.com does not designate permitted sender hosts) smtp.helo=xxx.com;
dmarc=pass (p=NONE sp=REJECT dis=NONE) header.from=xxx.co.nz
Received: from xxx.co.nz (xxx.co.nz [210.xxx.xxx.5x])
by xxx.com (Postfix) with ESMTP id 4DD66xxx8E3
for xxx@zzz.com; Thu, 25 Jan 2024 20:03:28 +0000 (UTC)
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zzz.com;
s=dkim; t=1706213008;
h=from:from:reply-to:subject:subject:date:date:message-id:message-id:
to:to:cc:mime-version:mime-version:content-type:content-type:
dkim-signature;

Таким образом, мой вопрос:

  1. Почему Discourse не помечает эти письма как доставленные с ошибкой (bounced) и как мне это исправить?

Discourse использует reply_key для связывания возвратов с отправленными письмами. Когда Discourse отправляет почту, он использует значение reply_by_email_address в качестве отправителя SMTP-оболочки.

Вам необходимо убедиться, что ваш reply_by_email_address включает {reply_key}, чтобы ваш экземпляр мог связывать возвраты с правильным отправленным письмом.

Например, здесь, на meta, это установлено как incoming+%{reply_key}@meta.discoursemail.com, и любое письмо, отправленное на этот адрес, будет доставлено в этот экземпляр.

Спасибо за ответ. К сожалению, мне пришлось отключить reply_key в ответе на письмо, так как наш почтовый релей-сервер не поддерживает адресацию с использованием символа +. Есть ли какой-то другой вариант?

Ваш почтовый сервер-ретранслятор не должен иметь значения, важно только конечный сервер, где находится почтовый ящик. Локальные части адресов электронной почты непрозрачны для любых промежуточных серверов.

Разве это не Gmail, как указано ниже:


Не уверен, что у нас есть другой механизм для надежного обнаружения возвратов писем.

Чтобы прояснить ситуацию: целевой SMTP-сервер домена, получающий отклонённое письмо, не распознаёт адресацию с символом +, поэтому он пересылает письма только на основе поля To на адрес Gmail, который Discourse обрабатывает через POP. Если поле To содержит reply_key, то такое письмо не будет переслано на учётную запись Gmail, используемую Discourse.

Следовательно, я не могу размещать reply_key в адресе электронной почты. Можно ли разместить его в другом месте? Например, в теме письма, в теле письма или где-либо ещё, где Discourse сможет его распознать и обработать?

Было бы очень полезно, если бы вы указали, какие именно настройки вы используете, даже если они слегка анонимизированы.

Также стоит рассмотреть возможность настройки параметра alternative_reply_by_email_addresses.

Я не думаю, что это возможно.

Если это возможно, для вашей конфигурации я рекомендую следующее:

  • установите параметр reply_by_email_address в значение inbound+%{reply_key}@forum.hostname
  • настройте почтовый сервер для приёма писем на домен forum.hostname и пересылки всех писем на the.gmail.account@gmail.com

В данном случае промежуточный почтовый сервер не должен понимать адресацию с символом плюс; ему достаточно просто пересылать все письма для этого домена.

Абсолютно

Настройка парсинга электронной почты:

Настройка отправки электронной почты:

  • Электронное письмо, отправленное с помощью Discourse, исходит с адреса discourse@xxx.com через выделенный SMTP-сервер домена, настроенный для Discourse (с использованием адреса домена Discourse).
  • При ответах или возврате писем (bounce) SMTP-сервер домена пересылает их на адрес discourse.xxxx@gmail.com. Этот SMTP-сервер домена не распознаёт адресацию с использованием символа +, поэтому, если включить reply_key в адрес ответа, письмо будет отброшено сервером домена. Я могу настроить правила только для пересылки отдельных/уникальных входящих адресов электронной почты.
  • Форум Discourse затем использует POP через Gmail для доступа к этим пересланным ответам/возвращённым письмам и их парсинга.

Это помогло?

Я понимаю, что вы имеете в виду. К сожалению, из-за ограничений правил SMTP-сервера я не могу настроить пересылку для поддоменов; я могу настроить её только для уникальных идентификаторов адресов в поле «Кому».

Однако у меня есть уточнение — скорее, несоответствие в том, как, по-видимому, работает Discourse:

  1. Когда пользователь отвечает на электронное письмо, связанное с постом, оно приходит корректно — ответы работают безупречно, даже если параметр {reply_key} явно нигде не настроен (см. скриншоты выше).
  2. Однако, когда письмо возвращается (bounce), Discourse классифицирует его как Полученное (Received), а не как Возвращённое (Bounced).
  3. В журналах ошибок видно, что какой-то компонент Discourse распознаёт это как возвращённое письмо (см. журнал ошибок в моём первом сообщении). Если какой-то компонент Discourse распознаёт это как возвращённое письмо, почему оно отображается в разделе Полученное, а не в разделе Возвращённое?

Так почему же ответ пользователя на пост обрабатывается Discourse корректно, а возвращённое письмо — нет (и, судя по всему, существует несоответствие между журналами ошибок и панелью управления для возвращённых писем)? Не упустил ли я что-то в конфигурации?

Я также пробовал настроить параметр «Ответ по адресу электронной почты» в Discourse как discourse.xxx+%{reply_to}@gmail.com, но при доставке письма Gmail, похоже, считает, что домен yyy.com (домен SMTP Discourse) пытается подделать домен Gmail, и в итоге помечает письмо как спам. Похоже, что установка домена для ответа, отличного от домена отправителя, вызывает сбои DMARC и SPF.

ARC-Authentication-Results: i=1; mx.google.com;
       spf=softfail (google.com: domain of transitioning discussion.xxx+verp-b9c40db917ca04993dd3433cc9748518@gmail.com does not designate y.y.y.y as permitted sender) smtp.mailfrom=discussion.xxx+verp-b9c40db917ca04993dd3433cc9748518@gmail.com;
       dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=yyy.com
Return-Path: <discussion.xxx+verp-b9c40db917ca04993dd3433cc9748518@gmail.com>

Вы отключили опцию «Найти связанные посты по ключу» (надеюсь, вы прочитали предупреждение), поэтому Discourse использует заголовок in-reply-to электронной почты для определения, к какой теме/посту должен относиться ответ.

Это невозможно сделать для отскочивших писем — Discourse должен знать конкретное сообщение, которое отскочило, и эта информация гарантированно содержится только в одном месте — в адресе поля To: (который берется из адреса envelope-from исходного сообщения).

Для этого, когда Discourse отправляет сообщение, он должен получать ответ на адрес, с которого оно было отправлено. Discourse ищет это в заголовке To: (а не в envelope-to).

подделывает домен gmail

Если вы хотите отправлять письма с адреса gmail, вам нужно делать это через их серверы. Но они этого не любят.

Похоже, вы не настроили DKIM для yyy.com; вам следует это сделать. Если вы всё настроите правильно, DMARC должен пройти успешно.

Да, он настроен, и письма, отправляемые через SMTP-сервер yyy.com, успешно проходят проверку SPF, DKIM и DMARC (по крайней мере, со страницы «Отправить тестовое письмо» в консоли администратора).

Проблема возникает, когда я указываю адрес Gmail в качестве reply_by_email_address вместо адреса домена yyy.com. Есть ли способ настроить это так, чтобы DMARC не сбрасывался при указании адреса Gmail в reply_by_email_address, при этом исходящий сервер остаётся yyy.com?

Не может ли он проанализировать остальное содержимое или вложения в письме с ошибкой доставки, чтобы извлечь эту информацию, если не может найти то, что нужно, в «очевидном» месте? Или, по крайней мере, предоставить в настройках опцию для этого с необходимыми предупреждениями о возможности подмены личности?

Согласование DMARC не проходит, поскольку в данной ситуации reply_by_email_address используется как адрес envelope-from.

Почти единственное, что гарантированно остаётся целым при отклонении письма, — это адрес.

Возможно, тема письма, но размещать там такую информацию было бы непрактично.

Я вижу, что некоторые системы включают оригинальное сообщение как вложение… теоретически возможно проверять отклонённые письма на наличие вложений для получения дополнительной информации, если они существуют.

Если я правильно понимаю, reply_by_email_address должен быть установлен в поле reply-to заголовка письма, отправляемого системой Discourse (с домена yyy.com). Таким образом, когда пользователь отвечает, он отвечает на адрес в поле reply-to (gmail.com), а не на исходный адрес (yyy.com). Следовательно, в ответном письме адрес в поле from должен быть адресом пользователя, а адрес в поле to — адресом gmail.com.

Почему reply_by_email_address используется как адрес в поле from?

reply_by_email_address не используется как адрес «от», а как адрес envelope-from, именно для того, чтобы корректно работали уведомления о недоставке.

Вот изображение, демонстрирующее, как используется каждый адрес. Сами адреса специфичны для нашего хостинга, но их достаточно для примера.

notification_email: notifications@hs1.discoursemail.com
reply_by_email_address: incoming+%{reply_key}@hs1.discoursemail.com

Уведомление:

Сообщение с возможностью ответа:

Спасибо. Это очень полезно.

Итак, по сути, reply_by_email_address используется в заголовке From, чтобы при возврате письма (bounce) оно возвращалось на этот адрес. Этот же адрес также устанавливается в заголовке Reply-To, если отправка ответов по электронной почте включена.

Таким образом, если моё понимание верно, то если в Discourse появится отдельная настройка (reply_to_email) для заголовка Reply-To, это решит проблему с неудачей DMARC. При этом при отправке в заголовке From будет использоваться домен yyy.com (взятый из reply_by_email_address), а в заголовке Reply-To — gmail.com (взятый из новой настройки reply_to_email). Если письмо вернётся, оно всё равно будет доставлено на адрес reply_by_email_address, но если пользователь ответит, ответ придёт на адрес reply_to_email.

Речь идёт об SMTP envelope-from, а не о заголовке From. Заголовок From не используется для возвратов.

Т.е. ①, а не ②:

Чтобы пройти проверку DMARC, должны совпадать либо SPF (который проверяет envelope-from в сочетании с IP-адресом отправителя), либо DKIM (который проверяет заголовок From и контрольные суммы сообщения) соответствие.

Получается, вся суть этой затеи — иметь «красивый» адрес reply-to, на который пользователи будут отвечать?

Вы хотите что-то вроде этого?

envelope-from: outgoing+12309847801923840923502389423@yyy.com
…
From: notifications@yyy.com
To: user@contoso.com
Reply-to: my_sweet_forum+12309847801923840923502389423@gmail.com

У вас должен быть именно такой envelope-from, иначе возвраты не будут работать.}

Да, всё верно. Идея в том, чтобы envelope-from отличался от reply-to.

Поскольку домен envelope-from совпадает с IP-адресом отправителя, SPF-проверка должна пройти успешно. В то же время ответы будут отправляться на Gmail для обработки, а если письмо отскочит, оно вернётся на сервер исходного домена, который затем перешлёт уведомление об ошибке обратно в почтовый ящик Gmail.

На самом деле это будет выглядеть так:

envelope-from: outgoing@yyy.com
…
From: notifications@yyy.com
To: user@contoso.com
Reply-to: my_sweet_forum+12309847801923840923502389423@gmail.com

В моей настройке для outgoing не используется VERP, так как мой входящий SMTP не поддерживает VERP (то есть уведомление об ошибке не будет содержать VERP-адрес). Вот почему reply-to отправляется на Gmail, поскольку он поддерживает VERP. Это не должно вызывать сбой DMARC, как происходит сейчас.