Как настроить отправку почты через postfix в Битрикс24
Разработка
Разработка

Как настроить отправку почты через postfix в Битрикс24

По-умолчанию веб-окружение Битрикс24 использует msmtp для отправки почтовых сообщений. Преимущество такого решения — простота настройки. Недостаток — медленная отправка почтовых сообщений через внешние сервисы (Google, Yandex). В большинстве случаев, это не приводит к каким-либо проблемам, так как почтовые события отрабатывают из-под cron’a, но в ряде случаев это может создать проблему.

Почему так происходит?

Скорость отправки писем напрямую зависит от того, как настроена отправка почты в Битрикс24. На агентах или на хитах? А так же зависит от вида отправляемых писем. Например, отправка рассылок из CRM выполняется только на агентах — такие письма обрабатываются cron'ом по расписанию.

Если все агенты, которые выполняют получение и отправку писем исполняются на хитах (задан параметр немедленной доставки) или разработчик напрямую использует функцию mail(), то в этом случае она будет зависеть от активности сотрудников, которые работают на портале Битрикс24. Поэтому, если на портале отсутствует активность, рассылка писем будет выполняться достаточно медленно, при каждом хите сотрудника по несколько писем.
Переходить на Postfix имеет смысл если:
  • не устраивает низкая скорость отправки почты через msmtp
  • нужно настроить DKIM-подпись отправляемых писем
  • отправляемые письма не доходят до адресатов
У Битрикса в документации по веб-окружению есть инструкция по установке Postfix, но она почему-то не работает и вообще делает не совсем то, что нужно: Настройка Postfix для отправки почты.

Разберем настройку отправки почтовых сообщений с помощью Postfix. Будем считать, что наш Битрикс24 развёрнут на домене example.com и в его MX-записях прописаны сервера Яндекса.

Настраиваем Postfix

Ставим необходимые пакеты.
$ yum -y install postfix cyrus-sasl-plain
Добавляем в /etc/postfix/canonical указание через какой аккаунт отправлять почту:
# Слева - регулярное выражение, применяемое к отправителю
# Справа - отправитель, которого нужно поставить
/.+/ mail@example.com
Добавляем в /etc/postfix/generic отправку почты админу на внешний ящик:
# Слева - локальный пользователь

# Справа - отправитель, которого нужно поставить
root@localhost mail@example.com
bitrix@localhost mail@example.com
Добавляем в /etc/postfix/mailpasswd логин и пароль релей-хостов:
# [SMTP_HOST]:SMTP_PORT SMTP_USER:SMTP_PASSWORD
[smtp.yandex.ru] mail@example.com:SecretPassword*
* в этом примере мы создаем учетную запись для аутентификации на Яндексе. При отправке писем от mail@example.com необходимо авторизоваться на сервере Яндекса от этой же учетной записи с паролем SecretPassword

Добавляем в /etc/postfix/sender_relay привязку доменов и конкретных отправителей к внешним службам:
# Слева - отправитель, справа - релей
mail@example.com [smtp.yandex.ru]*
*в этом примере мы все сообщения, отправляемые от домена example.com переправляем через сервер smtp.yandex.ru.

Настройка значений From и Reply-To

По-умолчанию Битрикс24 будет использовать в качестве отправителя адрес, заданный в настройках главного модуля. Но вот в карточках лидов, контактов и компаний система использует мейл текущего авторизованного пользователя. Заголовки, например:
From: Иван Петров <i.petrov@example.com>
To: Фёдор Сидоров <f.sidorov@example.com>
В некоторых случаях это даже будет работать, но в основном нет. Такие сервисы, как Яндекс и Гугл блокируют отправку почты, если для авторизации SMTP используется один аккаунт, а в поле “From” стоит другое значение.

Чтобы этого не происходило — можно настроить замену заголовка From на статичное значение, которое невозможно изменить из Битрикс24. Но неправильно просто затереть значение, которое там стояло - нужно оставить возможность пользователю ответить на отправленное ему письмо.

Для этого у нас есть заголовок Reply-To. Но и это не все! При ответе пользователя на письмо, в качестве получателей будет подставляться два адреса (два поля Reply-To). Первый — сам отправитель, второй — технический адрес, с которого это письмо было отправлено. Сделаем, так чтобы технический адрес Postfix игнорировал и письмо отправлялось только его отправителю.

