Сервер для отправки писем с сайта (debian, mail, postfix, SPF, DKIM)

  1. Введение
  2. Подготовка к настройке собственного почтового сервера
  3. Что такое DMARC и с чем его едят
  4. PTR-запись
  5. Поднимаем POSTFIX
  6. Подключаем OpenDKIM
  7. Настраиваем SPF
  8. Тестируем отправку
  9. Безопасность

Введение

В последнее время подключить почту к домену не составляет особого труда: сервисы Yandex, Google, Mail.ru и т.п. предоставляют широкие возможности для работы с почтой на своём домене. Там можно создавать ящики, заводить пользователей, настраивать учётные записи, принимать и отправлять почту через популярные клиенты. Но есть одно большое НО — ограничения на отправку писем. Если отправлять много однотипных писем, например через Яндекс, то рано или поздно это заблокируют. Причём решения принимает не человек, а бездушная машина, которой абсолютно без разницы, отсылаете ли вы письма себе или своим коллегам, или добропорядочным клиентам.

Но можно ли сделать так, чтобы письма с сайта отправлять прямо с сервера сайта, а принимать на Yandex или Google? Да, можно!

[Наверх]

Подготовка к настройке собственного почтового сервера

Прежде чем настраивать почтовый сервер убедитесь, что хостинг-провайдер разрешает использовать SMTP-порты (25, 465 и другие). Многие провайдеры по умолчанию блокируют эти порты и открывают при обращении в службу поддержки. Уточните у своего хостера требования для того, чтобы безбоязненно рассылать письма, а также требования к самим письмам, чтобы они не подпадали под определение SPAM.

Быстрая проверка доступности SMTP через консоль:

telnet smtp.yandex.ru 25

Если появится примерно такой текст, то порты открыты:

Trying 77.88.21.158...
Connected to smtp.yandex.ru.
Escape character is '^]'.
220 sas1-27140bb19246.qloud-c.yandex.net ESMTP (Want to use Yandex.Mail for your domain? Visit https://pdd.yandex.ru

А вот так выглядит сообщение, если порты закрыты и придётся обращаться в поддержку хостера:

Trying 77.88.21.158...
Trying 2a02:6b8::19d...
telnet: Unable to connect to remote host: Network is unreachable

[Наверх]

Что такое DMARC и с чем его едят

DMARC — это набор правил и протоколов для авторизации почты. Если перевести на общечеловеческий, то это будет означать "делай вот так и письма не будут сразу падать в спам".

Дело в том, что протоколы электронной почты разрабатывались в те славные времена, когда люди доверяли друг другу. И если на сервер приходило письмо, то оно сразу падало в ящик получателя. Сам протокол отправки писем не предполагает авторизацию сервера, который отправляет письмо, а это значит, что любое устройство в сети Интернет может слать письма от кого угодно и куда угодно. Да-да, именно от кого угодно и куда угодно!

DMARC позволяет:

  1. Проверять сервер, чтобы ему было дозволено отправлять письма с определённого домена (SPF-запись).
  2. Проверять письмо, что оно пришло именно то, какое было отправлено (цифровая подпись DKIM).
  3. Получать отчёты о проблемах доставки (DMARC-запись).

То есть DMARC не решает проблемы спама на 100%, но позволяет понимать, что письмо было отправлено и доставлено с добросовестного сервера, а не с кофеварки бабы Маши. Если не выполнять требования DMARC, то вероятность того, что ваше письмо отправится в спам, будет примерно 100%.

[Наверх]

PTR-запись

Помимо DMARC существует ещё один способ проверить, что сервер, который отправляет письмо, это настоящий сервер, а не кофеварка. Надо всего лишь получить его PTR-запись по IP-адресу:

# Получаем IP-адрес по домену
$ host mta-6.email2.gog.com
mta-6.email2.gog.com has address 188.114.82.45

# Получаем домен по IP-адресу
$ host 188.114.82.45
45.82.114.188.in-addr.arpa domain name pointer mta-6.email2.gog.com.

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

Без PTR-записи некоторые сервера не будут даже и пытаться принимать вашу почту, сразу обрывают соединение. Лучше уточните этот вопрос заранее.

[Наверх]

Поднимаем POSTFIX

Все команды необходимо выполнять в консоли от суперпользователя.

Для удобства сделаем алиасы для замены доменов из примеров на ваши значения:

alias my_smtp_host='sed "s/smtp.example.com/ВАШ_ХОСТ/g"'
alias my_mail_domain='sed "s/example.org/ВАШ_ПОЧТОВЫй_ДОМЕН/g"'

Дальше будут приводиться конфиги с smtp.example.com и example.org, а фильтры будут заменять их на правильные значения. Все команды оптимизированы для копирования и вставки в консоль БЕЗ редактирования (кроме тех мест, где встречается ВАШ_..., там нужно вставлять ваши значения).

Задаём имя хоста

echo smtp.example.com | my_smtp_host > /etc/hostname
echo 127.0.0.1 smtp.example.com | my_smtp_host >> /etc/hosts

Теперь следующая команда должна выводить ВАШ_ХОСТ:

hostname --fqdn

Если выводится ошибка или значение отличное от вашего хоста, проверьте /etc/hostname и /etc/hosts.

Устанавливаем и настраиваем POSTFIX

apt install postfix mailutils

Создаём конфиг (предполагается, что сервер будет только отправлять письма, а принимать будет кто-нибудь другой):

(cat | my_smtp_host > /etc/postfix/main.cf) <<EOF
# Настройки доменов
myhostname = smtp.example.com
mydomain = smtp.example.com
myorigin = smtp.example.com

# Тут что-то на эльфийском
compatibility_level = 2
command_directory = /usr/sbin
daemon_directory = /usr/lib/postfix/sbin
milter_default_action = accept

# Подсети, на которых будет работать постфикс
inet_interfaces = 127.0.0.1
mynetworks = 127.0.0.0/8
inet_protocols = ipv4

# Ограничение на попытки отправить письмо (30 минут, чтобы не забивать очередь)
maximal_queue_lifetime = 30m
bounce_queue_lifetime = 30m

# Добавляем некоторые важные заголовки, если их нет (например Date, Message-ID)
always_add_missing_headers = yes

# Папка для писем
data_directory = /var/lib/postfix
EOF

Перезапускаем POSTFIX:

service postfix stop; service postfix start

[Наверх]

Подключаем OpenDKIM

Устанавливаем OpenDKIM:

apt install opendkim opendkim-tools net-tools

Создаём конфиг:

mv /etc/opendkim.conf /etc/opendkim.conf.orig
(cat | my_mail_domain > /etc/opendkim.conf) <<EOF
AlwaysAddARHeader       Yes
SubDomains              Yes
RemoveARAll             Yes
RemoveOldSignatures     Yes
SendReports             Yes
Canonicalization        relaxed/relaxed
Mode                    sv
UserID                  opendkim:opendkim

Syslog                  Yes
SyslogSuccess           Yes
LogWhy                  Yes

KeyTable                /etc/opendkim/KeyTable
SigningTable            refile:/etc/opendkim/SigningTable

## Настройки для работы внурисети (определяют сети/хосты, с которых письма должны подписываться)
#ExternalIgnoreList      refile:/etc/opendkim/TrustedHosts
#InternalHosts           refile:/etc/opendkim/TrustedHosts

PidFile                 /run/opendkim/opendkim.pid
Socket                  inet:10021@localhost
ReportAddress           "DKIM Error Postmaster" <postmaster@example.org>
EOF

Создаём папку для хранения ключей и таблиц:

mkdir -m 750 /etc/opendkim && chown opendkim:opendkim /etc/opendkim

Генерируем ключи для домена из настроек POSTFIX и селектора key1:

opendkim-genkey -D /etc/opendkim --domain=$(echo example.org | my_mail_domain) --selector=key1

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

Задаём верные права на доступ к приватному ключу:

chown opendkim:opendkim /etc/opendkim/*.private
chmod 600 /etc/opendkim/*.private

Заполняем таблицу приватных ключей (имя_ключа домен:селектор:/путь/до/ключа):

echo "key1 example.org:key1:/etc/opendkim/key1.private" | my_mail_domain > /etc/opendkim/KeyTable

Говорим, какой домен, каким ключом подписывать (шаблон имя_ключа):

echo "*@example.org key1" | my_mail_domain > /etc/opendkim/SigningTable

Перезапускаем OpenDKIM:

service opendkim restart

Прописываем настройки для POSTFIX:

cat >> /etc/postfix/main.cf <<EOF

# Настройки DKIM
smtpd_milters = inet:localhost:10021
non_smtpd_milters = inet:localhost:10021
EOF
service postfix restart

Выводим конфиг TXT-записи с публичным ключом для домена:

echo $(cat /etc/opendkim/key1.txt) | sed 's/" "//g;s/[\(\)]//g;s/ *; --.*//'

Идём в панель управления DNS и добавляем TXT-запись key1._domainkey со значением, которое указано в кавычках (кавычки вносить не нужно).

Если всё сделано правильно, то через некоторое время запись можно получить командой:

host -t txt key1._domainkey.$(echo example.org | my_mail_domain)

Результат должен быть примерно такой:

key1._domainkey.ВАШ_ПОЧТОВЫЙ_ДОМЕН descriptive text "v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCg
KCAQEA6gsfUaFIqMgCvqiwNKQ0rlbH0JTOz1od+EuicKg8SmhtaI+W6Ro2OBI38L9slMjTRIOrwCT8FqvZ532Dgn6hyrePuy4PRUzPjx5mgW5mE1PbLOw77
bMK0vEn52Oyw1L/BNb6KpKJagQo+xffAgxCCfdB/ZBDEMZqPKeyo4IdrXNrIF89I9rask5" "wnQTAmJWt0/kWai3JwkkGFlLNEnhduNAqxH9I9ECrXyP2m
GEbTvmbjFMq8grJ9/LF4dZTmUgjmIcpyxR2ao7+edHB4y0+V4752oXL/MMc3dSbhp8+EhFE19TPKE44NZMMRS2Iv3xbNfxqjd+W1ThEZ7nHoFLMOQIDAQAB"

[Наверх]

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

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

Если домен уже привязан к какой-либо почте

Находим в настройках домена TXT-запись, которая начинается с v=spf1 и добавляем в середину информацию о дополнительном сервере, например так:

v=spf1 a:ВАШ_ХОСТ redirect=_spf.yandex.net

Запись a:ВАШ_ХОСТ означает, что в результирующий список разрешённых адресов нужно добавить IP-адреса, которые указаны в A-записях домена ВАШ_ХОСТ. Запись redirect=_spf.yandex.net и другие подобные оставляйте как есть, они нужны для работы других служб.

Если домен не привязан к почте

Добавляем новую TXT-запись к самому домену (иногда нужно указать @ в качестве имени хоста) следующего содержания:

v=spf1 a:ВАШ_ХОСТ -all

Такая запись означает "доверять почте с сервера ВАШ_ХОСТ и не доверять остальным".

После внесения изменений

Выполните следующую команду, чтобы проверить настройки домена:

host -t txt $(echo example.org | my_mail_domain) 8.8.8.8

Если изменения успели попасть на сервер, то результатом будет значение, которое вы внесли, а если нет, придётся ждать. Время ожидания зависит от того, как быстро ДНС-сервер вашего провайдера применяет настройки, и сколько времени живёт запись (TTL). И то и другое можно проверить с помощью команды dig:

$ dig TXT anton-pribora.ru @dns1.yandex.net

; <<>> DiG 9.16.1-Ubuntu <<>> TXT anton-pribora.ru @dns1.yandex.net
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 65139
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;anton-pribora.ru.              IN      TXT

;; ANSWER SECTION:
anton-pribora.ru.       21600   IN      TXT     "v=spf1 redirect=_spf.yandex.net"

;; AUTHORITY SECTION:
anton-pribora.ru.       900     IN      NS      dns2.yandex.net.
anton-pribora.ru.       900     IN      NS      dns1.yandex.net.

;; Query time: 20 msec
;; SERVER: 213.180.204.213#53(213.180.204.213)
;; WHEN: Сб июл 31 14:05:21 +05 2021
;; MSG SIZE  rcvd: 137

Ищите строчку ;; ANSWER SECTION:, под ней будут нужные вам значения: число перед IN, это секунды жизни записи, а строка после TXT — значение. Однако, если вы делегируете домен на Яндекс (как и я), то независимо от того, что показывает dig, изменения могут вступить в силу только спустя несколько дней (старый баг, который они постоянно обещают исправить).

[Наверх]

Тестируем отправку

Проверяем запущенные сервисы:

netstat -lp4n | grep -E '(:25|:10021)'

Должно быть две записи:

tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      11201/master        
tcp        0      0 127.0.0.1:10021         0.0.0.0:*               LISTEN      12893/opendkim

Если что-то не запустилось, смотрите /var/log/syslog на предмет ошибок.

Теперь отправляем тестовое письмо:

echo "This is a test message" | mail -a "From: test@$(echo example.org | my_mail_domain)" -s test ВАШ_ЛИЧНЫЙ_ЯЩИК@gmail.com

И смотрим логи отправки:

tail -n100 /var/log/mail.log

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

Когда письмо придёт (на Google), откройте его исходный текст (Ctrl + U) и проверьте заголовок Authentication-Results, там будет информация о проверках сервера, откуда пришло письмо:

Authentication-Results: mx.google.com;
       dkim=pass ...
       spf=pass ...

[Наверх]

Безопасность

Пока POSTFIX работает на 127.0.0.1, можно особо не переживать, что вас через него взломают или как-то навредят. Но как только вы измените внутренний адрес на внешний, ваш сервер может стать источником рассылки спама и целью для хакерских атак. На мой взгляд, самая безопасная схема работы — когда сервер только отправляет письма, а принимает и обрабатывает их кто-то другой. Протокол SMTP имеет слишком большие возможности для разного рода обмана и вредоносных действий. Вы с этим столкнётесь, когда откроете 25 порт для внешнего мира.

Как только POSTFIX будет настроен и сможет отправлять письма, то заработает и функция mail() в PHP. Это значит, что вы должны быть передельно осторожны с выбором программ, которые работают на сервере. Крайне не рекомендуется настраивать POSTFIX на одном сервере с WordPress. Большинство атак на сайты заключаются в том, чтобы заразить сайт вирусом и рассылать спам. Сам по себе WordPress не является чем-то опасным, угрозу представляют его плагины. Некоторые из них изначально могут содержать вирусы, а некоторые имеют дыры и легко взламываются.

Следите за тем, куда и когда отправляются письма с вашего сервера (на основании /var/log/mail.log). Попасть в чёрные списки спамеров очень легко, а вот восстановить репутацию домена и IP-адреса в разы сложнее.

[Наверх]

Хорошая статья, мне понравилась. Оставлю отзыв!