Postfix SPF DKIM DMARC

Postfix_SPF_DKIM_DMARCДля того, чтобы снизить риск попадания исходящей почты в спам, используется популярная связка SPF, DKIM и DMARC. Данные технологии направлены на борьбу с поддельными почтовыми сообщениями. Рассмотрим практическую реализацию этих технологий на почтовом сервере, сконфигурированном в статье Postfix Dovecot LDAP.
Для лучшего понимания, обратимся к примеру отправки почтового сообщения через утилиту telnet.

message_structure

Все, что находится до команды DATA, является конвертом сообщения. С этой информацией работает SPF. То, что находится после команды DATA, является заголовком сообщения, с этой информацией работает DKIM. DMARC является результирующей политикой, определяющей дальнейшую судьбу сообщения, опираясь на результаты SPF и DKIM проверок. Результаты SPF, DKIM и DMARC проверок записываются в заголовок сообщения, и в дальнейшем могут быть использованы антиспам фильтрами.

Установка и настройка postfix-policyd-spf-python

Для того, чтобы Postfix мог проводить SPF проверки для входящей почты нужно установить пакет postfix-policyd-spf-python

# apt install postfix-policyd-spf-python

В конец файла master.cf нужно добавить следующие строки

# nano /etc/postfix/master.cf
. . .
policyd-spf unix -      n       n       -       0       spawn
  user=policyd-spf argv=/usr/bin/policyd-spf

Добавим поддержку postfix-policyd-spf-python в Postfix

# nano /etc/postfix/main.cf
. . .
smtpd_relay_restrictions =
 permit_mynetworks
 permit_sasl_authenticated
 defer_unauth_destination
 check_policy_service unix:private/policyd-spf
. . .
policyd-spf_time_limit = 3600

Здесь важна последовательность. Параметр check_policy_service unix:private/policyd-spf активирует SPF проверку в Postfix. Он должен быть последним в списке ограничении smtpd_relay_restrictions. Параметр policyd-spf_time_limit = 3600 может находится в любом месте, он определяет временной предел проверки сообщения.

Откроем конфигурационный файл postfix-policyd-spf-python и добавим в конец это файла следующие строки

# nano /etc/postfix-policyd-spf-python/policyd-spf.conf
. . .
Header_Type = AR
Authserv_Id = mail.4skill.ru

Параметр Header_Type указывает метод документирования результатов SPF проверки в проверяемых сообщениях. По умолчанию используется метод SPF. Смена метода SPF на AR используется для совместимости с OpenDMARC. Параметр Authserv_Id определяет имя идентификатора, которое используется при создании поля результатов проверки в заголовке сообщения. Обычно в качестве идентификатора указывается полное доменное имя сервера, на котором производится проверка. Более подробное описание всех параметров можно найти на странице Ubuntu manuals. Детальное описание настройки SPF изложено в статье Postfix настройка SPF.

Перезапустим Postfix

# postfix reload

Теперь Postfix будет производить SPF проверку для всех входящих писем. Осталось добавить SPF запись для домена 4skill.ru. Перейдем в консоль своего DNS провайдера и добавим туда запись типа TXT для домена 4skill.ru.

@ TXT v=spf1 mx ~all

 
add_spf_4skill

В начале указывается версия SPF записи (v=spf1), затем разрешения. В данном случае осуществлять отправку почты от имени домена 4skill.ru могут хосты, указанные в MX записи для данного домена (mx). Почта от иных хостов будет принята с пометкой о неудачной проверке(∼all). Более подробное описание синтаксиса и примеры SPF записей можно найти на странице SPF Record Syntax, или в начале статьи Postfix настройка SPF.

Установка и настройка OpenDKIM

Для того, чтобы Postfix мог проводить DKIM проверки для входящей почты, нужно установить пакеты OpenDKIM и OpenDKIM-Tools. Последний нужен для генерации ключей.

# apt install opendkim opendkim-tools

После установки данных пакетов, заменим оригинальный конфигурационный файл OpenDKIM на следующий

# nano /etc/opendkim.conf

AutoRestart             yes
AutoRestartRate         10/1h

Canonicalization        relaxed/relaxed

ExternalIgnoreList      refile:/etc/opendkim/trusted_hosts
InternalHosts           refile:/etc/opendkim/trusted_hosts
KeyTable                refile:/etc/opendkim/key_table
SigningTable            refile:/etc/opendkim/signing_table

On-BadSignature         accept

ReportAddress           noreply-dkim@4skill.ru
SendReports             yes

