В этой статье я покажу, как настроить свой почтовый сервер для аккаунтов локальных пользователей без веб-морд, docker-ов, телеграм-помоек и LLM-высеров.
Жалкие отп^W^W Дисклеймер: я - админ локалхоста. В статье полно англицизмов и недопереведённых терминов.
Что происходит? Где я? Помогите!
Эта статья — перевод моего же поста, по сути копии вот этого немного устаревшего поста в другом блоге (архивная копия) (далее — «оригинальный гайд»), на который мне в своё время очень повезло наткнуться.
Будем настраивать почтовый сервер так, чтобы у каждого пользователя ОС была своя почта, а письма хранились в ~/Maildir.
Индигриенты ©
Понадобятся нам, собственно, сервер на arch linux с белым IP, доменное имя, сертификат и, желательно, reverse DNS.
Сервер должен быть исключительно и только на arch, на других дистрибутивах поднять почтовый сервер принципиально невозможно.
Reverse DNS нас интересует только тем, что позволяет по IP-адресу получить доменное имя, на этот адрес указывающее, а нужен он нам затем, чтобы у почтовых серверов, с которыми будет общаться наш сервер, была yet another проверка, уменьшающая вероятность попадания нашего сервера в спам-листы.
На самом по себе попадании в спам-листы мои полномочия всё, но для выхода из одного того, в который попадал я, достаточно было ткнуть мышкой одну кнопку на сайте спам-листа.
Вычитать reverse DNS для, например, 8.8.8.8 можно вот так (указывает оно на dns.google, точка в конце нас сейчас не интересует):
$ dig -x 8.8.8.8 | grep -vE "^$|^;" 8.8.8.8.in-addr.arpa. 75882 IN PTR dns.google.
Выставить reverse DNS можно либо в админке вашего домашнего провайдера или хостера, либо, как сделал я, написав e-mail в поддержку провайдера и попросив выставить нужное значение reverse DNS для вашего IP-адреса. ИЧСХ, мне сделали это за 15 минут без лишних вопросов, я сильно удивился.
Оригинальный гайд использует self-signed сертификат, я использую let’s encrypt, потому что мне даже думать лень о проблемах, связанных с самоподписными сертификатами. Когда-нибудь, думаю, подумаю о проблемах с let’s encrypt, но Леонид Каневский.
Перед тем, как отправлять письма со своего почтового сервера на реальные адреса внешних серверов прямо в процессе настройки своего сервера с мыслью «я, конечно, гайд не дочитал, но а вдруг оно уже работает», потренируйтесь на кошках, иначе есть риск загреметь в спам-листы.
После того как у вас всё заработает, или после последующих изменений конфигов, делайте бекапы. По крайней мере, dovecot не так давно сломал обратную совместимость конфигов. Если у вас под рукой есть бекап конфигов, вы можете откатить версию postfix/dovecot/whatever из /var/cache/pacman/pkg/, а уже потом, имея рабочую почту, спокойно разбираться с новой версией сломанного софта.
Ниже, как и в оригинальном гайде, пойдут однотипные главы с подглавами, в которых большинство содержимого — команды, показывающие значимые части конфигов, и их, команд, вывод. В оригинальном гайде, который я по сути слямзил, достаточно много поясняющих комментариев. Туда я вас за ними и приглашаю, а здесь будет минимум, необходимый для запуска сервера.
Что вообще делать со всем нижеизложенным? В каждой главе накатывать указанный софт, потом grep-ать по /etc/<tool-name>, в каких конфигах у вас лежит что-то похожее, и доводить эти конфиги до нужной кондиции.
Сертификаты
$ pacman -S certbot $ certbot -d mydomain.name
Если у вас это не работает или у вас нет веб-сервера, добро пожаловать в вики: https://wiki.archlinux.org/title/Certbot
Dovecot
https://wiki.archlinux.org/title/Dovecot
Установка
pacman -S dovecot
Настройка
Тип и расположение почты, «папки»
doveconf -n mail_path mail_driver namespace inbox
Вывод:
mail_path = ~/Maildir mail_driver = maildir namespace inbox { inbox = yes separator = / mailbox Drafts { special_use = "\\Drafts" } mailbox Junk { special_use = "\\Junk" } mailbox Trash { special_use = "\\Trash" } mailbox Sent { special_use = "\\Sent" } mailbox "Sent Messages" { special_use = "\\Sent" } }
Добавляем юзера dovecot в группу mail
usermod -aG mail dovecot id dovecot
Вывод:
uid=76(dovecot) gid=76(dovecot) groups=76(dovecot),12(mail)
SSL-сертификаты
SSL
doveconf ssl ssl_server_cert_file ssl_server_key_file ssl_server_dh_file
Вывод:
ssl = yes ssl_server_cert_file = /etc/letsencrypt/live/kostovsky.me/fullchain.pem ssl_server_key_file = /etc/letsencrypt/live/kostovsky.me/privkey.pem ssl_server_dh_file = /etc/dovecot/dh.pem
Аутентификация
doveconf -n userdb passdb
Вывод (имена секций здесь выбраны случайно: с некоторой версии dovecot потребовал их, имена, добавить, я и добавил):
userdb fuck_dovecot { driver = passwd } passdb fuck_dovecot { driver = pam }
cat /etc/pam.d/dovecot
Вывод:
#%PAM-1.0 auth include system-auth account include system-auth session include system-auth password include system-auth
Создаём юзера
Если вдруг у вас его ещё нет. Его логин и будет использоваться в почтовом адресе. У каждого «нормального» пользователя в системе будет свой почтовый ящик, а для логина в почту будет использоваться пароль этого пользователя в ОС.
useradd nikita -m passwd nikita
Весь итоговый конфиг
Спрятал под спойлер
doveconf -n
Output:
## 2.4.4 (8b687aa65c): /etc/dovecot/dovecot.conf ## OS: Linux 7.1.5-arch1-2 x86_64 ## Hostname: home ## 29 default setting changes since version 2.4.1 dovecot_config_version = 2.4.1 auth_mechanisms = plain dovecot_storage_version = 2.4.1 mail_driver = maildir mail_path = ~/Maildir protocols { imap = yes lmtp = yes } passdb fuck_dovecot { driver = pam } userdb fuck_dovecot { driver = passwd } service director { listen = * :: unix_listener login/director { } fifo_listener login/proxy-notify { } unix_listener director-userdb { } } service imap-login { inet_listener imap { } inet_listener imaps { } } service pop3-login { inet_listener pop3 { } inet_listener pop3s { } } service submission-login { inet_listener submission { } inet_listener submissions { } } namespace inbox { inbox = yes separator = / mailbox Drafts { special_use = "\\Drafts" } mailbox Junk { special_use = "\\Junk" } mailbox Trash { special_use = "\\Trash" } mailbox Sent { special_use = "\\Sent" } mailbox "Sent Messages" { special_use = "\\Sent" } } service lmtp { unix_listener lmtp { } } service imap { } service pop3 { } service submission { } service auth { unix_listener /var/spool/postfix/private/auth { group = postfix mode = 0666 user = postfix } } service auth-worker { } service dict { unix_listener dict { } } ssl_server { cert_file = /etc/letsencrypt/live/kostovsky.me/fullchain.pem dh_file = /etc/dovecot/dh.pem key_file = /etc/letsencrypt/live/kostovsky.me/privkey.pem }
Сервис
systemctl enable --now dovecot.service
Если вы используете ufw, загляните в оригинальный гайд, там есть дополнительные настройки и комментарии.
Инструментарий для тестирования
Postfix
https://wiki.archlinux.org/title/Postfix
Установка
pacman -S postfix
Каталоги
postconf -n | grep -E "directory\s"
Вывод:
command_directory = /usr/bin daemon_directory = /usr/lib/postfix/bin data_directory = /var/lib/postfix html_directory = no manpage_directory = /usr/share/man meta_directory = /etc/postfix queue_directory = /var/spool/postfix readme_directory = /usr/share/doc/postfix sample_directory = /etc/postfix shlib_directory = /usr/lib/postfix
Домен
postconf -n | grep ^my
Output:
mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain, mail.$mydomain mydomain = kostovsky.me myhostname = kostovsky.me myorigin = $mydomain
Aliases
postconf -n | grep -E "^alias|newaliases"
Вывод:
alias_database = $alias_maps alias_maps = lmdb:/etc/postfix/aliases newaliases_path = /usr/bin/newaliases
Catch-all email
«Всеперехватывающий»(?) адрес. Здесь автор оригинального гайда указал пользователя, который будет получать почту (в смысле спам), адресованную несуществующим в системе пользователям. У меня здесь пусто.
postconf -n luser_relay local_recipient_maps
Output:
local_recipient_maps =
Шифруемся с помощью TLS
Получение почты:
postconf -n | grep smtpd_ | grep tls
Вывод:
smtpd_sasl_tls_security_options = noanonymous smtpd_tls_auth_only = yes smtpd_tls_cert_file = /etc/letsencrypt/live/kostovsky.me/fullchain.pem smtpd_tls_key_file = /etc/letsencrypt/live/kostovsky.me/privkey.pem smtpd_use_tls = yes
Отправка почты:
postconf -n | grep smtp_ | grep tls
Вывод:
smtp_tls_security_level = may
Аутентификация/авторизация
Конфиг аутентификации postfix:
postconf -n | grep ^smtpd_sasl
Вывод:
smtpd_sasl_auth_enable = yes smtpd_sasl_local_domain = $mydomain smtpd_sasl_path = private/auth smtpd_sasl_security_options = noanonymous, noplaintext smtpd_sasl_tls_security_options = noanonymous smtpd_sasl_type = dovecot
Интеграция с аутентификацией dovecot
doveconf -n service/auth
Вывод:
service auth { unix_listener /var/spool/postfix/private/auth { group = postfix mode = 0666 user = postfix } }
Relay (шлюз?) и ограничения
postconf -n | grep -E "helo|relay"
Вывод:
smtpd_helo_required = yes smtpd_helo_restrictions = reject_invalid_helo_hostname, reject_non_fqdn_helo_hostname smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination
Mail submission
Я не знаю, как и есть ли смысл переводить некоторые словосочетания, но перевод “mail submission” не так уж и необходим для настройки сервера.
postconf -M submission
Вывод:
submission inet n - n - - smtpd -o syslog_name=postfix/submission -o smtpd_tls_security_level=encrypt -o smtpd_sasl_auth_enable=yes -o smtpd_tls_auth_only=yes -o smtpd_reject_unlisted_recipient=no -o smtpd_relay_restrictions= -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject -o milter_macro_daemon_name=ORIGINATING
DNS-запись TXT
drill -Q kostovsky.me TXT drill -Q smtp.kostovsky.me TXT
Вывод:
"v=spf1 a mx ip4:195.234.21.24 ~all" "v=spf1 a mx ip4:195.234.21.24 ~all"
Запись reverse DNS
drill -Q 195.234.21.24 -x
Вывод:
kostovsky.me.
Весь итоговый конфиг
Спрятал под спойлер
postconf -n
Вывод:
alias_database = $alias_maps alias_maps = lmdb:/etc/postfix/aliases command_directory = /usr/bin compatibility_level = 3.11 daemon_directory = /usr/lib/postfix/bin data_directory = /var/lib/postfix debug_peer_level = 2 debugger_command = PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin ddd $daemon_directory/$process_name $process_id & sleep 5 home_mailbox = Maildir/ html_directory = no inet_protocols = ipv4 local_recipient_maps = mail_owner = postfix mailbox_size_limit = 512000000 mailq_path = /usr/bin/mailq manpage_directory = /usr/share/man message_size_limit = 512000000 meta_directory = /etc/postfix milter_default_action = accept mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain, mail.$mydomain mydomain = kostovsky.me myhostname = kostovsky.me mynetworks = 10.66.66.0/24 myorigin = $mydomain newaliases_path = /usr/bin/newaliases non_smtpd_milters = $smtpd_milters queue_directory = /var/spool/postfix readme_directory = /usr/share/doc/postfix sample_directory = /etc/postfix sendmail_path = /usr/bin/sendmail setgid_group = postdrop shlib_directory = /usr/lib/postfix smtp_header_checks = regexp:/etc/postfix/smtp_header_checks smtp_tls_security_level = may smtpd_banner = exim smtpd_helo_required = yes smtpd_helo_restrictions = reject_invalid_helo_hostname, reject_non_fqdn_helo_hostname smtpd_milters = inet:localhost:8891, inet:localhost:8893 smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination smtpd_sasl_auth_enable = yes smtpd_sasl_local_domain = $mydomain smtpd_sasl_path = private/auth smtpd_sasl_security_options = noanonymous, noplaintext smtpd_sasl_tls_security_options = noanonymous smtpd_sasl_type = dovecot smtpd_tls_auth_only = yes smtpd_tls_cert_file = /etc/letsencrypt/live/kostovsky.me/fullchain.pem smtpd_tls_key_file = /etc/letsencrypt/live/kostovsky.me/privkey.pem smtpd_use_tls = yes unknown_local_recipient_reject_code = 550
Сервис
systemctl enable --now postfix.service
Если вы используете ufw, загляните в оригинальный гайд, там есть настройки и комментарии.
Инструментарий для тестирования
https://check.spamhaus.org/results/?query=195.234.21.24 - hidden behind cloudflare
Особо упоротые могут быстренько проверить SMTP с помощью openssl:
openssl s_client -connect kostovsky.me:25 -starttls smtp
DomainKeys Identified Mail - DKIM
Установка
https://wiki.archlinux.org/title/OpenDKIM
pacman -S opendkim
Настройка
Минимальный конфиг
grep -v -e '^#' -e '^[[:space:]]*$' /etc/opendkim/opendkim.conf
Вывод:
Canonicalization relaxed/simple Domain kostovsky.me KeyFile /etc/opendkim/rig.private Selector rig Socket inet:8891@localhost Syslog Yes UserID opendkim:postfix
Генерация файлов ключей
Generate rig.private and rig.txt files.
opendkim-genkey --restrict --selector rig --domain kostovsky.me --directory /etc/opendkim
Интеграция с postfix
postconf -n | grep milter
Вывод:
milter_default_action = accept non_smtpd_milters = $smtpd_milters smtpd_milters = inet:localhost:8891, inet:localhost:8893
Общаться с postfix оно будет через сокет inet:localhost:8891.
DNS-запись TXT
cat /etc/opendkim/rig.txt
Вывод:
rig._domainkey IN TXT ( "v=DKIM1; k=rsa; s=email; " "p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDA8dfa8T7Hhn5/nWVivUpeBEs339fa9bCUHN7tqWxchzy5vBw/8Z3/9Pp8XIXo/0GnpOCu0Fh4P27tMnIdH5N4Sy3E/XcNHL7cRAB7gqKjEMuT2S49L7j3g19Dns7qN8rbArC7Krzq5mdHPHk1EqGsytGlv9CMD5gegpUsM7wd3QIDAQAB" ) ; ----- DKIM key rig for kostovsky.me
drill -Q rig._domainkey.kostovsky.me TXT
Вывод:
"v=DKIM1; k=rsa; s=email; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDA8dfa8T7Hhn5/nWVivUpeBEs339fa9bCUHN7tqWxchzy5vBw/8Z3/9Pp8XIXo/0GnpOCu0Fh4P27tMnIdH5N4Sy3E/XcNHL7cRAB7gqKjEMuT2S49L7j3g19Dns7qN8rbArC7Krzq5mdHPHk1EqGsytGlv9CMD5gegpUsM7wd3QIDAQAB"
Сервис
systemctl enable --now opendkim.service
Инструментарий для тестирования
https://dmarcian.com/dkim-inspector/ - вот, для примера, мой отчёт
Domain-based Message Authentication, Reporting and Conformance - DMARC
https://wiki.archlinux.org/title/OpenDMARC
Установка
pacman -S opendmarc
Настройка
Минимальный конфиг
grep -vE "^#|^\s*$" /etc/opendmarc/opendmarc.conf
Вывод:
AuthservID HOSTNAME IgnoreAuthenticatedClients true Socket inet:8893@localhost SPFSelfValidate true UMask 002
Интеграция с postfix
postconf -n | grep milter
Вывод:
milter_default_action = accept non_smtpd_milters = $smtpd_milters smtpd_milters = inet:localhost:8891, inet:localhost:8893
Общаться с postfix оно будет через сокет inet:localhost:8893.
DNS-запись TXT
drill -Q _dmarc.kostovsky.me TXT
Вывод:
"v=DMARC1; p=quarantine; rua=mailto:postmaster@kostovsky.me; ruf=mailto:forensic@kostovsky.me; adkim=s; aspf=s; fo=1; pct=25"
Сервис
systemctl enable --now opendmarc.service
Инструментарий для тестирования
Настройка Thunderbird
У меня весь гуй везде на английском, так что прошу извинить.
Outgoing server
“Account Settings”->“Edit outgoing server…”
Server Name: kostovsky.me
Port: 587
Connection security: STARTTLS
Authentication method: Normal password
User Name: nikita
Incoming server
“Server settings” section
Server Type: IMAP Mail Server
Server Name: kostovsky.me
Port: 993
User Name: nikita
Connection security: SSL/TLS
Authentication method: Normal password
Вот, вроде бы, всё и готово, но, пожалуйста, без резких движений. Шуруйте на mail-tester или подобный сервис и добивайтесь всех (значимых, содержимое письма не в счёт) зелёных галочек из всех, прежде чем отправить своё первое письмо на реальный внешний почтовый адрес.