Добавляем блок в /etc/postfix/header_checks:
# Добавить в Reply-To значение, использованное в From
/^From: (.*)/ PREPEND Reply-To: $1
# Перезаписать From на значение по-умолчанию
/^From: (.*)/ REPLACE From: mail@example.com
# Удалить добавление технического адреса в ответном письме
/^(Reply-To:.*)/ IGNORE
Включаем обработку заголовков в /etc/postfix/main.cf:
header_checks = regexp:/etc/postfix/header_checks
Добавляем блок в /etc/postfix/main.cf:
# Отключаем ipv6, потому что он толком не работает
inet_protocols = ipv4

# Указываем все письма отсылать через этот релей
relayhost = [smtp.yandex.ru]

# Включение аутентификации на стороне клиента
$ smtp_sasl_auth_enable = yes
# Указывает на файл, в котором хранится база связок логин/пароль
$ smtp_sasl_password_maps = hash:/etc/postfix/mailpasswd

# Опции SASL. noanonymous указывает на запрет анонимной аутентификации
$ smtp_sasl_security_options = noanonymous
# Задает плагин SASL для аутентификации
$ smtp_sasl_type = cyrus
# Перечисляет механизмы проверки пользователя и пароля
$ smtp_sasl_mechanism_filter = login

# Включает зависящую от отправителя аутентификацию, отключает кэширование SMTP-соединения
$ smtp_sender_dependent_authentication = yes
# Указание на список адресов и почтовых серверов, через которые нужно отправлять письма на эти адреса
sender_dependent_relayhost_maps = hash:/etc/postfix/sender_relay
# Добавляет для домена указание через какой аккаунт отправлять почту
sender_canonical_maps = hash:/etc/postfix/canonical

# Добавляет отправку почты админу на внешний ящик
smtp_generic_maps = hash:/etc/postfix/generic
# Указывает, использовать ли TLS для SMTP
smtp_use_tls = yes
Логи о неработающем IPv6 выглядят как-то так:
Mar  5 19:48:28 MegaRoss-207-Krasnodar postfix/smtp[43577]: connect to smtp.gmail.com[2a00:1450:4010:c0b::6c]:587: Network is unreachable

Mar  5 19:48:28 MegaRoss-207-Krasnodar postfix/smtp[43577]: 919BB1A3E6E: to=<user@example.com>, relay=none, delay=0.44, delays=0.02/0/0.43/0, dsn=4.4.1, status=deferred (connect to smtp.gmail.com[2a00:1450:4010:c0b::6c]:587: Network is unreachable)
Редактируем параметр sendmail_path в /etc/php.d/bitrixenv.ini:
# Удаляем работу через msmtp
; sendmail_path = msmtp -t -i
# Добавляем работу через postfix
sendmail_path = /usr/sbin/sendmail -t -i -f mail@example.com
Генерируем индексированные файлы:
$ postmap /etc/postfix/generic
$ postmap /etc/postfix/canonical
$ postmap /etc/postfix/mailpasswd
$ postmap /etc/postfix/sender_relay
Добавляем в автозагрузку и запускаем сервис:
$ systemctl enable postfix
$ systemctl start postfix

Отправка тестового письма

Выполняем из консоли следующую команду:
$ echo "Test message" | mail -s "Test subject" user@example.com
Смотрим логи, ищем фразу “status=sent”:
$ tail /var/log/maillog
Mar  6 10:33:43 MegaRoss-207-Krasnodar postfix/pickup[45768]: 24FD91A3E6F: uid=0 from=<root>
Mar  6 10:33:43 MegaRoss-207-Krasnodar postfix/cleanup[46875]: 24FD91A3E6F: message-id=<20190306073343.24FD91A3E6F@megaross-207-krasnodar.ugtel.ru>
Mar  6 10:33:43 MegaRoss-207-Krasnodar postfix/qmgr[45769]: 24FD91A3E6F: from=<mail@example.com>, size=478, nrcpt=1 (queue active)
Mar  6 10:33:44 MegaRoss-207-Krasnodar postfix/smtp[46877]: 24FD91A3E6F: to=<user@example.com>, relay=smtp.yandex.ru[187.150.150.138]:25, delay=1.3, delays=0.03/0.01/0.33/0.89, dsn=2.0.0, <strong>status=sent</strong> (250 2.0.0 Ok: queued on smtp2o.mail.yandex.net as 1551857624-LUq73TrSs8-XhiW0rUr)
Mar  6 10:33:44 MegaRoss-207-Krasnodar postfix/qmgr[45769]: 24FD91A3E6F: removed
Поздравляем, тестовое письмо успешно отправлено! :)

