Top.Mail.Ru

Свой почтовый сервер на VPS: установка Postfix и Dovecot с нуля

10
Свой почтовый сервер на VPS: установка Postfix и Dovecot с нуля

Собственный почтовый сервер имеет смысл, когда вам нужен полный контроль над доставкой писем на корпоративном домене, вы хотите разобраться, как устроена почта на уровне протоколов, или просто не хотите зависеть от стороннего почтового сервиса. Но это не разовая настройка на пять минут: помимо установки Postfix и Dovecot придётся следить за репутацией IP-адреса, вовремя продлевать TLS-сертификаты и разбираться, почему письмо не дошло. В этой статье — только техническая часть: установка, базовая настройка и рабочая связка Postfix (принимает и отправляет почту по SMTP) и Dovecot (отдаёт её пользователям по IMAP и POP3) на VPS с Ubuntu 24.04 LTS.

Установка Postfix и Dovecot

Все три компонента ставятся одной командой: сам Postfix и три модуля Dovecot — для IMAP, POP3 и LMTP (протокол, через который Postfix будет передавать письма в Dovecot, об этом ниже).

sudo apt update
sudo apt install postfix dovecot-imapd dovecot-pop3d dovecot-lmtpd

На Ubuntu 24.04 в репозитории лежат Postfix 3.8.6 и Dovecot 1:2.3.21+dfsg1 — для новой установки этого достаточно, отдельно подключать сторонние репозитории не нужно. Вместе с Postfix автоматически появляется /usr/sbin/sendmail — классическая команда для отправки почты из скриптов и других программ. А вот утилита для чтения и отправки почты из терминала (команды mail/mailx) в пакет не входит — если она понадобится для тестов, ставится отдельно: sudo apt install mailutils.

Что спросит установщик Postfix

В процессе установки появится текстовый диалог debconf с двумя-тремя вопросами:

  • General type of mail configuration — тип конфигурации. Выбирайте Internet Site: сервер будет принимать и отправлять почту напрямую через SMTP, а не пересылать всё через промежуточный smarthost.
  • System mail name — системное имя почты. Этот вопрос появится, только если установщик не смог определить имя автоматически.
  • Other destinations to accept mail for — список доменов, письма для которых сервер принимает как «свои», локальные. По умолчанию туда попадает $myhostname, <mailname>, <короткое имя хоста>, localhost.localdomain, localhost — этот список и становится значением параметра mydestination в конфиге.

Базовая настройка Postfix: main.cf

Сразу после установки конфигурация уже рабочая, хоть и минимальная. Посмотреть её в чистом виде (только заданные параметры, без комментариев) можно командой postconf -n — реальный вывод будет длиннее (порядка 25 строк в алфавитном порядке), здесь — только те из них, что важны на этом шаге:

smtpd_tls_security_level = may
smtpd_tls_cert_file = /etc/ssl/certs/ssl-cert-snakeoil.pem
smtpd_tls_key_file = /etc/ssl/private/ssl-cert-snakeoil.key
myhostname = <короткое системное имя, НЕ FQDN>
myorigin = /etc/mailname
mydestination = $myhostname, <mailname>, <hostname>, localhost.localdomain, localhost
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
inet_interfaces = all
smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination

Разберём важные строки:

  • smtpd_tls_security_level = may — STARTTLS уже включён и предлагается всем, кто подключается. Соединение шифруется сразу, из коробки, даже до всякой ручной настройки TLS — это отдельный вопрос от того, доверяет ли клиент сертификату.
  • smtpd_tls_cert_file / smtpd_tls_key_file указывают на самоподписанный сертификат ssl-cert-snakeoil, который Ubuntu генерирует при установке. Шифрование работает, но почтовые клиенты будут показывать предупреждение о недоверенном сертификате — до тех пор, пока вы не подключите Let’s Encrypt (раздел ниже).
  • myhostname берётся из системного имени сервера — если оно не настроено как FQDN (частый случай на свежем VPS, где hostname выглядит как короткое служебное имя), Postfix так его и оставит. Для боевого сервера имя стоит проверить и при необходимости явно задать: sudo postconf -e "myhostname = mail.вашдомен.ru" и перезапустить Postfix. Именно это имя сервер представляет в приветствии SMTP-сессии (HELO/EHLO), и многие принимающие серверы обращают на него внимание при оценке спам-репутации.
  • myorigin = /etc/mailname — домен, который подставляется в адрес отправителя для локально отправленной почты, если он не указан явно. Значение берётся из содержимого файла /etc/mailname.
  • mydestination — домены, почта для которых считается «своей» и доставляется локально. Официальная документация Postfix (postfix.org/BASIC_CONFIGURATION_README.html) отдельно предупреждает: если убрать из этого списка $myhostname или localhost.$mydomain, можно получить зацикливание при доставке писем — трогайте этот параметр осторожно.
  • mynetworks — список сетей, которым разрешено пересылать почту через этот сервер без аутентификации. По умолчанию это только localhost — и это правильно: расширять его нужно только под конкретную задачу.
  • smtpd_relay_restrictions — определяет, кто вообще может использовать сервер как релей. Дефолтное значение уже защищает от превращения сервера в открытый релей для спама — именно поэтому позже для отправки через аутентификацию мы будем добавлять SASL, а не ослаблять этот параметр.

Если в будущем понадобится обслуживать несколько доменов через отдельные почтовые ящики (не системные Linux-пользователи, а виртуальные адреса), у Postfix есть отдельный механизм — virtual mailbox domains, описанный в postfix.org/VIRTUAL_README.html. Он использует свои параметры (virtual_mailbox_domains, virtual_mailbox_maps и так далее) и официально несовместим с одновременным указанием того же домена в mydestination — это взаимоисключающие механизмы. В этой статье мы идём по более простому и понятному для старта пути — через обычных системных пользователей Linux, поэтому в virtual-домены здесь не углубляемся.

Настройка Dovecot: переходим на Maildir

До правок Dovecot хранит почту в формате mbox — это видно в выводе doveconf -n: mail_location = mbox:~/mail:INBOX=/var/mail/%u. Формат mbox — это один большой файл на весь ящик, куда письма дописываются подряд. Большинство современных руководств советуют формат Maildir, и на то есть причина:

Параметр mbox (по умолчанию) Maildir
Хранение один файл на весь ящик отдельный файл на каждое письмо
Параллельный доступ требует блокировки файла без блокировок, безопаснее при одновременном доступе
Восстановление после сбоя повреждение файла может задеть весь ящик повреждение касается одного письма

Меняем формат в /etc/dovecot/conf.d/10-mail.conf:

sudo nano /etc/dovecot/conf.d/10-mail.conf

Находим строку mail_location и заменяем её значение на:

mail_location = maildir:~/Maildir

Аутентификацию настраивать отдельно не нужно: из коробки Dovecot работает через PAM и системных пользователей Linux (passdb { driver = pam }, userdb { driver = passwd }). Это значит, что почтовый ящик для нового человека в этой схеме — это обычный Linux-пользователь: sudo useradd -m mailuser и sudo passwd mailuser. Отдельную базу учётных записей заводить не требуется. TLS-сертификат у Dovecot по умолчанию — симлинк на тот же самоподписанный snakeoil-сертификат, что использует Postfix, так что шифрование IMAP/POP3-сессий тоже включено сразу после установки.

Связка Postfix и Dovecot через LMTP

LMTP (Local Mail Transfer Protocol) — протокол финальной доставки, по которому Postfix передаёт принятое письмо Dovecot, а тот уже сам кладёт его в правильный Maildir нужного пользователя. Это официальный, рекомендованный способ связки (документация на doc.dovecot.org).

Сначала включаем приём LMTP на стороне Dovecot. Открываем /etc/dovecot/conf.d/10-master.conf и внутри секции service lmtp { } добавляем unix-сокет:

service lmtp {
  unix_listener /var/spool/postfix/private/dovecot-lmtp {
    group = postfix
    mode = 0600
    user = postfix
  }
}

Теперь говорим Postfix использовать этот сокет для доставки. В /etc/postfix/main.cf:

sudo postconf -e "mailbox_transport = lmtp:unix:private/dovecot-lmtp"

