Сервер для отправки писем через SMTP с авторизацией (debian, postfix, sasl, sasldb)

Предполагается, что начальная установка и настройка POSTFIX была сделана по статье «Сервер для отправки писем с сайта (debian, sendmail, postfix, SPF, DKIM)».

  1. Включаем SMTP
  2. Подключаем SASL с авторизацией через sasldb
  3. Включаем SMTPS (защищённый SMTP на 465 порту)
  4. Проверяем работу SMTPS
  5. Подключаем fail2ban

Включаем SMTP

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

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

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

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

Разрешаем POSTFIX'у работать на всех адресах вашего сервера:

sed -i 's/^inet_interfaces.*/inet_interfaces = all/' /etc/postfix/main.cf

Чтобы ваш сервер не стал лакомым кусочком для спамеров, добавляем ограничения на пересылку сообщений:

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

# Режем спам и неавторизованных пользователей
smtpd_sender_restrictions = permit_sasl_authenticated reject_unknown_sender_domain reject_unknown_reverse_client_hostname reject_unknown_client_hostname
smtpd_recipient_restrictions = permit_sasl_authenticated permit_mynetworks
smtpd_relay_restrictions = permit_sasl_authenticated reject_unauth_destination
EOF

Настройка TLS

Крайне рекомендуется использовать шифрование при передаче данных. Самый простой способ — использовать бесплатные сертификаты Let's Encrypt.

Если у вас ещё нет сертификата, то получить его можно очень просто:

# Устанавливаем certbot для NGINX'а
apt install python3-certbot-nginx

# Добавляем свой домен
sed "s/server_name.*;/server_name $(echo smtp.example.com | my_smtp_host);/;s/ default_server//" \
    /etc/nginx/sites-enabled/default \
    | grep -v '^#' > /etc/nginx/sites-enabled/my_domain.conf

# Запускаем мастер получения сертификата
certbot

Добавляем конфиг для TLS:

(cat | my_smtp_host >> /etc/postfix/main.cf) <<EOF

# Добавляем поддержку TLS
smtpd_tls_key_file = /etc/letsencrypt/live/smtp.example.com/privkey.pem
smtpd_tls_cert_file = /etc/letsencrypt/live/smtp.example.com/fullchain.pem
smtpd_tls_security_level = may
smtpd_tls_auth_only = yes

smtp_use_tls = yes
tls_high_cipherlist = ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK
tls_ssl_options = NO_RENEGOTIATION
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtp_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
EOF

И ещё --deploy-hook "service postfix restart" в файл /etc/cron.d/certbot, чтобы получилось примерно так:

0 */12 * * * root test -x /usr/bin/certbot -a \! -d /run/systemd/system && perl -e 'sleep int(rand(43200))' && certbot -q renew --deploy-hook "service postfix restart"

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

service postfix restart

Проверяем поддержку TLS:

openssl s_client -starttls smtp -connect localhost:25 | openssl x509 -noout -dates 2>/dev/null | grep notAfter | cut -d'=' -f2

Результат должен быть следующим:

depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = R3
verify return:1
depth=0 CN = ВАШ_ДОМЕН
verify return:1
250 CHUNKING
Apr 25 11:03:31 2021 GMT

[Наверх]

Подключаем SASL с авторизацией через sasldb

SASL — это программный интерфейс, который позволяет в упрощённом режиме идентифицировать пользователей по логину и паролю.

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

apt install sasl2-bin

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

cat > /etc/postfix/sasl/smtpd.conf <<EOF
pwcheck_method: auxprop
auxprop_plugin: sasldb
mech_list: plain login
EOF

Добавляем пользователя в sasldb:

saslpasswd2 -c -u ДОМЕН ПОЛЬЗОВАТЕЛЬ

Утилита запросит у вас пароль, после чего добавит пользователя в базу. ДОМЕН может быть произвольным, необязательно, чтобы он совпадал с почтовым доменом.

Включаем авторизацию SASL в POSTFIX:

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

# Авторизация SASL
smtpd_sasl_local_domain = \$myhostname
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
EOF
sed -i 's~etc/nss_mdns.config"~etc/nss_mdns.config etc/sasldb2"~' /usr/lib/postfix/configure-instance.sh

Так как база sasldb это обычный файл, а SMTPD по умолчанию запускается в chroot, то каждый раз после добавления или изменения пользователя нужно выполнять перезапуск POSTFIX (чтобы нужные файлы скопировались):

service postfix restart

Вместо sasldb можно использовать PAM-аутентификацию, но это сложнее и по умолчанию работает с системными пользователями, что не очень удобно.

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

Закрываем доступ к папке POSTFIX'а, чтобы не было соблазна украсть базу пользователей sasldb:

chown root:postfix /var/spool/postfix/
chmod 0750 /var/spool/postfix/

Включаем SMTPS (защищённый SMTP на 465 порту)

Включаем конфиг SMTPS (который уже есть в master.cf, просто закомментирован):

sed -i -E '/#smtps/,/#\S/{s/#(smtps| )/\1/}' /etc/postfix/master.cf

Выключаем SMTP (25 порт), чтобы не принимать запросы от спамеров и других нехороших личностей:

sed -i -E 's/^smtp .*smtpd/#\0/' /etc/postfix/master.cf

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

service postfix restart

Проверяем работу SMTPS

Самый простой способ проверить, это отправить письмо и отслеживать логи:

$ tail /var/log/mail.log -f

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

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

cat > test.eml <<EOF
From: "test" <test@ВАШ_ПОЧТОВЫЙ_ДОМЕН>
To: "test" <test@example.com>
Subject: Test SMTPS

This is just a test mail
EOF

Отправляем с помощью curl:

curl -qv --ssl-reqd \
    --url 'smtps://ВАШ_ХОСТ:465' \
    --user 'ПОЛЬЗОВАТЕЛЬ@ДОМЕН:ПАРОЛЬ' \
    --mail-from 'ВАШ_ЯЩИК@ВАШ_ПОЧТОВЫЙ_ДОМЕН' \
    --mail-rcpt 'test@example.com' \
    --upload-file test.eml

Адрес получателя test@example.com указан только потому, что на домен example.com письма не уходят, но попадают в логи. Проверять правильность работы сервера нужно по логу:

Через PHP

Для тестирования я пользовался формой из ApMailer, более подробно описано в статье Отправка писем на PHP через SMTP.

Смотрим логи

Пример работы без ошибок:

Aug  5 15:37:16 aws postfix/smtps/smtpd[2755]: 9D14A1F94: client=unknown[XX.XX.XX.XX], sasl_method=PLAIN, sasl_username=ПОЛЬЗОВАТЕЛЬ@ДОМЕН
Aug  5 15:37:16 aws postfix/cleanup[2757]: 9D14A1F94: message-id=<20210805153716.9D14A1F94@ВАШ_ХОСТ>
Aug  5 15:37:16 aws opendkim[12893]: 9D14A1F94: DKIM-Signature field added (s=key1, d=ВАШ_ПОЧТОВЫЙ_ДОМЕН)
Aug  5 15:37:16 aws postfix/qmgr[2715]: 9D14A1F94: from=<ВАШ_ЯЩИК@ВАШ_ПОЧТОВЫЙ_ДОМЕН>, size=5562, nrcpt=1 (queue active)
Aug  5 15:37:16 aws postfix/smtp[2749]: 9D14A1F94: to=<test@example.com>, relay=none, delay=0.23, delays=0.22/0/0/0, dsn=5.1.0, status=bounced (Domain example.com does not accept mail (nullMX))
Aug  5 15:37:16 aws postfix/cleanup[2757]: C77972033: message-id=<20210805153716.C77972033@ВАШ_ХОСТ>
Aug  5 15:37:16 aws postfix/qmgr[2715]: C77972033: from=<>, size=8060, nrcpt=1 (queue active)
Aug  5 15:37:16 aws postfix/bounce[2758]: 9D14A1F94: sender non-delivery notification: C77972033
Aug  5 15:37:16 aws postfix/qmgr[2715]: 9D14A1F94: removed

Прежде всего интересует строчка, где есть sasl_method=PLAIN, sasl_username=ПОЛЬЗОВАТЕЛЬ@ДОМЕН, это значит, что аутентификация имела место быть по логину и паролю, который мы создали. Дальше надо искать DKIM-Signature field added, если есть, то цифровая подпись была добавлена. И последнее, что нас интересует, — status=bounced (Domain example.com does not accept mail (nullMX)), значит была попытка отправить письмо, но проблема в удалённом сервере.

А вот так выглядит ошибка авторизации, если логин или пароль неверные:

warning: SASL authentication failure: Password verification failed

Если же ошибок нет, попробуйте отправить письмо себе на Google или Яндекс (лучше это сделать через PHP).

Подключаем fail2ban

Чтобы пресечь любителей подбирать пароли, ставим fail2ban:

apt install fail2ban

Включаем конфиг для SASL:

cat >> /etc/fail2ban/jail.local <<EOF 
[postfix-sasl]
enabled  = true
port     = smtp,465
filter   = postfix[mode=auth]
logpath  = /var/log/mail.log
findtime = 60
bantime  = 86400
maxretry = 3

EOF

Перезапускаем сервис:

service fail2ban restart

Проверить работу фильтров можно с помощью команд:

fail2ban-client status
fail2ban-client status postfix-sasl

[Наверх]

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