2016-11-30

О спаме

В электропочте много спама. Со спамом борются. Борются довольно успешно. Так что даже приходится бороться со средствами борьбы со спамом.
Подымаешь какой-нибудь сервер, и этот бедный сервер не может отослать письма так, чтобы они не попали у получателя в спам. Потому что как минимум пара довольно интересных технологий борьбы со спамом нашли таки широкое применение. А значит, надо их учитывать и применять на своём сервере.
Смысл обеих технологий сводится к проверке сервера, который отправляет почту. А действительно ли этот сервер имеет право отправлять письма от данного почтового домена?
Postbox
Технология номер раз. SPF — Sender Policy Framework. Проверяется IP адрес сервера, отправляющего почту. Правила проверки задаются в DNS домена, в виде TXT записи, рядом с MX записью. Если вы не знали, то знайте, в DNS можно хранить произвольные текстовые данные. Впрочем, весьма ограниченной длины, ибо должны влезать в один UDP пакет.
Синтаксис SPF записи весьма прост. Сначала идёт префикс, указывающий, что это именно SPF запись, именно версии один.
v=spf1
Далее идут механизмы проверки, разделённые пробелами. Порядок определения механизмов важен, ибо проверка адреса сервера происходит именно в этом порядке.
Можно указать IPv4 или IPv6 адрес, или сеть в CIDR нотации.
v=spf1 +ipv4:188.232.141.211 +ipv6:2a02:2698:5424:f600::1
Плюсик в начале означает "pass", т.е. серверу с данным IP разрешено отправлять письма с этого домена. Плюсик подразумевается по умолчанию, поэтому повсеместно опускается.
Можно сказать, что все MX сервера, т.е. перечисленные в MX записях данного домена, могут отправлять почту данного домена.
v=spf1 mx
Можно сказать, что все A записи данного домена, т.е. IP адреса, в которые данное доменное имя резолвится, могут отправлять почту данного домена.
v=spf1 a
Можно сказать, что мы тут, в этом домене, не особо знаем, какие сервера будут отправлять нашу почту, а потому, мол, спроси SPF запись того домена. Это особенно актуально, если ваша почта хостится где-нибудь, например, у Гугла. И отправляется, соответственно, хоть и от вашего домена, но серверами Гугла.
v=spf1 include:_spf.google.com
Последним, но очень важным механизмом должен стоять all. Если все остальные правила не сработали, надо принять какое-то решение. -all (с минусом) означает, что все письма с вашим доменом отправителя, отправленные с серверов, не разрешённых предыдущими правилами, должны быть отвергнуты. Это означает, что письмо не будет принято и не дойдёт до получателя. Обычно так не делают, ибо, если вдруг случайно айпишник сервера сменится, письма перестанут доходить, а этого никто не хочет. Поэтому ставят ~all (с тильдой), что означает "soft fail", т.е. вроде низя, но мало ли что. Такие письма как раз и попадают в спам.
Итоговая TXT запись для вашего домена может выглядеть так:
example.com.    300 IN  TXT "v=spf1 include:_spf.google.com ~all"
Из-за того, что в конце ставят не сильно категоричный ~all или даже совсем неуверенный ?all, а также потому, что SPF запись всё ещё отсутствует у многих доменов, SPF не так сильно защищает от спама, как могла бы.
Кстати, чтобы не мучаться с этими механизами и всем прочим, есть онлайн конструкторы SPF записей.
SPF
Технология номер два. DKIM — DomainKeys Identified Mail. Привлекаем криптографию. Сервер перед отправкой подписывает письмо своим приватным ключом (это ассиметричное шифрование). Сервер получателя (или даже почтовый клиент получателя) проверяют правильность подписи публичным ключом. Сам публичный ключ хранится тоже в TXT записи DNS.
Серверов, отправляющих почту, может быть много. Чтобы не копировать один и тот же приватный ключ на все сервера, в DKIM поступили хитрее. С одним доменом может быть связано несколько пар ключей. То, какой ключ использовать, определяется селектором (selector).
Итак, чтобы нам начать подписывать письма на нашем сервере, отправляющем почту, нужно сгенерировать пару ключей. Это просто сделать с помощью OpenSSL.
openssl genrsa -out domainkey.pem 1024 -outform PEM
Ключи длиннее 2048 бит в DKIM не используются, опять-таки из-за ограничений на размер пакета DNS ответа. 1024 бит вполне достаточно.
OpenSSL создаёт pem файл, начинающийся с -----BEGIN RSA PRIVATE KEY-----. На самом деле в этом файле находится пара ключей. Нам нужно вытащить публичный ключ.
openssl rsa -in domainkey.pem -out domainkey-public.pem -pubout -outform PEM
Теперь у нас есть файл, начинающийся с -----BEGIN PUBLIC KEY-----. Вот его содержимое, с этого бегина и до -----END PUBLIC KEY-----, без переводов строки, и надо поместить в TXT запись. Только не в TXT запись самого домена, а поддомена селектор._domainkey. Как-то так:
mail._domainkey.example.com. 300 IN TXT \
"k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD0GCsik63lng6q171lmHx869xrYL9qExYM3YQuYpfEXoDnoatOsDUh8dNKnEzGr+jXxU8LOy/6haR32Is3gT9sKIYQ0QWFzG0wdu7moXfAmJcAdoElj6H2DV7Xkk6oFNz0W/8RB6Jw8nQnITTdRkN+ZQi0AjaDFIxXOeM749RspQIDAQAB;"
Ещё раз, селекторов может быть много, ключей тоже. Поэтому поддоменов _domainkey тоже может быть много. Иногда в начало TXT записи добавляют v=DKIM1;.
Конструкторы тоже имеются. Но я бы поостерёгся тех, которые сами генерируют ключи. Таки приватный ключ лучше сгенерировать самому, и никому его не давать.
С публичным ключом разобрались. Нужно теперь сделать так, чтобы письма подписывались приватным ключом. Для этого есть OpenDKIM. В Debian и Ubuntu он живёт в одноимённом пакете. Это — milter, т.е. почтовый фильтр, который можно подоткнуть как к Sendmail, так и к Postfix.
OpenDKIM может как подписывать отправляемые почтовые сообщения, так и проверять подпись в принимаемых сообщениях. В простейшем случае, когда нужно только подписывать письма и только с одного домена, его конфигурационный файл /etc/opendkim.conf может быть таким:
# стартовать как демон, писать в сислог и т.п.
AutoRestart             Yes
AutoRestartRate         10/1h
UMask                   002
Syslog                  yes
SyslogSuccess           yes
LogWhy                  yes