И перезапускаем оба сервиса, чтобы изменения применились:

sudo systemctl restart dovecot postfix

Самая частая ошибка связки: auth_username_format

На этом шаге у большинства новичков всё технически настроено правильно, но письма всё равно не доходят до ящика — и это единственная по-настоящему коварная деталь во всей установке, поэтому стоит остановиться на ней отдельно.

По умолчанию в Dovecot задан параметр auth_username_format = %Lu — это заставляет Dovecot искать пользователя по полному логину, который прислал Postfix, включая домен: например testmail@example.com. Проблема в том, что такого системного пользователя Linux просто не существует — есть пользователь testmail, без домена в имени. В результате Postfix спокойно принимает письмо снаружи, но на этапе передачи в Dovecot по LMTP оно возвращается отправителю с ошибкой:

550 5.1.1 User doesn't exist

Без исправления этого параметра связка Postfix и Dovecot по описанной здесь схеме просто не заработает — письма будут постоянно возвращаться с этой ошибкой. Фикс — одна строка в /etc/dovecot/conf.d/10-auth.conf:

auth_username_format = %n

%n обрезает домен и оставляет только имя пользователя перед «@» — то есть Dovecot начинает искать testmail, а не testmail@example.com, и находит нужного Linux-пользователя. После правки — снова перезапуск:

sudo systemctl restart dovecot

SASL: аутентификация для отправки писем

Пока что сервер умеет только принимать входящую почту. Чтобы можно было слать письма через этот же сервер из обычного почтового клиента (с логином и паролем, а не только принимать входящие), нужен SASL — и удобнее всего реализовать его через тот же Dovecot, который уже знает, как проверять системных пользователей.

В /etc/dovecot/conf.d/10-master.conf, внутри секции service auth { }, добавляем сокет для Postfix:

service auth {
  unix_listener /var/spool/postfix/private/auth {
    mode = 0660
    user = postfix
    group = postfix
  }
}

В /etc/postfix/main.cf указываем Postfix использовать этот сокет:

sudo postconf -e "smtpd_sasl_type = dovecot"
sudo postconf -e "smtpd_sasl_path = private/auth"
sudo postconf -e "smtpd_sasl_auth_enable = yes"

И в /etc/dovecot/conf.d/10-auth.conf указываем допустимые механизмы аутентификации:

auth_mechanisms = plain login

Механизм login добавлен не для галочки — он нужен для совместимости со старыми почтовыми клиентами вроде Outlook и Windows Mail, которые не поддерживают чистый plain. После правок перезапускаем оба сервиса.

Порт 587: приём почты от клиентов с аутентификацией

Порт 25 предназначен для передачи почты между серверами, а не для отправки с личных устройств — многие провайдеры и почтовые клиенты сейчас требуют для исходящей отправки отдельный порт 587 (submission) с обязательной аутентификацией. В стоковом /etc/postfix/master.cf блок submission присутствует, но закомментирован по умолчанию — его нужно раскомментировать и добавить опции TLS и SASL:

В стоковом файле у этого блока другие опции, и одна из них — пустое значение smtpd_relay_restrictions= — если раскомментировать блок как есть, ничего не потеряв, но и не исправив именно эту строку, получится открытый релей на 587: сервер начнёт пересылать почту куда угодно без реальной проверки. Поэтому не раскомментируйте стоковые строки по одной — замените весь блок целиком на этот:

submission inet n       -       y       -       -       smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_reject_unlisted_recipient=no
  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
  -o milter_macro_daemon_name=ORIGINATING

Ключевая строка здесь — smtpd_relay_restrictions=permit_sasl_authenticated,reject: непустая, и требует авторизации для любой пересылки через этот порт. Проверить, что релей закрыт, можно так: подключиться к 587 без авторизации и попробовать отправить письмо на чужой домен — сервер должен ответить отказом (554 5.7.1 Recipient address rejected: Access denied), а не принять письмо.

Если нужен ещё и порт 465 с неявным TLS (для старых клиентов, которые не умеют STARTTLS) — в актуальном master.cf он называется submissions, с «s» на конце: это современное имя по RFC 8314, старые гайды могут называть его устаревшим термином «smtps». Настраивается он аналогично блоку submission, тоже раскомментированием и добавлением TLS/SASL-опций.

После правки конфигурации — перезапуск Postfix:

sudo systemctl restart postfix

Файрвол: открываем нужные порты в UFW

Даже если сервисы уже слушают все интерфейсы, снаружи почта будет недоступна, пока порты закрыты файрволом. Удобство в том, что пакеты Postfix и Dovecot сами регистрируют в UFW готовые профили с портами — их можно посмотреть командой:

sudo ufw app list

Среди прочего там появятся: Postfix, Postfix Submission, Postfix SMTPS, Dovecot IMAP, Dovecot Secure IMAP, Dovecot POP3, Dovecot Secure POP3. Проще открыть их по имени, чем запоминать номера портов:

sudo ufw allow Postfix
sudo ufw allow 'Postfix Submission'
sudo ufw allow 'Dovecot Secure IMAP'
sudo ufw allow 'Dovecot Secure POP3'

Незащищённые версии — обычный IMAP/POP3 без TLS (порты 143 и 110) — здесь намеренно не открываются: почтовые клиенты сегодня умеют TLS без исключений, а незащищённый вход означает пароль открытым текстом. Если UFW на сервере ещё не включался — перед ufw enable обязательно разрешите SSH (sudo ufw allow OpenSSH или ваш нестандартный порт), иначе рискуете потерять доступ к серверу вместе с настройкой почты.

Отдельный момент, который стоит проверить заранее: исходящий порт 25 у VPS-провайдеров не всегда открыт по умолчанию — многие блокируют его исходящим трафиком, чтобы сервер нельзя было использовать для рассылки спама. Это не общее правило для всех провайдеров, а вопрос к конкретному тарифу и провайдеру — проверить самостоятельно можно так:

nc -zv -w5 smtp.gmail.com 25

Если netcat не установлен, тот же результат можно получить через bash напрямую:

timeout 5 bash -c "echo > /dev/tcp/smtp.gmail.com/25" && echo открыт || echo закрыт

TLS-сертификат: от самоподписанного к Let’s Encrypt

Самоподписанный сертификат шифрует трафик, но почтовые клиенты будут жаловаться на недоверенный сертификат. Прежде чем выпускать настоящий сертификат, домен mail.вашдомен.ru должен указывать A-записью на IP этого сервера — certbot проверяет владение доменом через HTTP-запрос на порт 80.

Дальше два пути в зависимости от того, стоит ли уже на сервере веб-сервер:

  • Если на сервере уже работает nginx — ставим плагин и получаем сертификат с автонастройкой: sudo apt install certbot python3-certbot-nginx, затем sudo certbot --nginx.
  • Если веб-сервера нет и порт 80 свободен — можно выпустить сертификат в отдельном режиме: sudo certbot certonly --standalone. Если порт 80 занят каким-то другим веб-сервером, standalone-режим не сработает — его нужно временно остановить на время выпуска сертификата.

После выпуска сертификата прописываем пути к нему в Postfix (/etc/postfix/main.cf):

sudo postconf -e "smtpd_tls_cert_file = /etc/letsencrypt/live/mail.вашдомен.ru/fullchain.pem"
sudo postconf -e "smtpd_tls_key_file = /etc/letsencrypt/live/mail.вашдомен.ru/privkey.pem"

И в Dovecot, файл /etc/dovecot/conf.d/10-ssl.conf. Здесь важен синтаксис: перед путём к файлу нужен символ <, он означает «прочитать содержимое файла»:

ssl_cert = </etc/letsencrypt/live/mail.вашдомен.ru/fullchain.pem
ssl_key = </etc/letsencrypt/live/mail.вашдомен.ru/privkey.pem
sudo systemctl restart postfix dovecot

Автопродление в Ubuntu 24.04 идёт не через cron, как было в старых руководствах, а через systemd-таймер certbot.timer — он уже включён после установки пакета certbot, отдельно ничего запускать не нужно (файл в /etc/cron.d/certbot тоже ставится пакетом, но самоотключается, если видит, что в системе есть systemd — это не ошибка, если вы его там увидите).

Единственное, что стоит добавить — хук, чтобы после КАЖДОГО продления сертификата демоны подхватывали его без полного рестарта. Важно: команда certbot renew --deploy-hook ..., выполненная вручную сразу после выпуска, ничего не сохранит — она сработает, только когда до реального продления (обычно за 30 дней до истечения) действительно дойдёт очередь, а разово выполненная команда покажет «No renewals were attempted» и на этом всё закончится, хук нигде не останется. Хук нужно указать сразу при выпуске сертификата — тогда certbot сам запишет его в конфиг продления, и таймер будет запускать его каждый раз:

sudo certbot --nginx --deploy-hook "systemctl reload postfix dovecot"

(для варианта certonly --standalone — тот же флаг, добавьте его к той же команде). Проверить, что хук сохранился, можно, заглянув в файл /etc/letsencrypt/renewal/mail.вашдомен.ru.conf — там должна появиться строка renew_hook = systemctl reload postfix dovecot.

Проверка, что всё работает

Первым делом — статус сервисов:

systemctl status postfix
systemctl status dovecot

Для Postfix статус, скорее всего, покажет «active (exited)» — и это нормальное поведение, а не ошибка. Юнит postfix.service устроен как Type=oneshot с ExecStart=/bin/true — фактически заглушка, которая нужна только для интеграции с systemd. Реальные процессы (master и его потомки) поднимает и следит за ними отдельный внутренний инстанс-юнит, свой у каждого Postfix — его можно увидеть отдельной командой systemctl status 'postfix@*', там будет уже привычный «active (running)». Если решить, что «exited» у основного юнита значит «не работает», и начать перезапускать сервис по кругу — ничего не изменится, потому что реальная проблема, если она есть, не в этом. Dovecot в этом плане проще — у него обычный статус «active (running)».

Дальше проверяем, что порты реально слушаются:

ss -tlnp

После полной настройки в списке должны быть 25 и 587 (Postfix), а также 110, 143, 993 и 995 (Dovecot: POP3, IMAP и их защищённые версии).

Для теста отправки удобен инструмент swaks:

sudo apt install swaks
swaks --to user@домен --server 127.0.0.1

Если всё настроено верно, письмо должно появиться в ~/Maildir/new/ — файлом с именем вида <timestamp>.M<uniq>P<pid>.<hostname>,S=<size>,W=<size>. Параллельно стоит держать открытым лог:

tail -f /var/log/mail.log

При успешной доставке цепочка в логе выглядит так: postfix/smtpd принимает соединение, postfix/local определяет получателя, дальше строка о передаче на transport=lmtp, и в конце — запись самого Dovecot о том, что письмо сохранено в INBOX. Если цепочка обрывается на этапе LMTP с ошибкой про несуществующего пользователя — возвращайтесь к разделу про auth_username_format выше, это почти всегда она.

Что дальше

Сам сервер настроен и технически готов принимать и отправлять почту, но без DNS-записей внешние серверы его просто не найдут. Понадобится MX-запись, указывающая на этот сервер (с приоритетами, если серверов несколько), и SPF-запись, разрешающая ему отправлять почту от имени вашего домена. Оба этих типа записей и их синтаксис подробно разобраны в статье «Что такое ресурсные записи DNS» — повторять здесь не будем.

Дальше стоит настроить DKIM (цифровую подпись писем) и DMARC (политику, объясняющую принимающим серверам, что делать с письмами, которые не прошли SPF или DKIM) — без них письма от нового домена с высокой вероятностью будут улетать в спам, особенно у крупных провайдеров. И отдельная тема, которая раскрывается уже не за один день — репутация нового IP-адреса: у свежего сервера её попросту нет, и её наработка требует постепенного увеличения объёма отправки и мониторинга по чёрным спискам.

Похожее

Все статьи
Dkim + Dmarc (подпись и защита писем)

DKIM и DMARC на VPS: как настроить, чтобы письма не улетали в спам

Зачем DKIM и DMARC, если уже настроен SPF SPF отвечает на вопрос «с каких IP-адресов разрешено слать почту от имени домена». Это только часть защиты. Почтовый сервер может проверить SPF и пропустить письмо, но у него нет способа убедиться, что…