Настраиваем DKIM

Переходим к настройке DKIM.
info-message-background

DKIM (DomainKeys Identified Mail) — технология, которая борется с поддельными сообщениями. Для почтового домена генерируется пара ключей (открытый и закрытый). Закрытым ключом подписываются отправляемые сообщения. Открытый ключ добавляется на домене в качестве DNS в виде TXT-записи. Когда принимающий сервер находит в заголовке сообщения DKIM-подпись, он выполняет DNS-запрос к серверу домена на получение открытого ключа.

Это нужно для массовой рассылки писем, так как наличие DKIM-подписи — обязательное условие для большинства почтовых провайдеров. Письма с отсутствием такой подписи скорее всего попадут в спам.

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

Чтобы снизить вероятность попадания писем в спам (повысить доверие почтовых серверов к отправляемым письмам), настроим добавление подписи DKIM в заголовок. Ставим нужные пакеты:
# Пакет opendkim-tools требуется для генерации ключей
$ yum -y install opendkim opendkim-tools
Генерируем ключи:
$ opendkim-genkey -D /etc/opendkim/keys -d example.com -s default*
* после выполнения команды в директории /etc/opendkim/ должны появится ключи (публичный и приватный) для домена example.com Смотрим результат:
$ ls -alh /etc/opendkim/keys
total 8,0K
drwxr-x--- 2 opendkim opendkim  48 мар  4 15:24 .
drwxr-xr-x 3 root     opendkim  74 мар  4 15:33 ..
-rw-r----- 1 opendkim opendkim 887 мар  4 15:24 default.private
-rw-r----- 1 opendkim opendkim 316 мар  4 15:24 default.txt
Изменяем права на файлы и директории:
$ chgrp opendkim /etc/opendkim/*
$ chgrp opendkim /etc/opendkim/keys/*
$ chmod g+r /etc/opendkim/keys/*
$ gpasswd -a postfix opendkim
Добавляем в автозагрузку и запускаем сервис:
$ systemctl enable opendkim
$ systemctl start opendkim
Идём в настройки DNS домена и добавляем TXT-запись для поддомена default._domainkey, в содержимое ставим содержимое публичного ключа:
$ cat /etc/opendkim/keys/default.txt 
default._domainkey	IN	TXT	( "v=DKIM1; k=rsa; p=MIGfMA0GC5qGSIb3DQEBAQUAA4GNADCBiQKBgQDDW...JtXs1XbcZ6qHNJKXw1DAQAB" )  ; ----- DKIM key default for example.com
Проверим, что всё работает:
$ dig TXT default._domainkey.example.com
;; <<>> DiG 9.10.3-P4-Ubuntu <<>> TXT default._domainkey.example.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 50978
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;default._domainkey.example.com. IN	TXT
;; ANSWER SECTION:
default._domainkey.example.com. 599 IN	TXT	"v=DKIM1; k=rsa; p=MIGfMA0GC5qGSIb3DQEBAQUAA4GNADCBiQKBgQDDW...JtXs1XbcZ6qHNJKXw1DAQAB"
;; Query time: 94 msec
;; SERVER: 127.0.1.1#53(127.0.1.1)
;; WHEN: Wed Mar 06 10:40:22 MSK 2019
;; MSG SIZE  rcvd: 296
Добавляем блок в /etc/opendkim/KeyTable (одной строкой):
# Полное имя ключа - домен - селектор - путь к приватному ключу (одной строкой)
default._domainkey.example.com example.com:default:/etc/opendkim/keys/default.private
Добавляем блок в /etc/opendkim/SigningTable:
# Какие домены - каким ключом подписывать
*@example.com default._domainkey.example.com
И пишем в /etc/opendkim.conf:
# Режим работы - подписывать(signer)(s) и валидировать/контролировать(verified)(v)
Mode	sv
# Указываем список ключей
KeyTable file:/etc/opendkim/keytable
# Соответствие ключей и доменов
SigningTable file:/etc/opendkim/signingtable
Укажем postfix’у использовать opendkim - добавляем блок в /etc/postfix/main.cf:
# Обработка ошибок при фильтрации писем до попадания сообщений в очередь 
milter_default_action = accept
# Версия протокола: 2 для 2.3 ≤ Postfix ≤ 2.5, 6 для Postfix ≥ 2.6
milter_protocol = 2
# Подписывать сообщения, полученные по SMTP с других серверов
smtpd_milters = inet:127.0.0.1:8891
# Подписывать сообщения, приходящие из sendmail
non_smtpd_milters = inet:127.0.0.1:8891
Перезапускаем postfix и opendkim:
$ systemctl restart postfix
$ systemctl restart opendkim
Проверяем работу - ищем в лог-файле почты строку “DKIM-Signature field added”:
$ tail -20 /var/log/maillog
Mar  6 09:34:25 server1 postfix/pickup[29010]: 0A2AA407C6EA: uid=600 from=<bitrix24@example.com>
Mar  6 09:34:25 server1 postfix/cleanup[29684]: 0A2AA407C6EA: prepend: header From: =?UTF-8?B?0JzQsNC60YHQuNC8INCU0L7RgNC+0YnQtdC90LrQvg==?= <user2@example.com> from local; from=<bitrix24@example.com>: Reply-To: =?UTF-8?B?0JzQsNC60YHQuNC8INCU0L7RgNC+0YnQtdC90LrQvg==?= <user2@example.com>
Mar  6 09:34:25 server1 postfix/cleanup[29684]: 0A2AA407C6EA: message-id=<crm.activity.9434-0ZQRGV@bitrix24.example.com>
Mar  6 09:34:25 server1 opendkim[23286]: 0A2AA407C6EA: DKIM-Signature field added (s=default, d=example.com)
Mar  6 09:34:25 server1 postfix/qmgr[29011]: 0A2AA407C6EA: from=<bitrix24@example.com>, size=1282, nrcpt=2 (queue active)
Mar  6 09:34:25 server1 postfix/smtp[29687]: 0A2AA407C6EA: to=<0123456@example.com>, relay=121.145.167.190[121.145.167.190]:25, delay=0.27, delays=0.1/0/0.12/0.05, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 44DkWP1dDJz3X8r)
Mar  6 09:34:25 server1 postfix/smtp[29687]: 0A2AA407C6EA: to=<user2@example.com>, relay=121.145.167.190[121.145.167.190]:25, delay=0.27, delays=0.1/0/0.12/0.05, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 44DkWP1dDJz3X8r)
Mar  6 09:34:25 server1 postfix/qmgr[29011]: 0A2AA407C6EA: removed

Настраиваем DMARC

Теперь перейдем к настройке записи DMARC. Это нужно, чтобы получатель мог быть уверен, что письмо с адреса durov@vk.com действительно отправил Павел Дуров, а не злоумышленник.
info-message-background

DMARC (Domain-based Message Authentication, Reporting and Conformance) — политика обработки почтовых сообщений, основанная на SPF или DKIM аутентификации. С ее помощью администратор почты может самостоятельно определять, что делать с письмами, которые не прошли проверку доменом на сторонних серверах. Настраивается на домене путем добавления DNS в виде TXT-записи.

В настройках домена, добавляем DNS-запись TXT для поддомена _dmarc.example.com с таким значением:
v=DMARC1; p=quarantine
Проверяем, что всё работает:
$ dig TXT _dmarc.example.com
;; <<>> DiG 9.10.3-P4-Ubuntu <<>> TXT _dmarc.example.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 22795
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;_dmarc.example.com.		IN	TXT

;; ANSWER SECTION:
_dmarc.example.com.	600	IN	TXT	"v=DMARC1; p=quarantine"

;; Query time: 52 msec
;; SERVER: 127.0.1.1#53(127.0.1.1)
;; WHEN: Wed Mar 06 10:40:55 MSK 2019
;; MSG SIZE  rcvd: 83

Опциональная автоматизация настройки Postfix через ansible

Практически всё можно автоматизировать. Мы используем ansible, вот ссылка на репозиторий
Пользуйтесь:)
Алексей Цывенко
Алексей Цывенко
Системный администратор