Чтобы письма не попадали в спам. Настройка почтовой инфраструктуры.

Я совсем не специалист по почтовым серверам и их настройке, но внезапно встала задача по организации такового для отправки почты от имени нескольких совершенно разных доменов.
Речь шла исключительно об отправке(без приема) и задача казалась мне совершенно простой. Как наивен я был…
Собственно данная заметка о том как в 2k18-ом нужно заморочиться, что бы письма отправляемые вашим почтовым сервером не отбрасывались на этапе доставки или не попадали в спам являясь при этом совершенно нормальными.

Тут не будет технических подробностей по настройке postfix, только описание организационных моментов и порядка настройки DNS. По большей части это заметка себе на память.

Часть первая — rDNS для почтового сервера

Итак, есть какой-то сервер, ВМ или контейнер в котором скорее всего(если это Linux) бежит postfix который и будет доставлять почту. Так же скорее всего у этого сервера есть доступ в интернет и какой то публичный IP на одном из интерфейсов или назначающийся по средствам NAT 1:1, не важно, предполагается что публичный IP есть.  
Как известно от клиента к серверу или от сервера к серверу почтовые сообщения передаются по протоколу SMTP.
Так вот, при передаче сообщения серверу-получателю, ваш сервер-отправитель сообщает свой hostname или так называемый hello-id. Сервер-получатель зная IP сервера-отправителя первым делом пытается разрешить IP в имя через DNS и сравнить полученное имя с именем(hostname) переданным сервером-отправителем.

В случае с postfix вместо реального hostname можно задать, что то другое но это совершенно ничего не меняет, какое бы имя вы не передавали — для него должны быть сделаны записи типа A и PTR в DNS.

Запись типа A это разрешение имени в IP а PTR это разрешение IP-адреса в доменное имя. На сегодняшний день, для многих почтовых сервисов это является важным требованием прописанным в их правилах, например у mail.ru и outlook.com, у других я не читал).

Пример из служебного заголовка письма в mail.ru когда hostname не совпадает с именем полученным через DNS(здесь и далее IP и доменные имена не настоящие):

Received: from c2-87-27-11-51.elastic.cloud.ru ([87.23.11.51]:54138 helo=smtp.domain.ru)

А вот когда все хорошо:

Received: from smtp.domain.ru ([87.27.11.51]:49916)

Уже на этом этапе, при отсутствии PTR-записи или наличии таковой отличной от hostname почтовый сервис может отказать вам в приеме почты ответив сообщением об ошибке.

В случае с именем сервера smtp.domain.ru «A» запись должна быть сделана в зоне domain.ru на отвечающем за нее DNS-сервере.

Сделать PTR запись для вашего публичного IP может только владелец публичной подсети. У него должны быть DNS-серверы зарегистрированные в RIPE как ответственные за эту подсеть и на них должна быть создана эта запись.

Итак, теперь у вас есть нужные DNS записи(A, PTR) нормальный резолвинг для postfix сервера: smtp.domain.ru -> IP, IP -> smtp.domain.ru.

Важно то, что имя почтового сервера(smtp.domain.ru) может быть совершенно любым и никак не связанным с тем доменом от которого планируется отправлять почту.

Часть вторая — MX-запись для почтового сервера

Предположим, что вы хотите отправлять почту от имени noreply@mystore.ru используя домен mystore.ru.

Этот домен должен принадлежать вам а у регистратора доменных имен должны быть указаны NS-сервары ответственные за данную DNS-зону а у вас соответственно должна быть возможность управлять записями в этой зоне на этих DNS-серверах.

Тут и далее мы пройдемся по всем типам записей которые необходимо настроить для того, что бы почта попадала во «Входящие»

Записи типа «A» для mystore.ru мы не будем трогать совсем, они могут вести на какие то веб-серверы например с интернет магазином или не использоваться вовсе.

Нам нужна запись типа MX(mail exchanger) в которой будет указан сервер ответственный за отправку(и прием, хотя нам это не интересно) почты с домена mystore.ry.

Запись mx.mystore.ru в DNS(BIND) будет выглядеть примерно так:

mystore.ru. IN  MX  10  mx.mystore.ru.

А вот так при запросе через DNS:

$ nslookup -type=MX mystore.ru 8.8.8.8
Server:        8.8.8.8
Address:    8.8.8.8#53

Non-authoritative answer:
mystore.ru    mail exchanger = 10 mx.mystore.ru.

Теперь у вас есть MX запись но оно пока просто пустыщка и никуда не ведет. Далее мы должны связать наш почтовый сервер с этой mx записью путем добавления A записи для mx.mystore.ru в качестве IP у которой будет использоваться публичный IP нашего postfix-сервера.

Должно получиться примерно так:

$ nslookup -type=A mx.mystore.ru 8.8.8.8
Server:        8.8.8.8
Address:    8.8.8.8#53

Non-authoritative answer:
Name:    mx.mystore.ru
Address: 87.27.11.51

Само по себе наличие MX записи не особо нам поможет, по большей части нам это нужно для следующего шага — более интересного) Вы же не думали что это все)?

Часть третья — SPF

У вас есть домен mystore.ru с mx записью указывающей на почтовый сервер smtp.domain.ru с корректно настроенными A и PTR записями. Казалось бы, что еще нужно, и почему этого недостаточно..?
Проблема в том, что при отправке письма, в поле from можно написать, что угодно. Например мы со своего почтового сервера можем слать письма от имени apple.com или же кто то может слать письма от имени нашего домена mystore.ru с паразитным содержимым.

Для того, что бы не позволять использовать чужие домены в поле from придуман SPF (Sender Policy Framework).

SPF — это DNS-запись в которой вы явно указываете с каких серверов может отправляться почта от имени вашего домена. Это крайне важная запись которая должна быть у всех доменов без исключений! Даже если вы не рассылаете почту с вашего домена то SPF необходима для того, что бы не позволить кому то другому использовать имя вашего домена в поле from.

Синтаксис SPF-записи: http://www.openspf.org/SPF_Record_Syntax
Конструктор записи: https://www.dynu.com/NetworkTools/SPFGenerator
Проверка DNS-записи: https://mxtoolbox.com/SuperTool.aspx

Изначально SPF это TXT запись но в какой то момент в DNS появилась запись с одноименным типом SPF а TXT запись стала считаться устаревшей. Но, к сожалению, некоторые провайдеры по прежнему запрашивают запись типа TXT. По этой причине я настоятельно рекомендую завести две записи — TXT, SPF с одним и тем же содержимым.

Примерно вот так:

mystore.ru.  IN TXT "v=spf1 mx a:smtp.domain.ru -all"
mystore.ru.  IN SPF "v=spf1 mx a:smtp.domain.ru -all"

В итоге должно получиться примерно так:

тип SPF:

$ nslookup -type=SPF mystore.ru 8.8.8.8
Server:        8.8.8.8
Address:    8.8.8.8#53

Non-authoritative answer:
mystore.ru    rdata_99 = "v=spf1 mx a:smtp.domain.ru -all"

тип TXT:

$ nslookup -type=TXT mystore.ru 8.8.8.8
Server:        8.8.8.8
Address:    8.8.8.8#53

Non-authoritative answer:
mystore.ru    text = "v=spf1 mx a:smtp.domain.ru -all"

Вот тут на сцену и выходит настроенная ранее mx запись. Здесь мы говорим, что отправка почты от имени mystore.ru возможна только с mx серверов этой зоны + на всякий явно указываем наш почтовый сервер(он же и mx). -all в конце явно запрещает отправку почты с каких то других IP.

Из служебных заголовков письма в mail.ru:

Received-SPF: pass (mx161.mail.ru: domain of mystore.ru designates 87.27.11.51 as permitted sender) client-ip=87.27.11.51; envelope-from=noreply@mystore.ru; helo=smtp.domain.ru;

pass — в данном случае означает что проверка пройдена успешно.

Теперь вашему почтовому серверу разрешено сдать почту от имени mystore.ru а всем остальным запрещено.

Часть четвертая — DKIM

DKIM (DomainKeys Identified Mail) — это еще один механизм проверки отправителя письма который не является обязательным но по факту из-за его отсутствия практически любой почтовый сервис занизит наш рейтинг.
DKIM использует криптографию с открытым ключем для генерации и проверки цифровой подписи.

Принцип следующий:
Генерируется пара ключей(закрытый и открытый) для определенного домена. При отправке письма, почтовый сервер с помощью закрытого ключа подписывает поля «Received», «From» и прочие(это настраивается) и добавляет к письму получившуюся подпись.
В свою очередь открытый ключ публикуется через DNS с помощью специальной TXT записи которую запрашивает сервер-получателя, получает открытый ключ, расшифровывает подпись и проверяет заголовки.

Как не сложно заметить этот метод гораздо сложнее чем SPF. Например в случае с postfix подписью сообщений занимается сторонний сервис — OpenDKIM который нужно отдельно настроить для конкретного домена и подружить с postfix.

Генератор ключей: https://www.port25.com/dkim-wizard/
Проверка DNS-записи: https://mxtoolbox.com/SuperTool.aspx

В DNS, запись DKIM выглядит примерно так:

mail._domainkey IN TXT "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCkO0+T2feUFaRmYLguncmWpjkowBcn8hpoXGRzZXyubOgDEmPgp0gcAnIfpf4l2nDRXp9CJ2p10ZTiQTTGVOldVtcRBngH9Isa1u/eh2dNvH4xrg8N8HQgq5nt1mimf88ma9BcYXVsV5ucrKV0NLad75pXSzGiDvJe4R20Hkrj/QIDAQAB"

Запрашивается следующим образом:

$ nslookup -type=TXT mail._domainkey.mystore.ru 8.8.8.8
Server:        8.8.8.8
Address:    8.8.8.8#53

Non-authoritative answer:
mail._domainkey.mystore.ru    text = "p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC2g+o45Rcn42ysqRG3S/4GMEZvEbqYc3bXUaspJPzm8qKphak6CArNewecaT7XqWkoyy3CtwI2FPZpoqyNpYg4VTk5gb1fwng3ucdsJcBVboKQwwDIyvNUoKVQoLhVSNHHsFiOLVcvQ347mcHSjuUSt9WtKS7Crw7XwJyuKxjaGwIDAQAB"
mail._domainkey.mystore.ru    text = "v=DKIM1\; k=rsa\;"

Часть пятая — DMARC

SPF и DKIM это только механизмы, они ни как не настаивают на том или ином решении почтового сервера. Сервер-получатель сам принимает решение что делать с письмами при том или ином результате проверок.

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

Мастер генерации DMARC записи: https://www.unlocktheinbox.com/dmarcwizard/
Проверка DNS-записи: https://mxtoolbox.com/SuperTool.aspx

В DNS, запись DMARC выглядит примерно так:

_dmarc IN TXT "v=DMARC1; p=quarantine; sp=reject; adkim=s; aspf=s; rf=afrf; pct=100; ri=86400"

У mail.ru она выглядит так:

$ nslookup -type=TXT _dmarc.mail.ru 8.8.8.8
Server:        8.8.8.8
Address:    8.8.8.8#53

Non-authoritative answer:
_dmarc.mail.ru    text = "v=DMARC1\;p=reject\;rua=mailto:d@rua.agari.com,m" "ailto:dmarc_rua@corp.mail.ru\;ruf=mailto:d@ruf.a" "gari.com\;fo=1\;"

В итоге DNS-зона mystore.ru в AWS Route53 выглядит примерно так:

Проверка результата

https://www.mail-tester.com/ — очень крутой сайт. Вам дается сгенерированный email, отправляете на него сообщение и получаете оценку по шкале от 0-10 и узнаете все о своей почтовой инфраструктуре. Без подписки можно провести проверку только три раза в утки, учтите это.

При всех описанных тут настройках у меня 10/10!

В каждом из пунктов довольно много деталей о вашем письме и вашей инфраструктуре:

Отправить тестовое письмо из консоли можно так:

echo "This is the main body of the mail" | mail -S smtp=10.0.2.4 -r {username}@{from} -s "Subject of the Email" {destination_email}

Итог

Самая грустная часть… Можно идеально настроить почтовый сервер но при этом письма от него, даже с пустым содержимым могут попадать в спам. Или иначе, когда даже без всего этого gmail.com или yandex.com кладут письма во входящие. В общем посыл такой — хорошо настроенный сервер это важно но все равно необходимо проверять доставку во все интересующие вас сервисы и возможно писать в поддержку.