SignHeaders             From,To,Subject,Date,Message-ID

Syslog                  yes
SyslogSuccess           yes
#LogWhy                 yes

UMask                   002
  • AutoRestart — автоматический перезапуск сервиса в случае падения.
  • AutoRestartRate — допустимая частота падений сервиса. В данном случае не более 10 раз за 1 час.
  • Canonicalization — канонизация для заголовка/тела сообщения. Подробнее об это тут.
  • ExternalIgnoreList — список внешних хостов, которые могут производить пересылку почты от имени подписываемого домена без аутентификации.
  • InternalHosts — список внутренних хостов, чья исходящая почта не должна проверяться, но должна быть подписана.
  • KeyTable — список ключей, используемых для подписи.
  • SigningTable — список доменов, сопряженных с ключами из KeyTable.
  • On-BadSignature — что делать с письмом в случае неудачной проверки. Возможны варианты:
    ∘ accept — пропустить(по умолчанию);
    ∘ discard — отбросить сообщение без уведомления;
    ∘ quarantine — поместить сообщение в спам;
    ∘ reject — отбросить сообщение;
    ∘ tempfail — выдать сообщение об ошибке.
  • ReportAddress — адрес, от имени которого будет происходить рассылка отчетов о неудачных проверках.
  • SendReports — включение рассылки отчетов о неудачных проверках.
  • SignHeaders — набор полей заголовка сообщения, которые будут включены в подпись.
  • Syslog — запись логов в syslog.
  • SyslogSuccess — включение в лог информации об успешных подписях и проверках.
  • LogWhy — включение более детального логирования.
  • UMask — маска прав для Socet файла.

Подробное описание всех параметров можно найти на странице opendkim.conf.

Создадим каталок для хранения ключей

# mkdir -p /etc/opendkim/keys/4skill.ru

Добавим ключи для домена 4skill.ru

# opendkim-genkey -D /etc/opendkim/keys/4skill.ru -d 4skill.ru -s mail

Ключ -D определяет каталог для записи ключей. Ключ -d указывает название домена. Ключ -s указывает название селектора. Селектор может быть произвольным. Обычно селектор отражает название сервиса, дату создания ключа, или битность ключа. В данном случае это название сервиса.

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

# chown opendkim /etc/opendkim/keys/4skill.ru/*.private

Открытый ключ находится в файле с расширением .txt

# cat /etc/opendkim/keys/4skill.ru/*.txt

mail._domainkey IN TXT ( "v=DKIM1; k=rsa; "
"p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMtyLiYLXF0ywByDzfnQmdI+OMBsr6dg0OfXnejQtKwwCKmxLdxdk7dXXca7ZaO10b35deSX2XQuCuKp0ZeVsRAQsEu4dAYrJ/5Km7Rh3V45uLv1w7ksky128u6oiqllL5iVg+MWWQYtbedO7hT/Rod+aXb7DV3NjNAIl53ZdPtQIDAQAB" ) ; ----- DKIM key mail for 4skill.ru

Добавим его на DNS сервер своего провайдера в виде TXT записи

mail._domainkey.4skill.ru TXT v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMtyLiYLXF0ywByDzfnQmdI+OMBsr6dg0OfXnejQtKwwCKmxLdxdk7dXXca7ZaO10b35deSX2XQuCuKp0ZeVsRAQsEu4dAYrJ/5Km7Rh3V45uLv1w7ksky128u6oiqllL5iVg+MWWQYtbedO7hT/Rod+aXb7DV3NjNAIl53ZdPtQIDAQAB

 
add_dkim_record_4skill

Создадим файл, определяющий от кого подписывать почту

# nano /etc/opendkim/trusted_hosts 

::1
127.0.0.1