# как канонизировать (т.е. нормализовать) заголовки и тело письма
# relaxed для заголовков означает привести их к нижнему регистру
# убрать лишние пробелы и всё такое
# simple для тела письма означает только игнорировать
# последнюю пустую строку в конце письма
Canonicalization        relaxed/simple

# домен, который подписываем
Domain                  example.com
# селектор, которым подписываем
Selector                mail
# приватный ключ, которым подписываем
KeyFile                 /etc/opendkim/domainkey.pem

# режим работы, только подписываем
Mode                    s

# где пид файл хранить
PidFile                 /var/run/opendkim/opendkim.pid

# каким алгоритмом подписывать
SignatureAlgorithm      rsa-sha256

# под каким пользователем запускать демона
UserID                  opendkim:opendkim

# где демон будет слушать
Socket                  inet:12301@127.0.0.1
Теперь Postfix может подключить этот milter. В /etc/postfix/main.cf нужно добавить несколько строк.
milter_protocol = 2
milter_default_action = accept
smtpd_milters = inet:127.0.0.1:12301
non_smtpd_milters = inet:127.0.0.1:12301
OpenDKIM может заниматься подписью и нескольких доменов. Тогда ему нужно сказать, в каких случаях какими ключами пользоваться. Это настраивается несколько сложнее, но принципы не меняются.
В результате работы OpenDKIM в письме появляется ещё один заголовок.
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=example.com; s=mail;
 t=1479817077; bh=pr3OwqtRJV10SOrRVobgyVcUHIepUu4Dr5yPJGUpD7s=;
 h=Date:To:From:Subject:From;
 b=G8MGkpMDnusbtuL29KSW6z1maK/M2HcvEP+rRSvVifsFfdgXTcPv7yHVEZfjw8z5K
 C6IF7/BDtEifsRb9H1Saauu1IsBoCO5NbRjciGxgWsAK6GGqW+RL1TxJpDlODY8z4g
 LCDe+zGxqD4MmnudDvuImPVZzFt+0dGIdMQ3P8Sw=
Тут у нас присутствет куча настроек OpenDKIM. Алгоритм подписи "a=". Канонизация "c=". Домен "d=". Селектор "s=", именно так принимающая сторона понимает, каким ключом проверять. Заголовки "h=", которые использовались при формировании подписи. Заметьте, далеко не все заголовки включены в подпись.
DKIM
Как проверить, что всё это работает?
Можно отправить письмо себе на Gmail ящик. Гугловый почтовый сервер добавит к письму заголовок (чтобы его увидеть, надо посмотреть исходный текст письма).
Authentication-Results: mx.google.com; dkim=pass header.i=@example.com; spf=pass
 (google.com: domain of sender@example.com designates 188.232.141.211 as permitted
 sender) smtp.mailfrom=sender@example.com
Соответственно, "dkim=pass" и "spf=pass" означают, что соответствующие проверки прошли успешно.
Кстати, Gmail добавляет DKIM подпись для писем, пересылаемых через него с полной авторизацией.
Можно отправить письмо на адрес check-auth@verifier.port25.com. В ответ придёт подробнейший отчёт о проведённых проверках и их результатах.
Подобный же отчёт, но в вебе, с красивыми картинками, можно получить, если зайти на mail-tester.com и послать письмо на указанный адрес. Осторожно, после третьего письма за сутки начнут просить денег.
Sunny mail
Есть ещё третья технология. DMARC — Domain-based Message Authentication, Reporting and Conformance. В некотором смысле это объединение SPF и DKIM. Вы можете добавить ещё одну TXT запись к вашему домену. Точнее к поддомену _dmarc.
_dmarc.example.com. 300 IN  TXT \
"v=DMARC1;p=reject;pct=100;rua=mailto:dmarcreports@example.com"
Этим вы скажете получателям писем от вашего домена, чтобы они обязательно проверяли письма посредством SPF и DKIM. Если письма не проходят проверку, их надо отбрасывать ("p=reject"), причём отбрасывать 100% писем ("pct=100"). Отбрасывать не просто так, а некоторую агрегированную статистику слать на почту dmarcreports@example.com. Да, таким образом владелец домена может получать информацию о том, как много спама ходит от его имени.
DMARC
Есть, конечно, куча других мелочей, влияющих на спамовость письма. Тело письма, если уж содержит HTML разметку MIME типа "text/html", должно ещё содержать и альтернативное представление в виде "text/plain". По крайней мере, если оно не так, это не нравится SpamAssassin. Ваш почтовый сервер должен представляться (указывать в SMTP команде HELO/EHLO) правильным доменным именем, в которое должен обратно резолвится его собственный IP адрес. Это тоже проверяется.
В общем, сейчас можно сделать так, чтобы правильные письма, отправленные вашим правильным почтовым сервером, разительно отличались от писем всяких негодяев, пишуших якобы от вашего имени/домена. И этими способами уже совсем-совсем нельзя пренебрегать.
З.Ы. Красивые картинки взяты с блога компании Postmark.