Почта на своём домене
Почта на стороне провайдера или свой сервер
Почта — самая трудная часть работы с сайтом и та, которую большинству людей не стоит держать самим. Создать почтовый ящик легко; трудная задача — доставить письмо в чужой ящик, потому что каждый крупный провайдер решает по репутации, а не по вашим добрым намерениям.
| Почтовая служба провайдера | Свой почтовый сервер | |
|---|---|---|
| Настройка | Сменить записи MX и создать ящики | Установить, настроить и обслуживать две службы |
| Доставляемость | Ею занимается провайдер с репутацией | Своя: строить с нуля, по одному IP за раз |
| Стоимость | За пользователя в месяц | Сервер, за который вы уже платите |
| Контроль | Ограничен тем, что предлагает провайдер | Каждая настройка, каждая строка журнала |
| Обслуживание | Провайдер обновляет | Вы обновляете Postfix, Dovecot и фильтры |
| Хорошо для | Бизнеса, настоящего ящика, общей команды | Учёбы, многих адресов, почтового шлюза, который вы понимаете |
Небольшому сайту с одним ящиком лучше подойдёт служба провайдера или почта хостинга из урока 4. Стройте свой сервер, когда хотите понять, как работает почта, когда нужно много адресов или когда у вас уже есть сервер и вы хотите держать журналы в своих руках. Остальная часть урока делает именно это на той же машине с Ubuntu.
MX, SPF, DKIM, DMARC
Четыре записи решают, будет ли доставлена ваша почта, и каждая отвечает на свой вопрос.
- MX говорит, куда идёт почта для домена.
- SPF перечисляет серверы, которым разрешено отправлять почту за домен.
- DKIM подписывает каждое письмо ключом, чтобы получатель мог проверить, что его не изменили.
- DMARC говорит получателю, что делать, когда проверки SPF и DKIM не совпадают с доменом в заголовке From, и куда отправлять отчёты. Проверка DMARC пройдена, если пройдена проверка SPF или DKIM с совпадением домена (выравниванием).
Почтовый сервис сам даёт точные значения, и вы вставляете их в панель DNS. На своём сервере они выглядят так:
example.com. 3600 IN MX 10 mail.example.com. mail.example.com. 3600 IN A 203.0.113.10 example.com. 3600 IN TXT "v=spf1 mx -all" mail._domainkey.example.com. 3600 IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBg..." _dmarc.example.com. 3600 IN TXT "v=DMARC1; p=none; rua=mailto:dmarc@example.com"
Запись SPF означает: серверы из записей MX могут отправлять, а все остальные проверку не проходят (жёсткий отказ). Запись SPF на имя должна быть ровно одна, и она может вызвать не больше десяти запросов DNS. Значение DKIM длинное, и его копируют точно, без переносов строк. Проверка DMARC пройдена, когда пройдена проверка SPF или DKIM и домен в ней совпадает с доменом в заголовке From; не пройдена — когда ни одна из них не даёт совпадения. SPF проверяют по конвертному отправителю, которого релей или почтовая программа может переписать, поэтому успешная проверка SPF ещё не означает совпадения домена; DKIM подписывает сам домен From. DMARC начинают с p=none, что только сообщает; после месяца чистых отчётов его поднимают до quarantine, а затем до reject.
Обратный DNS
Прямой DNS превращает имя в адрес. Обратный DNS делает наоборот, и почтовые серверы его проверяют: письмо с IP-адреса, у которого нет обратного имени или оно не совпадает с именем в приветствии, считают подозрительным. Создать его сами вы не можете; его задаёт владелец адреса.
В панели провайдера VPS найдите настройку обратного DNS или PTR для вашего адреса IPv4 и задайте mail.example.com. Адресу IPv6 нужна своя запись PTR. Затем проверьте снаружи:
dig +short -x 203.0.113.10
mail.example.com.
Имя в ответе должно совпадать с именем, которое ваш почтовый сервер объявляет в приветствии, а это myhostname в Postfix. Несовпадение — самая частая причина, по которой новый почтовый сервер попадает в спам.
Порт 25 часто закрыт
Многие провайдеры VPS по умолчанию блокируют исходящий порт 25, потому что это порт спама. Почта всё равно приходит, так как входящий порт 25 обычно открыт, но ничего из отправленного не покидает машину. Проверьте это, прежде чем что-то настраивать:
sudo apt install netcat-openbsd
nc -vz alt1.gmail-smtp-in.l.google.com 25
Connection to alt1.gmail-smtp-in.l.google.com 25 port [tcp/smtp] succeeded!
Тайм-аут вместо успеха означает, что провайдер блокирует порт. Попросите поддержку открыть его — многие провайдеры делают это для проверенного аккаунта, — или отправляйте через релей: смарт-хост, который принимает вашу почту на порту 587 с аутентификацией и доставляет её за вас. Релей — практичное решение и тогда, когда у вашего IP плохая репутация и вы не хотите ждать неделями, пока она улучшится.
Postfix и Dovecot
Postfix передаёт почту между серверами; Dovecot позволяет почтовому клиенту читать ящик по IMAP. Файрвол из урока 8 разрешает только SSH и веб-порты, поэтому сначала откройте почтовые порты, иначе службы запустятся и останутся недоступными:
sudo ufw allow 25/tcp
sudo ufw allow 587/tcp
sudo ufw allow 993/tcp
Затем установите обе службы и ответьте Internet Site с example.com как системным именем почты, когда установщик спросит:
sudo apt install postfix dovecot-core dovecot-imapd
Основная настройка задаёт имя сервера, домены, для которых он принимает почту, и сертификат TLS. В main.cf Ubuntu уже есть большинство этих ключей, поэтому править файл руками значит находить каждую старую строку и удалять её. postconf -e заменяет значение на месте, одна настройка на команду:
sudo postconf -e 'myhostname = mail.example.com'
sudo postconf -e 'mydomain = example.com'
sudo postconf -e 'myorigin = $mydomain'
sudo postconf -e 'inet_interfaces = all'
sudo postconf -e 'inet_protocols = ipv4'
sudo postconf -e 'mydestination = $myhostname, $mydomain, localhost.$mydomain, localhost'
sudo postconf -e 'home_mailbox = Maildir/'
sudo postconf -e 'mynetworks = 127.0.0.0/8'
sudo postconf -e 'relay_domains ='
sudo postconf -e 'smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination'
sudo postconf -e 'smtpd_tls_cert_file = /etc/letsencrypt/live/example.com/fullchain.pem'
sudo postconf -e 'smtpd_tls_key_file = /etc/letsencrypt/live/example.com/privkey.pem'
sudo postconf -e 'smtpd_tls_security_level = may'
sudo postconf -e 'smtpd_sasl_type = dovecot'
sudo postconf -e 'smtpd_sasl_path = private/auth'
sudo postconf -e 'smtpd_sasl_auth_enable = yes'
sudo postconf -e 'smtpd_tls_auth_only = yes'
sudo systemctl reload postfix
mydestination перечисляет домены, которые этот
сервер считает своими; relay_domains пуст,
поэтому он ничего никому не пересылает.
home_mailbox заставляет Postfix писать каждое
письмо в папку Maildir пользователя, откуда его читает Dovecot.
Ограничения релея разрешают почту с самой машины и от
аутентифицированных пользователей и отказывают во всём остальном для
внешних получателей. Именно эта строка делает открытый релей
невозможным.
smtpd_tls_auth_only = yes — вторая половина
замка: при глобально включённой аутентификации она заставляет Postfix
отказать в пароле, пока не поднят TLS. Без неё порт 25 принял бы пароль
открытым текстом до начала зашифрованного сеанса.
Порт отправки, 587, — там, откуда шлют почтовые клиенты. Клиент подключается открытым текстом, переключается на TLS через STARTTLS и только потом может аутентифицироваться. Порт 465 — другая схема, неявный TLS: там сеанс TLS начинается вместе с подключением, с первого байта.
sudo vi /etc/postfix/master.cf
submission inet n - y - - smtpd -o syslog_name=postfix/submission -o smtpd_tls_security_level=encrypt -o smtpd_sasl_auth_enable=yes -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
Dovecot нужны место для ящика, TLS и сокет, который Postfix использует для проверки паролей. Их держат три файла:
sudo vi /etc/dovecot/conf.d/10-mail.conf
mail_location = maildir:~/Maildir
sudo vi /etc/dovecot/conf.d/10-ssl.conf
ssl = required ssl_cert = </etc/letsencrypt/live/example.com/fullchain.pem ssl_key = </etc/letsencrypt/live/example.com/privkey.pem
sudo vi /etc/dovecot/conf.d/10-master.conf
service auth { unix_listener /var/spool/postfix/private/auth { mode = 0660 user = postfix group = postfix } }
Блок вставляют внутрь существующего раздела service auth { }, где он заменяет закомментированный слушатель с тем же именем, который уже положил туда пакет.
Перезапустите обе службы и создайте первый ящик как системного пользователя — это простейшая схема для нескольких адресов:
sudo systemctl restart postfix dovecot
sudo adduser you
Аккаунт входит в IMAP как you со своим системным паролем, на порт 993 с TLS, и отправляет на порт 587 с STARTTLS. Более крупная схема хранит пользователей в базе данных, и это та же схема, только частей больше.
Подпись DKIM
DKIM подписывает каждое исходящее письмо закрытым ключом, а открытый ключ лежит в записи TXT, чтобы получатели могли проверить подпись. Сгенерируйте ключ на 2048 бит для селектора mail:
sudo apt install opendkim opendkim-tools
sudo mkdir -p /etc/dkimkeys
sudo opendkim-genkey -b 2048 -v -d example.com -s mail -D /etc/dkimkeys
opendkim-genkey: generating private key opendkim-genkey: private key written to mail.private opendkim-genkey: extracting public key opendkim-genkey: DNS TXT record written to mail.txt
opendkim-genkey молчит, если не задан -v, поэтому в команде выше он есть. Команда работает от root, поэтому закрытый ключ принадлежит root, а служба OpenDKIM, которая работает под opendkim, не может его прочитать и ничего не подписала бы. Отдайте ключ этому пользователю:
sudo chown opendkim:opendkim /etc/dkimkeys/mail.private
Файл /etc/dkimkeys/mail.txt содержит запись DNS; опубликуйте его содержимое как запись TXT на mail._domainkey.example.com. Файл разбивает длинный ключ на несколько строк в кавычках для читаемости; соедините их в одно значение, без пробелов и переносов строк.
Затем скажите OpenDKIM, какой ключ подписывает какой домен. В /etc/opendkim.conf Ubuntu уже есть активная строка Socket, local:/run/opendkim/opendkim.sock: замените эту строку на inet-сокет ниже, не добавляйте вторую:
sudo vi /etc/opendkim.conf
Domain example.com Selector mail KeyFile /etc/dkimkeys/mail.private Socket inet:12301@localhost
Подключите Postfix к милтеру, затем перезапустите обе службы:
sudo postconf -e 'milter_default_action = accept'
sudo postconf -e 'smtpd_milters = inet:localhost:12301'
sudo postconf -e 'non_smtpd_milters = inet:localhost:12301'
sudo systemctl restart opendkim postfix
dig +short TXT mail._domainkey.example.com
"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..."
Цель — подпись, которая проверяется у получателя, а не запись, которая существует. Отправьте письмо в ящик у крупного провайдера и прочитайте его заголовки, как показывает раздел о проверке.
TLS для почты, без открытого релея
Сертификат для mail.example.com получают через certbot, как любой другой. Добавьте имя в блок сервера сайта, чтобы проверка по HTTP могла до него добраться:
sudo vi /etc/nginx/sites-available/example.com
server_name example.com www.example.com mail.example.com;
sudo nginx -t
sudo systemctl reload nginx
Затем выпустите один сертификат на все три имени:
sudo certbot --nginx -d example.com -d www.example.com -d mail.example.com
Postfix и Dovecot теперь читают файлы из /etc/letsencrypt/live/example.com/: certbot называет папку по первому домену в команде, а сам сертификат покрывает все три. Плагин nginx у certbot перезагружает только nginx, поэтому Postfix и Dovecot нужен свой хук развёртывания; каждое продление его запускает:
sudo vi /etc/letsencrypt/renewal-hooks/deploy/reload-mail.sh
#!/bin/sh systemctl reload postfix dovecot
sudo chmod 755 /etc/letsencrypt/renewal-hooks/deploy/reload-mail.sh
Теперь докажите, что сервер не пересылает. С другой машины подключитесь к порту 25 и попробуйте отправить письмо на адрес, который вам не принадлежит. Сервер должен отказать с ошибкой релея:
swaks --to someone@example.net --server 203.0.113.10:25
<** 554 5.7.1 <someone@example.net>: Relay access denied
Отказ — правильный ответ. Если сервер принимает письмо, ограничения релея неверны, и у вас открытый релей: остановитесь и исправьте это, прежде чем адрес используют для спама. Та же проверка на порт 587 без пароля тоже должна закончиться отказом с ошибкой аутентификации.
Отправка с сайта
Форме обратной связи лучше не использовать функцию mail() в PHP через локальную очередь: письмо, отправленное так, не имеет аутентификации, и его легко пометить как спам. Отправляйте через порт отправки с аутентификацией. msmtp — небольшой клиент SMTP, который PHP может вызывать вместо sendmail:
sudo apt install msmtp
Ставьте только msmtp, а не пакет msmtp-mta: тот заменяет Postfix как локальный агент передачи почты, а этому серверу нужен Postfix.
sudo mkdir -p /etc/msmtp
sudo vi /etc/msmtp/example.conf
defaults auth on tls on tls_trust_file /etc/ssl/certs/ca-certificates.crt account example host mail.example.com port 587 user you password a-long-random-password from you@example.com account default : example
sudo chown root:example /etc/msmtp/example.conf
sudo chmod 640 /etc/msmtp/example.conf
Логин — you, системный пользователь из шага с ящиком, а не полный адрес: Dovecot аутентифицирует системных пользователей по имени. Адрес отправителя остаётся you@example.com.
Файл принадлежит root и группе сайта, поэтому процесс PHP может прочитать пароль, а другие пользователи — нет. Затем направьте на него пул сайта. sendmail_path — системная настройка, поэтому она идёт в пул через php_admin_value:
sudo vi /etc/php/8.3/fpm/pool.d/example.com.conf
php_admin_value[sendmail_path] = /usr/bin/msmtp -t --file=/etc/msmtp/example.conf
sudo systemctl reload php8.3-fpm
Теперь форма может отправлять через ваш сервер, с TLS и настоящим адресом отправителя. Никогда не кладите пароль SMTP в веб-корень; утёкший файл .env — одна из угроз из урока 7.
Проверка через swaks и заголовки
Bash и почтового клиента недостаточно, чтобы отлаживать почту. swaks — одна команда, которая говорит на SMTP и печатает весь разговор:
sudo apt install swaks
swaks --to you@gmail.com --from you@example.com --server mail.example.com:587 --auth LOGIN --auth-user you --auth-password "your-password" --tls
<- 250 2.0.0 Ok: queued as 4Xy2Z -> QUIT <- 221 2.0.0 Bye === Connection closed with remote host.
Письмо в очереди — только половина доказательства. В ящике получателя откройте полные заголовки и найдите результаты аутентификации:
Authentication-Results: mx.google.com; spf=pass (google.com: domain of you@example.com designates 203.0.113.10 as permitted sender) smtp.mailfrom=you@example.com; dkim=pass header.i=@example.com header.s=mail header.b=AbCdEfGh; dmarc=pass (p=NONE sp=NONE) header.from=example.com
Три отметки pass означают, что записи верны.
spf=fail
означает, что вашу почту шлёт адрес, которого нет в записи;
dkim=fail — что селектор, ключ или подпись
неверны. dmarc=fail появляется, только когда ни
SPF, ни DKIM не прошли с совпадением домена: обе проверки не пройдены
или обе пройдены для домена, отличного от домена в заголовке From, а это
и даёт релей, переписывающий конвертного отправителя. Читайте первую
ошибку, а не последнюю.
Если письмо попадает в спам, хотя все три проверки пройдены, обычная
причина — репутация IP. Проверьте адрес по публичным блок-листам на
check.spamhaus.org
и на
mxtoolbox.com.
Попавший в список адрес нужно очистить, а затем попросить об удалении;
пока он в списке, большинство получателей откажут или отправят вашу
почту в карантин.
MTA-STS и отчёты
Две необязательные записи делают почту надёжнее, и обе стоит добавить, когда основные проверки проходят.
MTA-STS публикует политику, которая говорит, что почта на ваш домен должна идти по TLS. Ей нужна запись TXT и небольшая страница по HTTPS:
_mta-sts.example.com. 3600 IN TXT "v=STSv1; id=20261106T000000"
Саму политику отдают по адресу https://mta-sts.example.com/.well-known/mta-sts.txt:
version: STSv1 mode: testing mx: mail.example.com max_age: 86400
Начните с mode: testing и читайте отчёты; переключитесь на enforce, когда каждый важный для вас отправитель всё ещё доставляет. TLS-RPT — канал отчётов о сбоях TLS:
_smtp._tls.example.com. 3600 IN TXT "v=TLSRPTv1; rua=mailto:tlsrpt@example.com"
Обе записи легко добавить и легко забыть. Запишите их вместе со значениями MX и SPF и проверяйте снова после любого изменения почтового сервера.
Тест
1 Что говорит запись MX домена?
Ответ
b) MX называет почтовые серверы назначения, с приоритетом. SPF — список разрешённых отправителей, DKIM — подпись, DMARC — политика.
2 Ваша запись SPF — v=spf1 mx -all. Что означает -all?
Ответ
b) Минус означает жёсткий отказ. Используйте ~all во время переезда, когда некоторые отправители ещё не в списке.
3 Зачем новому почтовому серверу совпадающая запись PTR?
Ответ
a) Отсутствующее или несовпадающее обратное имя — сильный сигнал спама. PTR задаёт владелец адреса, то есть провайдер VPS, а не ваша панель DNS.
4 nc -vz alt1.gmail-smtp-in.l.google.com 25 выдаёт тайм-аут. Что это значит?
Ответ
b) Тайм-аут на исходящем 25 — классическая блокировка. Попросите провайдера открыть его или отправляйте через релей на порту 587.
5 Какая настройка Postfix не даёт серверу стать открытым релеем?
Ответ
b) Ограничения релея решают, кто может отправлять почту на внешние домены. Проверка с другой машины должна ответить Relay access denied.
6 Письмо приходит с spf=pass, dkim=fail и dmarc=fail. Куда смотреть первым?
Ответ
b) SPF прошёл, но без совпадения домена с заголовком From, поэтому проверка DMARC всё равно не пройдена: конвертный отправитель принадлежит другому домену, например релею или почтовой программе, а заголовок From говорит ваш домен. DKIM подписывает сам домен From, поэтому исправлять надо его: сравните селектор в подписи с записью TXT и ключом на диске.
7 Зачем отправлять почту сайта через аутентифицированный SMTP, а не через локальную очередь mail()?
Ответ
b) Аутентифицированная отправка на порт 587 с TLS — то, что получающая сторона ждёт от настоящего почтового домена. Она ещё и держит пароль SMTP вне кода веб-приложения.
Автор статьи: Arthur Isaev