Даже если явно не указывать ::1 или 127.0.0.1, почта сформированная почтовыми клиентами пользователей, прошедших аутентификацию на сервере будет подписана. Явное указание адреса петлевого интерфейса дает возможность подписи почты, сформированной утилитой telnet на почтовом сервере(# telnet localhost 25). На практике в данный файл записываются адреса серверов, использующих Postfix в качестве сервера пересылки(relay), с целью подписи транзитных сообщений.

Создадим файл, определяющий список закрытых ключей

# nano /etc/opendkim/key_table

mail._domainkey.4skill.ru      4skill.ru:mail:/etc/opendkim/keys/4skill.ru/mail.private

Каждый ключ прописывается отдельной строкой и имеет формат

<название ключа>      <название домена>:<название селектора>:<путь к закрытому ключу>

Создадим файл, сопоставляющий закрытый ключ с именем домена

# nano /etc/opendkim/signing_table

*@4skill.ru      mail._domainkey.4skill.ru

Формат записи следующий

<название домена>      <название ключа из key_table>

Укажем SOCKET для подключения к OpenDKIM

# nano /etc/default/opendkim

Закоментируем строку

#SOCKET="local:/var/run/opendkim/opendkim.sock"

Раскоментируем строку

SOCKET="inet:12345@localhost"

Укажем Postfix как подключаться к OpenDKIM. Для этого добавим к конец main.cf следующие строки

# nano /etc/postfix/main.cf
. . .
smtpd_milters = inet:localhost:12345
non_smtpd_milters = $smtpd_milters

Перезапустим OpenDKIM

# service opendkim restart

Перезапустим Postfix

# postfix reload

Более детальная настройка OpenDKIM описана в статье Postfix OpenDKIM.

Установка и настройка OpenDMARC

Для того, чтобы Postfix мог проводить DMARC проверки для входящей почты, нужно установить пакет OpenDMARC

# apt install opendmarc

Заменим оригинальный конфигурационный файл OpenDMARC на следующий

# nano /etc/opendmarc.conf

AuthservID                      mail.4skill.ru

AutoRestart                     yes
AutoRestartRate                 10/1h

FailureReports                  yes
FailureReportsSentBy            noreply-dmarc@4skill.ru

HistoryFile                     /var/run/opendmarc/opendmarc.dat
IgnoreHosts                     /etc/opendkim/trusted_hosts
IgnoreAuthenticatedClients      yes

RejectFailures                  no
Syslog                          yes
UMask                           002
  • Authserv_Id — имя идентификатора, которое используется при создании поля результатов проверки в заголовке сообщения. Обычно в качестве идентификатора указывается полное доменное имя сервера, на котором производится проверка.
  • AutoRestart — автоматический перезапуск сервиса в случае падения.
  • AutoRestartRate — допустимая частота падений сервиса. В данном случае не более 10 раз за 1 час.
  • FailureReports — включение рассылки отчетов о неудачных проверках.
  • FailureReportsSentBy — адрес, от имени которого будет происходить рассылка отчетов о неудачных проверках.
  • HistoryFile — расположение файла, в который записывается информация, используемая для создания сводных отчетов.
  • IgnoreHosts — расположение файла, содержащего список имен хостов, IP адресов и CIDR выражений, которые нужно исключить из проверки.
  • IgnoreAuthenticatedClients — исключение из проверки аутентифицированных клиентов.
  • RejectFailures — действие в случае неудачной проверки. Возможные варианты:
    ∘ yes — отклонить письмо;
    ∘ no — пропустить письмо и добавить в заголовок сообщения поле с результатами проверки (поведение по умолчанию).
  • Syslog — запись логов в syslog.
  • UMask — маска прав для Socet файла.

Подробное описание всех параметров можно найти на странице opendmarc.conf.

Укажем SOCKET для подключения к OpenDMARC, для этого добавим следующую строку в файл /etc/default/opendmarc

# echo 'SOCKET="inet:12346@localhost"' >> /etc/default/opendmarc

Укажем Postfix как подключаться к OpenDMARC. Для этого отредактируем файл /etc/postfix/main.cf

# nano /etc/postfix/main.cf

Заменим строку (сочетание клавиш Alt+R)

smtpd_milters = inet:localhost:12345

На

smtpd_milters = inet:localhost:12345 inet:localhost:12346

Здесь важна последовательность. В начале указывается SOCET для подключения к OpenDKIM, затем к OpenDMARC.

Перезапустим службу OpenDMARC

# service opendmarc restart

Перезапустим службу Postfix

# postfix reload

Еще одна полезная функция OpenDMARC это рассылка сводных отчетов. Сводный отчет содержит в себе статистику принятых писем от определенных доменов, с процентным соотношением удачных и неудачных проверок SPF, DKIM и DMARC. Изначально статистика по входящей почте записывается в файл, определенный параметром HistoryFile. Затем скриптом opendmarc-import статистика импортируется в базу данных MySQL. Далее используется скрипт opendmarc-reports, который достает статистику из базу данных MySQL, формирует письмо с прикрепленным отчетом и осуществляет отправку письма. Заключительным этапом является запуск скрипта opendmarc-expire, который очищает базу данных MySQL от устаревших записей.

Установка и настройка MySQL

Для установки MySQL выполним команду

# apt install mysql-server

В процессе установки появится окно с запросом указания пароля для пользователя root. Это не системный пользователь Linux, а одноименный корневой администратор базы данных MySQL. На данном этапе можно ничего не указывать и оставить поле пустым, так как в дальнейшем пароль будет определен конфигурационным скриптом для настройки MySQL

mysql_pass

После установки базы данных MySQL, нужно запустить конфигурационный скрипт mysql_secure_installation

# mysql_secure_installation

Securing the MySQL server deployment.

Connecting to MySQL using a blank password.

VALIDATE PASSWORD PLUGIN can be used to test passwords
and improve security. It checks the strength of password
and allows the users to set only those passwords which are
secure enough. Would you like to setup VALIDATE PASSWORD plugin?

Press y|Y for Yes, any other key for No: n
Please set the password for root here.

New password:

Re-enter new password:
By default, a MySQL installation has an anonymous user,
allowing anyone to log into MySQL without having to have
a user account created for them. This is intended only for
testing, and to make the installation go a bit smoother.
You should remove them before moving into a production
environment.

Remove anonymous users? (Press y|Y for Yes, any other key for No) : y
Success.


Normally, root should only be allowed to connect from
'localhost'. This ensures that someone cannot guess at
the root password from the network.

Disallow root login remotely? (Press y|Y for Yes, any other key for No) : y
Success.

By default, MySQL comes with a database named 'test' that
anyone can access. This is also intended only for testing,
and should be removed before moving into a production
environment.


Remove test database and access to it? (Press y|Y for Yes, any other key for No) : y
 - Dropping test database...
Success.

 - Removing privileges on test database...
Success.

Reloading the privilege tables will ensure that all changes
made so far will take effect immediately.

Reload privilege tables now? (Press y|Y for Yes, any other key for No) : y
Success.

All done!

В начале предлагается использовать плагин проверки сложности пароля. Далее указывается пароль для пользователя root базы данных MySQL. Затем предлагается удалить анонимного пользователя. Потом предлагается выключить возможность удаленного подключения пользователя root к базе данных MySQL. После чего предлагается удалить тестовую базу. В заключении предлагается обновить таблицы прав пользователей.

Далее нужно отключить режимы STRICT_TRANS_TABLES, NO_ZERO_IN_DATE и NO_ZERO_DATE. Если этого не сделать, то при импорте схемы OpenDMARC вылетит ошибка

ERROR 1067 (42000) at line 20: Invalid default value for 'lastsent'

Для отключения выше указанных режимов нужно добавить в файл /etc/mysql/my.cnf следующие строки

# nano /etc/mysql/my.cnf
. . .
[mysqld]
sql-mode = ONLY_FULL_GROUP_BY,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

и перезапустить службу MySQL

# service mysql restart

В файле схемы нужно раскоментировать две последние строки

# nano /usr/share/doc/opendmarc/schema.mysql
. . .
CREATE USER 'opendmarc'@'localhost' IDENTIFIED BY 'changeme';
GRANT ALL ON opendmarc.* to 'opendmarc'@'localhost';

Первая команда создаст пользователя opendmarc с паролем changeme. Вторая команда назначит все доступные привилегии пользователю opendmarc на базу opendmarc.
Теперь можно импортировать схему OpenDMARC в MySQL

# mysql -u root -p < /usr/share/doc/opendmarc/schema.mysql
Enter password:

При выполнении данной команды нужно указать пароль пользователя root базы данных MySQL.

Далее отредактируем скрипт /usr/sbin/opendmarc-reports

# nano /usr/sbin/opendmarc-reports

Заменим строку (сочетание клавиш Alt+R)

$answer = ${${*$smtp}{'net_cmd_resp'}}[1];

На

$answer = ${${*$smtp}{'net_cmd_resp'}}[1] || $smtp->message() || 'unknown error';

Данная замена нужна для предотвращения ошибки #134.

Use of uninitialized value $answer in scalar chomp at /usr/sbin/opendmarc-reports line 938.
Use of uninitialized value $answer in concatenation (.) or string at /usr/sbin/opendmarc-reports line 939.

Далее нужно создать связующий скрипт

# nano /etc/cron.daily/opendmarc-report
 
#!/bin/bash

DB_SERVER='localhost'                   # Адрес MySQL сервера
DB_USER='opendmarc'                     # Пользователь для подключения к базе данных
DB_PASS='changeme'                      # Пароль пользователя для подключения к базе данных
DB_NAME='opendmarc'                     # Имя базы данных
WORK_DIR='/var/run/opendmarc'           # Директория, в которую записывается HistoryFile
REPORT_EMAIL='noreply-dmarc@4skill.ru'  # Адрес, от имени которого происходит рассылка сводных отчетов
REPORT_ORG='4skill.ru'                  # Название домена, от имени которого происходит рассылка сводных отчетов

mv ${WORK_DIR}/opendmarc.dat ${WORK_DIR}/opendmarc_import.dat -f
touch ${WORK_DIR}/opendmarc.dat
chown opendmarc:opendmarc ${WORK_DIR}/opendmarc.dat

/usr/sbin/opendmarc-import --dbhost=${DB_SERVER} --dbuser=${DB_USER} --dbpasswd=${DB_PASS} --dbname=${DB_NAME} --verbose < ${WORK_DIR}/opendmarc_import.dat
/usr/sbin/opendmarc-reports --dbhost=${DB_SERVER} --dbuser=${DB_USER} --dbpasswd=${DB_PASS} --dbname=${DB_NAME} --verbose --interval=86400 --report-email=${REPORT_EMAIL} --report-org=${REPORT_ORG}
/usr/sbin/opendmarc-expire --dbhost=${DB_SERVER} --dbuser=${DB_USER} --dbpasswd=${DB_PASS} --dbname=${DB_NAME} --verbose

Сделаем скрипт исполняемым

# chmod +x /etc/cron.daily/opendmarc-report

Идея скрипта взята с сайта njae.me.uk. Скрипт будет выполняться раз в сутки.

Заключительным этапом будет добавление DMARC записи для домена 4skill.ru. Для этого в DNS оснастке своего провайдера нужно добавить TXT запись следующего вида

_dmarc.4skill.ru TXT v=DMARC1; p=none; rua=mailto:dmarc@4skill.ru; ruf=mailto:dmarc@4skill.ru

 
add_dmarc_record_4skillru

  • v - версия записи.
  • p - политика, может иметь значения:
    ∘ none - не принимать никаких действий;
    ∘ quarantine - отправить сообщение в спам;
    ∘ reject - не принимать сообщение.
  • rua - адрес для получения сводных отчетов.
  • ruf - адрес для получения отчетов о неудачных проверках.

Описание всех доступных параметров DMARC записи и примеры можно найти на странице help.mail.ru.

В первое время лучше использоваться политику none, чтобы исключить потерю легитимной исходящей почты и изучить сводные отчеты DMARC, полученные от других почтовых систем. Далее можно изменить политику на более агрессивную, либо оставить none, в случае использования антиспам фильтров. Как правило современные антиспам системы используют SPF, DKIM и DMARC заголовки в своих проверках, что обеспечивает высокую вероятность попадания поддельной почты в спам. Сводный отчет представляет из себя файл с расширением .xml. Для его просмотра можно использовать XML to Human Converter.

Чтобы убедиться что все вышло, и исходящая почта проходит SPF, DKIM и DMARC проверки, можно отправить тестовое письмо на сторонний почтовый сервер, поддерживающий данные типы проверок, и посмотреть на нем оригинал письма. На примере gmail.com это выглядит следующим образом, нужно открыть отправленное письмо, и в правой части экрана нажать на кнопку, изображенную на картинке ниже

gmail_show_orig

Далее из всплывающего меню выбрать "Показать оригинал". В верхней части открывшегося окна буду представлены результаты проверок

test_dmarc_gmail

Также можно воспользоваться онлайн сервисом mail-tester.com.

Postfix SPF DKIM DMARC: 2 комментария

  1. Александр

    вроде все настроил по туториалу, но когда смотрю на mail.log то получаю ошибку что порт 12346 отказал в дотсупе:
    warning: connect to Milter service inet:localhost:12346: Connection refused
    что это вообще значит,milter добвавлен в main.cf после dkim записи, так что должно работать, даже добавил запись сокета в конфиг opendmarc

    1. Андрей Автор записи

      На порту 12346 должен работать OpenDMARK сервис.
      Это указывается в /etc/default/opendmarc

      # grep 12346 /etc/default/opendmarc
      SOCKET="inet:12346@localhost"

      В /etc/postfix/main.cf должно быть

      # grep 12346 /etc/postfix/main.cf
      smtpd_milters = inet:localhost:12345 inet:localhost:12346

      Сервис OpenDMARK должен быть запущен

      # service opendmarc status | grep active
         Active: active (running) since Ср 2018-07-25 14:37:44 MSK; 4min 31s ago

      И слушать указанный выше порт

      # netstat -nat4 | grep 12346
      tcp        0      0 127.0.0.1:12346         0.0.0.0:*               LISTEN

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *