Postfix и Dovecot. Установка и настройка. Часть 2 из 2

25.01.2025

Теги: LinuxКлиентКонфигурацияНастройкаПочтаСервер

Установка IMAP/POP3 сервера Dovecot

Здесь все просто — нужно установить два пакета

$ apt install dovecot-imapd dovecot-pop3d

Пакеты dovecot-lmtpd и dovecot-submissiond

Пакет dovecot-lmtpd обычно не нужно устанавливать. Потому что Postfix может передавать сообщения Dovecot по протоколу LMTP через unix-сокет. Необходимость в LMTP демоне возникает, если Postfix и Dovecot запущены на разных хостах и передача возможна только по сети.

# файл конфигурации /ect/dovecot/dovecot.conf
protocols = imap pop3 lmtp
# файл конфигурации /ect/dovecot/conf.d/10-master.conf
service lmtp {
    inet_listener lmtp { # требуется установка пакета dovecot-lmtpd
        address = 127.0.0.1
        port = 24
    }
}

Пакет dovecot-submissiond добавляет SMTP-клиента, что позволяет Dovecot отправлять почтовые сообщения через внешний relay-сервер.

# файл конфигурации /ect/dovecot/dovecot.conf
protocols = imap pop3 submission
# файл конфигурации /ect/dovecot/conf.d/20-submission.conf
hostname = mail.example.com
submission_relay_host = 127.0.0.1
submission_relay_port = 25
submission_relay_trusted = yes

Пакет dovecot-submissiond обычно не нужно устанавливать. Потому что почтовые клиенты могут отправлять сообщения Postfix демону submission(s) напрямую. Однако, Dovecot может выполнить аутентификацию клиента, после чего проксировать SMTP-соединение к Postfix.

# файл конфигурации /etc/dovecot/conf.d/10-master.conf
service submission-login {
    inet_listener submission {
        port = 587
    }
    inet_listener submissions {
        port = 465
        ssl = yes # немедленное SSL-соединение (не STARTTLS)
    }
}

service submission {
    # требуется установка пакета dovecot-submissiond
}

Настройка сервера Dovecot

Главный файл конфигурации dovecot.conf

Главный файл конфигурации небольшой, потому что большинство опций содержатся в файлах конфигурации в директории /etc/dovecot/conf.d.

# nano /ect/dovecot/dovecot.conf
# Разрешить демону Dovecot работу по протоколам IPAM и POP3
protocols = imap pop3

# Какие сетевые интерфейсы прослушивать, через запятую. Символ *
# означает все IPv4 интерфейсы, :: означает все IPv6 интерфейсы.
listen = 127.0.0.1, 222.222.222.222

# Директория для хранения данных во время работы службы Dovecot
base_dir = /var/run/dovecot/

# Текст сообщения для приветствия клиентов, которые подключаются
login_greeting = Dovecot ready

# Завершать дочерние процессы при завершении master процесса
shutdown_clients = yes

# Имя unix-сокета или host:port для соединения с демоном Dovecot
doveadm_socket_path = doveadm-server

# Список переменных среды, разделенных пробелами, которые Dovecot
# сохраняет при запуске и передает всем дочерним процессам. Также
# можно передать переменные в виде key=value.
import_environment = TZ

# Подключить файлы конфигурации из директории /etc/dovecot/conf.d
!include conf.d/*.conf

Директива protocols может содержать одно или несколько значений — протоколы, которые должны быть задействованы. Нам достаточно указать протоколы imap и pop3. Разрешать lmtp можно только после установки пакета dovecot-lmtpd. Разрешать submission можно только после установки пакета dovecot-submissiond. Мы уже говорили выше, что пакеты dovecot-lmtpd и dovecot-submissiond обычно не нужны.

  • imap — протокол IMAP доступа к почтовым ящикам, пакет dovecot-imapd
  • pop3 — протокол POP3 доступа к почтовым ящикам, пакет dovecot-pop3d
  • lmtp — протокол локальной доставки почты LMTP, пакет dovecot-lmtpd
  • submission — MSA (Message Submission Agent), пакет dovecot-submissiond
Чаще всего директива protocols не нужна. При установке пакетов dovecot-imapd, dovecot-pop3d, dovecot-lmtpd, dovecot-submissiond — в директории /usr/share/dovecot/protocols.d создаются файлы конфигурации imapd.protocol, pop3d.protocol, lmtpd.protocol, submissiond.protocol. И все эти файлы подключаются в файле /etc/dovecot/dovecot.conf с помощью include_try.

Запись логов и debug-сообщений

Теперь настроим запись сообщений в отдельный лог-файл вместо syslog и добавим запись отладочных сообщений в debug-файл.

# nano /ect/dovecot/conf.d/10-logging.conf
# По умолчанию опция имеет значение syslog, то есть все сообщения
# записываются в системный лог-файл. Мы будем записывать сообщения
# в отдельный лог-файл.
log_path = /var/log/dovecot.log
# На этапе настройки Dovecot будем записывать debug-сообщения в
# отдельный debug-файл, который потом нужно будет отключить.
debug_log_path = /var/log/dovecot-debug.log
 
# Задаем уровень подробности для записи в лог-файл + уровень
# подробности для записи в debug-файл на этапе отладки 
auth_verbose = yes
auth_verbose_passwords = no 
auth_debug = yes
auth_debug_passwords = yes
mail_debug = yes
verbose_ssl = yes

Ротация логов почтового сервера

Настроим ротацию логов Dovecot, для этого создадим файл конфигурации службы logrotate.service

# nano /etc/logrotate.d/dovecot
/var/log/dovecot*.log {
    rotate 8 
    missingok
    notifempty
    delaycompress
    sharedscripts
    postrotate
        doveadm log reopen
    endscript
}

Формат и расположение почтовых ящиков

Укажем в файле конфигурации conf.d/10-mail.conf расположение и формат почтовых ящиков пользователей. Мы уже задавали расположение и формат ящиков в файле конфигурации Postfix — здесь нужно указать такие же значения для согласованной работы.

# nano /etc/dovecot/conf.d/10-mail.conf
# Для системных пользователей
mail_location = maildir:/var/mail/%n
# Для виртуальных пользователей, только один домен
#mail_location = maildir:/var/vmail/%n
# Для виртуальных пользователей, несколько доменов
#mail_location = maildir:/var/vmail/%d/%n

Папки в почтовом ящике клиента

Чтобы Dovecot автоматически создавал стандартный набор папок (Входящие, Отправленные, Черновики, Спам, Корзина) при первом доступе к почтовому ящику или при первой доставке письма — редактируем файлы конфигурации conf.d/10-mail.conf и conf.d/15-mailboxes.conf.

# nano /etc/dovecot/conf.d/10-mail.conf
namespace inbox {
    # Тип пространства имен — личное для каждого пользователя
    type = private
    # Для создания иерархии папок можно в качестве разделителя
    # использовать слэш или точку, project/one или project.one
    separator = /
    # Префикс для этого пространства имен (пустой для inbox)
    prefix =
    # Указываем, что INBOX (то есть Входящие) находится здесь
    inbox = yes
}
# nano /etc/dovecot/conf.d/15-mailboxes.conf
namespace inbox {
    # Основная конфигурация namespace inbox (type, location, separator, inbox)
    # должна быть заранее определена в файле конфигурации conf.d/10-mail.conf

    mailbox Drafts { # Черновики
        auto = subscribe
        special_use = \Drafts
    }
    mailbox Junk { # Спам
        auto = subscribe
        special_use = \Junk
    }
    mailbox Trash { # Корзина
        auto = subscribe
        special_use = \Trash
    }
    mailbox Sent { # Отправленные
        auto = subscribe
        special_use = \Sent
    }
}

Директива auto предписывает Dovecot предпринять автоматические действия в отношении нового почтового ящика. Значение subscribe создает (create) папку и подписывает на нее пользователя. В IMAP «подписка» означает, что папка будет в списке, который сервер вернет по команде LSUB. Без подписки папка будет только в списке, которую сервер вертнет по команде LIST.

При первом доступе к почтовому ящику или при первой доставке письма — Dovecot создает директорию /var/vmail/domain/username, внутри нее — директории new, cur и tmp + скрытые директории .Drafts, .Junk, .Trash, .Sent + директории new, cur и tmp внутри каждой скрытой директории.

Традиционный формат Maildir подразумевает создание директорий new, cur и tmp в корне почтового ящика. Такую структуру понимают все приложения, которым нужно работать с почтовым ящиком формата Maildir. Корневая директория почтового ящика формата Maildir считается папкой «Входящие» (или INBOX). Для удобства пользователя Dovecot дополнительно создает в корневой директории скрытые директории .Drafts, .Junk, .Trash, .Sent. Если с почтовым ящиком будет работать Postfix — для него эти директории не существуют, он работает только с new, cur и tmp. При этом, внутри .Drafts, .Junk, .Trash, .Sent — Dovecot создает директории new, cur и tmp, то есть каждая из них тоже является почтовым ящиком формата Maildir.

Ограничение на размер почтового ящика

В Postfix за максимальный размер почтового ящика отвечают директивы mailbox_size_limit (для системных пользователей) и virtual_mailbox_limit (для виртуальных пользователей). Postfix информирует отправителя о превышении квоты с помощью NDR (Non-Delivery Report), но не информирует получателя (локального пользователя) — просто не доставляет сообщение.

Можно отключить проверку размера почтового ящика — тогда Postfix агенты local и virtual будут доставлять сообщения без ограничений.

# Нет ограничений на размер почтовых ящиков
mailbox_size_limit = 0
virtual_mailbox_limit = 0

Возможны два варианта совместной работы Postfix и Dovecot. Первый — доставкой сообщений занимаются Postfix агенты local и virtual, Dovecot только предоставляет доступ к ящикам по IMAP/POP3 протоколу. Второй — Postfix не занимается доставкой почты, а отправляет сообщения Dovecot по протоколу LMTP для конечной доставки. Во втором случае никаких проверок со стороны Postfix не будет, это зона ответственности Dovecot.

# nano /etc/dovecot/conf.d/10-mail.conf
# Список всех плагинов, которые загружаются и доступны для использования
mail_plugins = $mail_plugins quota
# nano /etc/dovecot/conf.d/20-lmtp.conf
# Список всех плагинов, которые должны быть доступны для протокола LMTP
protocol lmtp {
    mail_plugins = $mail_plugins quota
}
# nano /etc/dovecot/conf.d/20-imap.conf
# Список всех плагинов, которые должны быть доступны для протокола IMAP
protocol imap {
    # Плагин imap_quota — обертка для плагина quota под протокол IMAP
    mail_plugins = $mail_plugins quota imap_quota
}
# nano /etc/dovecot/conf.d/20-pop3.conf
# Список всех плагинов, которые должны быть доступны для протокола POP3
protocol pop3 {
    mail_plugins = $mail_plugins quota
}
# nano /etc/dovecot/conf.d/90-quota.conf
plugin {
    # Драйвер квоты, для Maildir обычно используется maildir
    # (служебный файл maildirsize в каждом почтовом ящике)
    quota = maildir:User quota
    # Квота на размер почтового ящика для всех пользователей,
    # можно потом переопределить в файле passwd-file
    quota_rule = *:storage=500M
    # Максимальное количество сообщений в почтовом ящике
    quota_rule2 = *:messages=10000
    # Предупреждение, которое Dovecot демон lmtpd отправит
    # Postfix, когда почтовый ящик заполнен полностью
    quota_exceeded_message = Mailbox full, try again later.
    # Максимальный размер сообщения, которое можно сохранять
    quota_max_mail_size = 15M
}

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

# nano /etc/dovecot/conf.d/90-quota.conf
plugin {
    ..........
    # Предупреждение об использовании места в процентах от максимума
    quota_warning = storage=80%% quota-warning 80 %u storage %d
    quota_warning2 = storage=90%% quota-warning 90 %u storage %d
    # Предупреждение о количестве сообщений в процентах от максимума
    quota_warning3 = messages=80%% quota-warning 80 %u messages %d
    quota_warning4 = messages=90%% quota-warning 90 %u messages %d
}

service quota-warning {
    executable = script /usr/local/bin/quota-warning.sh
    user = dovecot
    unix_listener quota-warning {
        user = vmail
    }
}
# nano /usr/local/bin/quota-warning.sh
#!/bin/bash

PERCENT="$1"
EMAIL="$2"
QTYPE="$3"
DOMAIN="$4" 

cat << EOF | /usr/sbin/sendmail -t
To: $EMAIL
From: postmaster@$DOMAIN
Subject: Mailbox Quota Warning ($QTYPE)
Content-Type: text/plain; charset=UTF-8

Your mailbox is currently ${PERCENT}% full (quota type: ${QTYPE}).
EOF

if [ $? -eq 0 ]; then
    logger -t quota-warning "SUCCESS: Quota warning sent to ${EMAIL} (type ${QTYPE})"
else
    logger -t quota-warning "ERROR: Failed quota warning to ${EMAIL} (type ${QTYPE})"
fi
# chmod +x /usr/local/bin/quota-warning.sh

Шифрование TLS/SSL при передаче

Чтобы защитить данные, которые передаются между клиентом и сервером — нужно добавить шифрование (особенно при использовании аутентификации PLAIN и LOGIN). Сейчас мы разрешаем передачу как с использованием шифрования, так и без — но это временно, пока не завершим настройку Dovecot.

# nano /etc/dovecot/conf.d/10-ssl.conf
# Может принимать значения: required — шифрование обязательно,
# no — без шифрования, yes — допускаются оба варианта
ssl = yes

ssl_cert = </etc/letsencrypt/live/mail.example.com/fullchain.pem
ssl_key = </etc/letsencrypt/live/mail.example.com/privkey.pem

# Использовать только современные протоколы шифрования
ssl_min_protocol = TLSv1.2
ssl_cipher_list = HIGH:!aNULL:!MD5:!RC4

# Рекомендуется для улучшения Perfect Forward Secrecy,
# sudo openssl dhparam -out /etc/dovecot/dh.pem 4096
ssl_dh = </etc/dovecot/dh.pem

Демон master и подчиненные демоны

Главный демон master может запускать подчиненных демонов, если это разрешено в директиве protocols, настройки запуска собраны в файле конфигурации conf.d/10-master.conf. При этом, даже если поддержка протокола включена в опции protocols, здесь можно запретить подчиненному демону прослушивать порт, если установить port=0.

# nano /etc/dovecot/conf.d/10-master.conf
service imap-login {
    client_limit = 25 # количество клиентов на один процесс
    process_limit = 50 # ограничение на количество процессов
    process_min_avail = 2 # всегда два процесса в режиме ожидания
    vsz_limit = 64M # ограничение на использования памяти
    
    inet_listener imap {
        port = 143 # обычный IMAP, но ssl=required требует STARTTLS
    }
    inet_listener imaps {
        port = 993
        ssl = yes # немедленное SSL-соединение (не STARTTLS)
    }
}

service pop3-login {
    client_limit = 15 # количество клиентов на один процесс
    process_limit = 30 # ограничение на количество процессов
    process_min_avail = 1 # всегда один процесс в режиме ожидания
    vsz_limit = 64M # ограничение на использования памяти
    
    inet_listener pop3 {
        port = 110 # обычный POP3, но ssl=required требует STARTTLS
    }
    inet_listener pop3s {
        port = 995
        ssl = yes # немедленное SSL-соединение (не STARTTLS)
    }
}

# Postfix будет отправлять Docecot сообщения для локальных пользователей
service lmtp {
    unix_listener /var/spool/postfix/private/dovecot-lmtp {
        mode = 0660
        user = postfix
        group = postfix
    }
}

service imap {
    service_count = 1 # процесс завершается после обслуживания одного клиента
    client_limit = 1 # процесс обслуживает одновременно только одного клиента
    process_limit = 100 # максимум 100 одновременных IMAP-соединений
    process_min_avail = 2 # поддерживать два процесса в режиме ожидания
    vsz_limit = 256M # ограничить использование памяти службой 256 МБ
}

service pop3 {
    service_count = 1 # процесс завершается после обслуживания одного клиента
    client_limit = 1 # процесс обслуживает одновременно только одного клиента
    process_limit = 50 # максимум 50 одновременных POP3-соединений
    process_min_avail = 1 # поддерживать один процесс в режиме ожидания
    vsz_limit = 128M # ограничить использование памяти службой 128 МБ
}

service auth {
    # Внутренняя аутентификация IMAP/POP3-клиентов по умолчанию
    unix_listener auth-userdb {
        # права доступа, владелец и группа unix-сокета
        mode = 0660
        user = dovecot
        group = dovecot
    }
}

# Эти демоны нам не нужны, потому что запущены аналогичные демоны Postfix

#service submission-login {
#    client_limit = 25
#    process_limit = 50
#    process_min_avail = 2
#    vsz_limit = 64M
#
#    inet_listener submission {
#        port = 587
#    }
#    inet_listener submissions {
#        port = 465
#        ssl = yes # немедленное SSL-соединение (не STARTTLS)
#    }
#}

#service submission {
#    service_count = 1
#    client_limit = 1
#    process_limit = 50
#    process_min_avail = 1
#    vsz_limit = 128M
#}

# Вспомогательный процесс Dovecot, используемый для выполнения операций аутентификации,
# которые требуют повышенных привилегий — например, для чтения файла /etc/shadow, если
# используется системная аутентификация (auth_mechanism=system). Основной процесс auth
# работает с пониженными привилегиями и не имеет доступа ко всем чувствительным файлам.
# Чтобы повысить безопасность, он делегирует опасные действия дочернему процессу.
service auth-worker {
    user = root
}
Директива service_count устанавливает количество клиентских подключений, которые обработает процесс, после чего будет принудительно завершен. Директива client_limit устанавливает максимальное количество одновременных клиентских соединений на один процесс. Директива process_limit устанавливает максимальное количество процессов, которое может быть одновременно запущенными для данной службы. Директива process_min_avail устанавливает минимальное количество процессов, которые всегда должны быть доступны для принятия новых соединений.

Демон imap-login отвечает за процесс аутентификации. Когда IMAP-клиент подключается к серверу, сначала происходит соединение с демоном imap-login. Демон imap отвечает за обработку команд IMAP уже после успешной аутентификации пользователя. Аналогичная ситуация с демонами pop3-login и pop3.

Утилита конфигурации doveconf

Позволяет посмотреть значения директив конфигурации — как установленные по умолчанию, так и заданные явно через файлы конфигурации.

# doveconf [-adnNx] настройка

Некоторые опции утилиты

  • -a — показать все настройки с текущими значениями
  • -d — показать значения по умолчанию для всех настроек
  • -n — только настройки, отличные от значений по умолчанию
  • -N — настройки non-default + явно установленные в default
  • -x — значения настроек после подстановки переменных

Утилита управления doveadm

Предназначена для администрирования различных аспектов сервера Dovecot, а также для управления почтовыми ящиками пользователей.

Перезагрузить конфигурацию Dovecot

# doveadm reload

Показать, кто подключен к серверу

# doveadm who

Протестировать аутентификацию для пользователя evgeniy@example.com

# doveadm auth test evgeniy@example.com
Password: qwerty
passdb: evgeniy@example.com auth succeeded
extra fields:
  user=evgeniy@example.com

Показать информацию о пользователе evgeniy@example.com из userdb

# doveadm user evgeniy@example.com
field    value
uid      1001
gid      1001
home
mail     maildir:/var/mail/evgeniy

Проверить аутентификацию и показать информацию о пользователе

# doveadm auth login evgeniy@example.com
Password: qwerty
passdb: evgeniy@example.com auth succeeded
extra fields:
  user=evgeniy@example.com
userdb extra fields:
  evgeniy@example.com
  mail=maildir:/var/mail/evgeniy
  uid=1000
  gid=1000
  auth_mech=PLAIN

Сгенерировать хэш пароля с использованием алгоритма CRYPT

# doveadm pw -s CRYPT -p qwerty
{CRYPT}$2y$05$mFXLr0NO1w0fVwZbEdXFP.KKu28fIy7HtQjfSTvZPsV1kjOJfvXAq

Найти все письма в папке INBOX пользователя evgeniy@example.com

# doveadm search -u evgeniy@example.com mailbox INBOX
e7cca41bf10a4c6864f200003b8ccfba 1
e7cca41bf10a4c6864f200003b8ccfba 2
e7cca41bf10a4c6864f200003b8ccfba 3
e7cca41bf10a4c6864f200003b8ccfba 4

Удалить все письма во всех папках пользователя evgeniy@example.com

# doveadm expunge -u evgeniy@example.com mailbox '*' all

Удалить все письма из папки Trash (Корзина) пользователя evgeniy@example.com

# doveadm expunge -u evgeniy@example.com mailbox Trash all

Удалить все письма из папки Junk (Спам) пользователя evgeniy@example.com

# doveadm expunge -u evgeniy@example.com mailbox Junk all

Удалить все письма из всех папок пользователя evgeniy@example.com

# doveadm expunge -u evgeniy@example.com mailbox '*' all

Проверить все папки пользователя evgeniy@example.com

# doveadm mailbox status -u evgeniy@example.com messages '*'
Junk messages=0
Trash messages=5
Sent messages=2
Drafts messages=0
INBOX messages=0

Установить флаг Deleted для писем в папке Trash (Корзина)

# doveadm flags add -u evgeniy@example.com '\Deleted' mailbox Trash ALL

Сбросить флаг Deleted для писем в папке Trash (Корзина)

# doveadm flags remove -u evgeniy@example.com '\Deleted' mailbox Trash ALL

Пересчитать квоту для пользователя evgeniy@example.com

# doveadm quota recalc -u evgeniy@example.com

Текущее использование квоты пользователем

# doveadm quota get -u evgeniy@example.com
Quota name Type    Value   Limit   %
------------------------------------
User quota STORAGE     0   20480   0  # квота 20 Мбайт, использовано 0%
User quota MESSAGE     0      10   0  # квота 10 писем, использовано 0%

Аутентификация IMAP/POP3-клиентов

Настройка хранения паролей в файле

Для аутентификации клиента демон imap-login обращается к unix-сокету, заданному опцией auth_socket_path (по умолчанию /var/run/dovecot/auth-userdb). Как при этом будет проверяться логин-пароль клиента — задается в файле конфигурации conf.d/10-auth.conf.

# nano /etc/dovecot/conf.d/10-auth.conf
# Временно разрешаем использовать механизмы PLAIN/LOGIN без TLS/SSL
# (значение yes запрещает использование PLAIN/LOGIN без TLS/SSL)
disable_plaintext_auth = no

# Добавлять доменное имя к логину клиента, который не имеет доменной
# части, то есть преобразовать «username» в «username@example.com»
auth_default_realm = example.com

# Список область аутентификации (REALMS), которые сервер объявляет
# для SASL-механизмов, которые этого требуют (DIGEST-MD5, CRAM-MD5)
auth_realms = example.com

# Какие механизмы аутентификации будут доступны IMAP/POP3-клиентам
auth_mechanisms = plain login digest-md5 cram-md5

# Преобразовать логин клиента перед поиском в базе passdb/userdb. Это
# происходит после того, как auth_default_realm мог добавить домен
auth_username_format = %Lu

# Задержка перед отправкой ответа на неудачную попытку аутентификации
auth_failure_delay = 10 secs

# Для хранения логинов и паролей клиентов будем использовать файл
!include auth-passwdfile.conf.ext
#!include auth-system.conf.ext
#!include auth-sql.conf.ext
#!include auth-ldap.conf.ext
#!include auth-checkpassword.conf.ext
#!include auth-static.conf.ext

Далее нужно отредактировать файл конфигурации auth-passwdfile.conf.ext, который мы подключили с помощью директивы !include.

# nano /etc/dovecot/conf.d/auth-passwdfile.conf.ext
passdb {
    driver = passwd-file
    # Если пароль сохранен в файле в открытом виде без шифрования
    args = scheme=PLAIN username_format=%u /etc/dovecot/users
    # Если пароль сохранен в файле с предварительным шифрованием
    #args = scheme=MD5-CRYPT username_format=%u /etc/dovecot/users
}
userdb {
    driver = passwd-file
    args = username_format=%u /etc/dovecot/users
    # В файле passwd-file может не быть данных о формате и расположении
    # почтовых ящиков, поэтому указываем значения по умолчанию
    default_fields = mail=maildir:/var/mail/%n
}

Имя пользователя в файле /etc/dovecot/users может быть просто username — тогда username_format должен иметь значение %n. Или имя может включать домен username@example.com — тогда username_format должен иметь значение %u или %n@%d. Тут важно обратить внимание на директиву конфигурации auth_default_realm — будет Dovecot дополнять username до username@example.com или нет.

Dovecot использует базу passdb для аутентификации пользователя, а базу userdb — для получения доп.информации (например, место расположения почтового ящика, ограничения на размер почтового ящика, UID/GID от имени которых будет сохранено сообщение).

Создание файла с логинами и паролями

Пароль может быть сохранен в файл /etc/dovecot/users открытым текстом или с предварительным шифрованием

# doveadm pw -s PLAIN
Enter new password: qwerty
Retype new password: qwerty
{PLAIN}qwerty
# nano /etc/dovecot/users
evgeniy@example.com:{PLAIN}qwerty:1000:1000
sergey@example.com:{PLAIN}123456:1001:1001

Пример шифрования паролей с использованием алгоритма MD5-CRYPT перед записью в файл /etc/dovecot/users.

# doveadm pw -s MD5-CRYPT
Enter new password: qwerty
Retype new password: qwerty
{MD5-CRYPT}$1$Z3x1LKRj$6d2rzY3qMq3iAdS7ByJ5B.
# nano /etc/dovecot/users
evgeniy@example.com:{MD5-CRYPT}$1$Z3x1LKRj$6d2rzY3qMq3iAdS7ByJ5B.:1000:1000
sergey@example.com:{MD5-CRYPT}$1$2cjn0CEK$y3NI5c3o77tDLwlfnOTk9.:1001:1001

Владелец и права доступа для файла паролей

# chown dovecot:dovecot /etc/dovecot/users
# chmod 660 /etc/dovecot/users

Если клиент и сервер для аутентификации используют CRAM-MD5 или DIGEST-MD5 — сервер должен иметь доступ к паролю, чтобы вычислить md5-сумму — и зашифрованный пароль для этого не подойдет. Так что в этом случае пароль на сервере нужно хранить в откытом виде — но по сети пароль не передается. Если клиент и сервер для аутентификации используют PLAIN или LOGIN — пароль на сервере можно хранить в зашифрованном виде. Но от клиента к серверу пароль будет передаваться в открытом виде, без шифрования.

Механизмы аутентификации и схемы паролей часто путают, поскольку они имеют несколько схожие значения. Например, есть механизм аутентификации PLAIN и схема пароля PLAIN. Но они означают совершенно разные вещи.

Механизм аутентификации — это клиент-серверный протокол. Он касается того, как клиент и сервер общаются друг с другом для выполнения аутентификации. Многие используют только аутентификацию PLAIN, что в основном означает, что имя пользователя и пароль отправляются на сервер без какого-либо шифрования.

Схема паролей — это то, как пароль хэшируется в базе данных для хранения. При использовании схемы PLAIN, пароли пользователей хранятся в открытом виде без какого-либо хэширования в базе данных паролей.

Хранение паролей в файле passwd-file

Директива passdb определяет механизм аутентификации пользователей, т.е. как Dovecot будет проверять имя пользователя и пароль.

  • passwd — для аутентификации пользователей из системного файла /etc/passwd
  • passwd-file — для аутентификации пользователей из произвольного файла в формате username:password
  • sql — для аутентификации пользователей, выполняя запросы к базе данных для проверки учетных данных
  • ldap — для аутентификации пользователей через LDAP-сервер, часто используется в корпоративной среде

Директива userdb определяет, где Dovecot будет искать информацию о пользователе, такую как м расположение и формат почтового ящика, UID и GID, квота, алиасы и прочие атрибуты.

  • passwd — получение информации о пользователе из системного файла /etc/passwd
  • passwd-file — получение информации из произвольного файла, имеющего формат, как у /etc/passwd
  • sql — получение дополнительное информации о пользователе через запрос к базе данных
  • ldap — получение информации о пользователе через LDAP-сервер, часто используется в корпоративной среде

Пример файла конфигурации conf.d/auth-passwdfile.conf.ext, формат логина username, пароли без шифрования, доп.информация из файла /etc/passwd. В файле /etc/passwd нет данных о формате и расположении почтовых ящиков пользователей, поэтому указываем эти данные в опции default_fields.

passdb {
    driver = passwd-file
    args = scheme=PLAIN username_format=%n /etc/dovecot/users
}
userdb {
    driver = passwd
    args = blocking=no
    default_fields = mail=maildir:/var/mail/%n
}

Следует быть осторожным при использовании драйвера passwd. Если логин имеет вид username@example.com — пользователь не будет найден в файле /etc/passwd. И указание формата username_format=%n не действует для драйвера passwd.

Директива driver может еще принимать значение static для userdb. В этом случае всю доп.информацию Dovecot получает прямо из файла конфигурации, из директивы args.

passdb {
    driver = passwd-file
    args = scheme=PLAIN username_format=%n@%d /etc/dovecot/users
}
userdb {
    driver = static
    args = uid=vmail gid=vmail mail=maildir:/var/vmail/%d/%n quota_rule=*:storage=100M
}

Если для driver используется значение passwd-file — дополнительную информацию Dovecot получает из указанного файла, который имеет приведенный ниже формат.

username:password:uid:gid:gecos:home:shell:extra_fields
evgeniy@example.com:{plain}qwerty:1000:1000::::userdb_mail=maildir:/var/mail/evgeniy userdb_quota_rule=*:storage=200M
sergey@example.com:{plain}123456:1001:1001::::userdb_mail=maildir:/var/mail/sergey userdb_quota_rule=*:storage=100M

Значение passwd-file для опции driver позволяет задать для каждого пользователя свой набор extra_fields. Кроме того, можно установить значения по умолчанию + переопределять значения из файла.

passdb {
    driver = passwd-file
    args = scheme=PLAIN username_format=%u /etc/dovecot/users
}
userdb {
    driver = passwd-file
    args = username_format=%u /etc/dovecot/users
    # Значения по умолчанию для полей, которые не указаны явно в файле /etc/dovecot/users
    default_fields = quota_rule=*:storage=100M
    # Принудительно переопределять эти поля, даже если они определены в /etc/dovecot/users
    override_fields = mail=/var/mail/%n
}

При использовании extra_fields можно задать userdb_home (домашняя директория пользователя) и userdb_mail (формат и путь к почтовому ящику). Если задано значение userdb_mail, то userdb_home не используется. Если userdb_mail не задано явно, то для определения формата и пути используются mail_location и userdb_home. Предпочтительно явно указывать userdb_mail для полного контроля.

Если в файле конфигурации есть несколько блоков passdb и/или userdb — то они обрабатываются все последовательно. Когда найдено совпадение — следующие блоки могут быть пропущены, это задается директивой skip. Но для блоков passdb и/или userdb — директива действует по разному и может принимать разные значения (см. здесь и здесь). Проверить это можно с помощью утилиты doveadm и просматривая логи с включенным режимом debug.

Проверка аутентификации IMAP клиента

Проверка IMAP-аутентификации с использованием LOGIN

$ telnet mail.example.com 143
Trying 222.222.222.222...
Connected to mail.example.com.
Escape character is '^]'.
* OK [CAPABILITY ... STARTTLS AUTH=PLAIN AUTH=LOGIN AUTH=DIGEST-MD5 AUTH=CRAM-MD5] Dovecot (Ubuntu) ready.
A1 LOGIN evgeniy@example.com qwerty
A1 OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SORT SORT=DISPLAY ...] Logged in
A2 LOGOUT
* BYE Logging out
A2 OK Logout completed (0.001 + 0.000 secs).
Connection closed by foreign host.

Проверка IMAP-аутентификации с использованием PLAIN

$ echo -ne '\0evgeniy\0qwerty' | base64
AGV2Z2VuaXkAcXdlcnR5
$ telnet mail.example.com 143
Trying 222.222.222.222...
Connected to mail.example.com.
Escape character is '^]'.
* OK [CAPABILITY ... STARTTLS AUTH=PLAIN AUTH=LOGIN AUTH=DIGEST-MD5 AUTH=CRAM-MD5] Dovecot (Ubuntu) ready.
A1 AUTHENTICATE PLAIN AGV2Z2VuaXkAcXdlcnR5
A1 OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SORT SORT=DISPLAY ...] Logged in
A2 LOGOUT
* BYE Logging out
A2 OK Logout completed (0.001 + 0.000 secs).
Connection closed by foreign host.

Проверка IMAP-аутентификации с использованием CRAM-MD5

$ telnet mail.example.com 143 # первый терминал
Trying 222.222.222.222...
Connected to mail.example.com.
Escape character is '^]'.
* OK [CAPABILITY ... STARTTLS AUTH=PLAIN AUTH=LOGIN AUTH=DIGEST-MD5 AUTH=CRAM-MD5] Dovecot (Ubuntu) ready.
A1 AUTHENTICATE CRAM-MD5
+ PDIyNDU5OT.....VuaXkzNDU+ # сообщение от сервера
ZXZnZW5peS.....VhOGMwYg== # наш ответ серверу
A1 OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SORT SORT=DISPLAY ...] Logged in
A2 LOGOUT
* BYE Logging out
A2 OK Logout completed (0.001 + 0.000 secs).
Connection closed by foreign host.
$ /path/to/cram-md5.sh evgeniy qwerty PDIyNDU5OT.....VuaXkzNDU+ # второй терминал
ZXZnZW5peS.....VhOGMwYg== # наш ответ серверу

Скрипт для формирования ответа почтовому серверу

#!/bin/bash

USERNAME=$1
PASSWORD=$2
CHALLENGE=$3

CHALLENGE=$(echo $CHALLENGE | base64 -d)
HMAC_HEX=$(printf '%s' "$CHALLENGE" | openssl dgst -md5 -mac HMAC -macopt key:$PASSWORD | awk '{print $2}')
RESPONSE="$USERNAME $HMAC_HEX"
RESPONSE=$(echo -n "$RESPONSE" | base64)

echo $RESPONSE

Проверка аутентификации POP3 клиента

Проверка POP3-аутентификации с использованием LOGIN

$ telnet mail.example.com 110
Trying 222.222.222.222...
Connected to mail.example.com.
Escape character is '^]'.
+OK Dovecot (Ubuntu) ready.
AUTH LOGIN
+ VXNlcm5hbWU6 # это запрос Username:
ZXZnZW5peQ== # наш ответ evgeniy
+ UGFzc3dvcmQ6 # это запрос Password:
cXdlcnR5 # наш ответ qwerty
+OK Logged in.
QUIT
+OK Logging out.
Connection closed by foreign host.

Проверка POP3-аутентификации с использованием PLAIN

$ echo -ne '\0evgeniy\0qwerty' | base64
AGV2Z2VuaXkAcXdlcnR5
$ telnet mail.example.com 110
Trying 222.222.222.222...
Connected to mail.example.com.
Escape character is '^]'.
+OK Dovecot (Ubuntu) ready.
AUTH PLAIN AGV2Z2VuaXkAcXdlcnR5
+OK Logged in.
QUIT
+OK Logging out.
Connection closed by foreign host.

Проверка POP3-аутентификации с использованием CRAM-MD5

$ telnet mail.example.com 110 # первый терминал
Trying 147.45.189.190...
Connected to mail.example.com.
Escape character is '^]'.
+OK Dovecot (Ubuntu) ready.
AUTH CRAM-MD5
+ PDY1ODQxOD.....VuaXkzNDU+ # сообщение от сервера
ZXZnZW5peS.....Q2Y2M2ZQ== # наш ответ серверу
+OK Logged in.
QUIT
+OK Logging out.
Connection closed by foreign host.
$ /path/to/cram-md5.sh evgeniy qwerty PDY1ODQxOD.....VuaXkzNDU+ # второй терминал
ZXZnZW5peS.....Q2Y2M2ZQ== # наш ответ серверу

Аутентификация SMTP-клиентов (Dovecot SASL)

Директива service auth задает unix-сокет, через который Dovecot проверяет логин-пароль IMAP/POP3-клиентов. Можно задать еще один unix-сокет, через который Postfix тоже сможет проверять логин-пароль SMTP-клиентов. При этом, список SMTP-клиентов может не совпадать со списком IMAP/POP3-клиентов (будет обращение к разным файлам логинов-паролей). Для начала у нас будет только один файл логинов-паролей, как для доступа к SMTP-серверу, так и для доступа к IMAP/POP3-серверу.

# nano /etc/dovecot/conf.d/10-master.conf
service auth { 
    # ..........
    unix_listener /var/spool/postfix/private/auth { 
        # права доступа, владелец и группа unix-сокета
        mode = 0660 
        user = postfix 
        group = postfix
    } 
    # ..........
} 

Теперь редактируем файл конфигурации Postfix

# nano /etc/postfix/main.cf
# АУТЕНТИФИКАЦИЯ КЛИЕНТОВ С ИСПОЛЬЗОВАНИЕМ DOVECOT SASL

# Включаем аутентификацию клиентов с использованием Cyrus или Dovecot
smtpd_sasl_auth_enable = yes
# Может принимать значения cyrus или dovecot, сейчас используем dovecot
smtpd_sasl_type = dovecot

# Путь к unix-сокету для связи Postfix с механизмом SASL-аутентификации
# Dovecot. Указывается относительно директории очередей $queue_directory,
# так что фактический путь будет /var/spool/postfix/private/auth
smtpd_sasl_path = private/auth
# Добавлять доменное имя к логину клиента, который не имеет доменной
# части, то есть преобразовать «username» в «username@example.com»
smtpd_sasl_local_domain = $mydomain
# Запрещаем анонимную аутентификацию клиентов на почтовом сервере
smtpd_sasl_security_options = noanonymous
Директива smtpd_sasl_local_domain по умолчанию имеет пустое значение. И это может создавать проблему для SASL-демона — как для Cyrus, так и для Dovecot. Нужно всегда задавать значение для этой директивы — и настроить Dovecot на использование логина с доменой частью.

# postconf -d smtpd_sasl_local_domain
smtpd_sasl_local_domain =
# файл конфигурации /etc/dovecot/conf.d/10-auth.conf
auth_username_format = %Lu

Если по каким-то причинам список SMTP-клиентов не совпадает со списком IMAP/POP3-клиентов — нужны два файла логинов и паролей.

service auth {
    # Внутренняя аутентификация IMAP/POP3-клиентов
    unix_listener auth-userdb {
        # права доступа, владелец и группа unix-сокета
        mode = 0660
        user = dovecot
        group = dovecot
    }

    # Аутентификация SMTP-клиентов для Postfix
    unix_listener /var/spool/postfix/private/auth {
        # права доступа, владелец и группа unix-сокета
        mode = 0660
        user = postfix
        group = postfix
    }
}
# Для IMAP/POP3-клиентов, аутентификация passdb + доп.данные userdb
passdb {
    driver = passwd-file
    args = scheme=PLAIN username_format=%u /etc/dovecot/imap_users
    service_name = imap pop3
}
userdb {
    driver = static
    args = uid=vmail gid=vmail mail=maildir:/var/vmail/%d/%n
    service_name = imap pop3
}

# Для SMTP-клиентов, обращение от Postfix для проверки логина-пароля
passdb {
    driver = passwd-file
    args = scheme=PLAIN username_format=%u /etc/dovecot/smtp_users
    service_name = smtp
}

Когда Postfix запрашивает аутентификацию через unix-сокет, он отправляет в этом запросе информацию о типе сервиса. В контексте протокола SASL, используемого между Postfix и Dovecot, эта информация передается как service=smtp. Это значение можно задать в файле конфигурации Postfix с помощью директивы smtpd_sasl_service.

Если от Dovecot нам нужна только проверка логинов и паролей SMTP-клиентов для Postfix, то нет необходимости устанавливать пакеты dovecot-imapd и dovecot-pop3d — достаточно будет установить пакет dovecot-core.

Проверка аутентификации SMTP-клиента

При использовании механизма аутентификации PLAIN

$ echo -ne '\0evgeniy\0qwerty' | base64
AGV2Z2VuaXkAcXdlcnR5
$ telnet mail.example.com 25
Trying 222.222.222.222...
Connected to mail.example.com.
Escape character is '^]'.
220 mail.example.com ESMTP Postfix
EHLO [101.101.101.101]
250-mail.example.com
250-PIPELINING
250-SIZE 15728640
250-ETRN
250-AUTH PLAIN LOGIN DIGEST-MD5 CRAM-MD5
250-ENHANCEDSTATUSCODES
250-8BITMIME
250-DSN
250-SMTPUTF8
250 CHUNKING
AUTH PLAIN AGV2Z2VuaXkAcXdlcnR5
235 2.7.0 Authentication successful
QUIT
221 2.0.0 Bye

При использовании механизма аутентификации CRAM-MD5

$ telnet mail.example.com 25 # первый терминал
Trying 222.222.222.222...
Connected to mail.example.com.
Escape character is '^]'.
220 mail.example.com ESMTP Postfix
EHLO [101.101.101.101]
250-mail.example.com
250-PIPELINING
250-SIZE 15728640
250-ETRN
250-AUTH PLAIN LOGIN DIGEST-MD5 CRAM-MD5
250-ENHANCEDSTATUSCODES
250-8BITMIME
250-DSN
250-SMTPUTF8
250 CHUNKING
AUTH CRAM-MD5
334 PDY1ODQxOD.....VuaXkzNDU+ # сообщение от сервера
ZXZnZW5peS.....Q2Y2M2ZQ== # наш ответ серверу
235 2.7.0 Authentication successful
QUIT
221 2.0.0 Bye
$ /path/to/cram-md5.sh evgeniy qwerty PDY1ODQxOD.....VuaXkzNDU+ # второй терминал
ZXZnZW5peS.....Q2Y2M2ZQ== # наш ответ серверу

Dovecot демоны LMTP и MSA

Dovecot демон LMTP

Postfix может сам доставлять почту для «своих» пользователей, используя транспорт local и virtual. А может отправлять их Dovecot по протоколу LMTP — через unix-сокет либо по сети. И тогда распределением сообщений по почтовым ящикам пользователей будет заниматься Dovecot — этот вариант предпочтительнее.

Через unix-сокет /var/spool/postfix/private/dovecot-lmtp — этот вариант доступен сразу после установки пакета dovecot-core

# файл конфигурации Postfix /etc/postfix/main.cf
local_transport = lmtp:unix:private/dovecot-lmtp
virtual_transport = lmtp:unix:private/dovecot-lmtp
# файл конфигурации Dovecot /etc/dovecot/conf.d/10-master.conf
service lmtp {
    unix_listener /var/spool/postfix/private/dovecot-lmtp {
        mode = 0660
        user = postfix
        group = postfix
    }
}

По сети, через интерфейс обратной петли — этот вариант доступен только после установки пакета dovecot-lmtpd

# файл конфигурации Postfix /etc/postfix/main.cf
local_transport = lmtp:[127.0.0.1]:24
virtual_transport = lmtp:[127.0.0.1]:24
# файл конфигурации Dovecot /etc/dovecot/dovecot.conf
protocols = imap pop3 lmtp
# файл конфигурации Dovecot /etc/dovecot/conf.d/10-master.conf
service lmtp {
    # требуется установка пакета dovecot-lmtpd
    inet_listener lmtp {
        address = 127.0.0.1
        port = 24
    }
}

Dovecot демон MSA

Dovecot не может выполнять роль MSA (Mail Submission Agent), потому что не является MTA (Mail Transport Agent). Но может выступать в качестве прокси-сервера, который передает сообщения от клиентов к Postfix. При этом Dovecot берет на себя задачу аутентификации почтовых клиентов, освобождая от этого Postfix.

Пакет dovecot-submissiond добавляет в Dovecot SMTP-клиента, который может отправлять сообщения через relay-сервер. Демон submission-login выполяняет аутентификацию почтового клиента, после чего передает SMTP-соединение демону submission. Dovecot демон submission проксирует SMTP-соединение к Postfix демону smtpd.

# apt install dovecot-submissiond
# файл конфигурации /etc/dovecot/dovecot.conf
protocols = imap pop3 submission
# файл конфигурации /etc/dovecot/conf.d/10-master.conf
service submission-login {
    inet_listener submission {
        port = 587
    }
    inet_listener submissions {
        port = 465
        ssl = yes # немедленное SSL-соединение (не STARTTLS)
    }
}

service submission {
    # требуется установка пакета dovecot-submissiond
}
# файл конфигурации /ect/dovecot/conf.d/20-submission.conf
hostname = mail.example.com
submission_relay_host = 127.0.0.1
submission_relay_port = 10025
submission_relay_trusted = yes

В файле конфигурации /etc/postfix/master.cf нужно создать демона, который будет принимать сообщения на tcp-сокете 127.0.0.1:10025.

Какие порты прослушивают Postfix и Dovecot

С помощью команды ss можно посмотреть, какие порты прослушивают Postfix и Dovecot.

$ ss -tpln | egrep 'master|dovecot'
State      Recv-Q     Send-Q     Local Address:Port      Peer Address:Port     Process
-------------------------------------------------------------------------------------------------------------------
LISTEN     0          100        127.0.0.1:24            0.0.0.0:*             users:(("dovecot",pid=231881,fd=32))
LISTEN     0          100        127.0.0.1:25            0.0.0.0:*             users:(("master",pid=194199,fd=14))
LISTEN     0          100        127.0.0.1:110           0.0.0.0:*             users:(("dovecot",pid=194775,fd=23))
LISTEN     0          100        127.0.0.1:143           0.0.0.0:*             users:(("dovecot",pid=194775,fd=40))
LISTEN     0          100        127.0.0.1:465           0.0.0.0:*             users:(("master",pid=232598,fd=23))
LISTEN     0          100        127.0.0.1:587           0.0.0.0:*             users:(("master",pid=232598,fd=19)) 
LISTEN     0          100        127.0.0.1:993           0.0.0.0:*             users:(("dovecot",pid=194775,fd=42))
LISTEN     0          100        127.0.0.1:995           0.0.0.0:*             users:(("dovecot",pid=194775,fd=25))
LISTEN     0          100        222.222.222.222:25      0.0.0.0:*             users:(("master",pid=194199,fd=13))
LISTEN     0          100        222.222.222.222:110     0.0.0.0:*             users:(("dovecot",pid=194775,fd=24))
LISTEN     0          100        222.222.222.222:143     0.0.0.0:*             users:(("dovecot",pid=194775,fd=41))
LISTEN     0          100        222.222.222.222:465     0.0.0.0:*             users:(("master",pid=232186,fd=25))
LISTEN     0          100        222.222.222.222:587     0.0.0.0:*             users:(("master",pid=232186,fd=21))
LISTEN     0          100        222.222.222.222:993     0.0.0.0:*             users:(("dovecot",pid=194775,fd=43))
LISTEN     0          100        222.222.222.222:995     0.0.0.0:*             users:(("dovecot",pid=194775,fd=26))

Это абстрактный пример, но давайте разберем, какие службы запущены и на каких сетевых интерфейсах.

  1. Dovecot, сетевой интерфейс 127.0.0.1, протокол LMTP, порт 24
  2. Postfix, сетевой интерфейс 127.0.0.1, протокол SMTP, порт 25
  3. Dovecot, сетевой интерфейс 127.0.0.1, протокол POP3, порт 110
  4. Dovecot, сетевой интерфейс 127.0.0.1, протокол IMAP, порт 143
  5. Postfix, сетевой интерфейс 127.0.0.1, протокол SMTPS, порт 465 (для MUA)
  6. Postfix, сетевой интерфейс 127.0.0.1, протокол SMTP, порт 587 (для MUA)
  7. Dovecot, сетевой интерфейс 127.0.0.1, протокол IMAPS, порт 993
  8. Dovecot, сетевой интерфейс 127.0.0.1, протокол POP3S, порт 995
  9. Postfix, сетевой интерфейс 222.222.222.222, протокол SMTP, порт 25
  10. Dovecot, сетевой интерфейс 222.222.222.222, протокол POP3, порт 110
  11. Dovecot, сетевой интерфейс 222.222.222.222, протокол IMAP, порт 143
  12. Postfix, сетевой интерфейс 222.222.222.222, протокол SMTPS, порт 465 (для MUA)
  13. Postfix, сетевой интерфейс 222.222.222.222, протокол SMTP, порт 587 (для MUA)
  14. Dovecot, сетевой интерфейс 222.222.222.222, протокол IMAPS, порт 993
  15. Dovecot, сетевой интерфейс 222.222.222.222, протокол POP3S, порт 995

Резервное копирование почтовых ящиков

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

#!/bin/bash

SOURCE='/var/vmail'
BACKUP_ROOT='/backup/vmail'
TODAY=$(date +%F)
TODAY_BACKUP="$BACKUP_ROOT/backup-$TODAY"
YESTERDAY=$(date -d 'yesterday' +%F)
YESTERDAY_BACKUP="$BACKUP_ROOT/backup-$YESTERDAY"
LOG_FILE='/var/log/dovecot-backup.log'

TELEGRAM_API_KEY='.........'
TELEGRAM_CHAT_ID='.........'

function telegram {
    curl -s -X POST "https://api.telegram.org/bot$TELEGRAM_API_KEY/sendMessage" \
        -d chat_id="$TELEGRAM_CHAT_ID" \
        -d text="${1}" > /dev/null
}

echo "[$TODAY] Остановка Dovecot..." >> "$LOG_FILE"
systemctl stop dovecot

# Создание полного или инкрементного бэкапа
mkdir -p "${TODAY_BACKUP}"
if [ "$(date +%d)" == "01" ]; then
    BACKUP_TYPE='Полный (ежемесячный)'
    rsync -a "$SOURCE/" "$TODAY_BACKUP/" >> "$LOG_FILE" 2>&1
else
    if [ -d "$YESTERDAY_BACKUP" ]; then
        BACKUP_TYPE='Инкрементный'
        rsync -a --link-dest="$YESTERDAY_BACKUP" "$SOURCE/" "$TODAY_BACKUP/" >> "$LOG_FILE" 2>&1
    else
        BACKUP_TYPE='Полный (вынужденный)'
        rsync -a "$SOURCE/" "$TODAY_BACKUP/" >> "$LOG_FILE" 2>&1
    fi
fi

echo "[$TODAY] Запуск Dovecot..." >> "$LOG_FILE"
systemctl start dovecot

# Удаление старых бэкапов (старше 365 дней)
echo "[$TODAY] Удаление старых бэкапов..." >> "$LOG_FILE"
find "$BACKUP_ROOT" -maxdepth 1 -type d -name "backup-*" -mtime +365 -exec rm -rf {} \; >> "$LOG_FILE" 2>&1

# Подсчёт общего размера резервных копий
TOTAL_SIZE=$(du -sh "$BACKUP_ROOT" 2>/dev/null | awk '{print $1}')

# Подсчёт использования диска в процентах
DISK_USAGE=$(df --output=pcent "$BACKUP_ROOT" | tail -1 | tr -dc '0-9')

# Отправка отчёта по бэкапу в телеграм
REPORT_MSG="Бэкап Dovecot завершён:
Дата: $TODAY
Тип: $BACKUP_TYPE
Новый бэкап: $TODAY_BACKUP
Размер всех бэкапов: $TOTAL_SIZE
Раздел заполнен на: $DISK_USAGE%"

telegram "$REPORT_MSG"

LOG_MESSAGE="[$TODAY] Бэкап завершён: $BACKUP_TYPE."
LOG_MESSAGE="$LOG_MESSAGE Общий размер: $TOTAL_SIZE."
LOG_MESSAGE="$LOG_MESSAGE Заполнение раздела: $DISK_USAGE%"
echo "$LOG_MESSAGE" >> "$LOG_FILE"

Сервер для нескольких доменов

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

1. Домены виртуальных псевдонимов

По сути дела, конечными получателями почты будут пользователи операционной системы, как в самой простой конфигурации, которую мы рассмотрели в самом начале. Просто мы сможем создать дополнительные адреса почты на других доменах — и перенаправить сообщения для этих адресов существующим пользователям операционной системы. Пользователю sergey можно направить письмо на любой из этих адресов — sergey@example.com, sergey@example.net, sergey@example.org — хотя мне трудно представить, зачем такое может потребоваться.

Изменения в конфигурации Postfix

Давайте сначала посмотрим на фрагмент файла конфигурации — чтобы был перед глазами и было понятно, какие изменения нужны. Файл конфигурации отредактируем чуть позже, когда разберемся с новыми опциями.

myhostname = mail.example.com
mydomain = example.com
myorigin = $mydomain
mydestination = $mydomain, localhost

virtual_alias_domains = example.net, example.org
virtual_alias_maps = hash:/etc/postfix/virtual_alias_map

Обычно сервер Postfix идентифицирует себя как место конечного назначения только для доменов, имена которых определены в опции mydestination. Домены, перечисленные в опции mydestination, называются каноническими доменами, т.к. обычно они представляют собой имена сервера, где установлен Postfix (и иногда имя его родительского домена).

Postfix также идентифицирует себя как место конечного назначения для адресов в форме user@ip-address, где указан один из ip-адресов Postfix.

Нужно сообщить серверу Postfix, что он является местом конечного назначения для некоторого домена (или доменов) в дополнение к тем, которые перечислены в опции mydestination — для этого предназначена опция virtual_alias_domains. Потом создать файл карты для отображения адресов получателей нового домена (или доменов) на локальные адреса пользователей операционной системы — и указать эту карту в качестве значения опции virtual_alias_maps.

При указании домена в качестве значения опции virtual_alias_domains — нельзя использовать его же в качестве значения опции mydestination. Postfix не будет знать, как ему поступить — доставлять почту локально или же отправлять ее на виртуальное преобразование.

Из документации Postfix: NEVER list a virtual alias domain name as a mydestination domain!

Создаем файл карты для отображения адресов — фактически, это аналог файла /etc/aliases. При этом пользователя, которому изначально предназначено сообщение, может не существовать — например, abuse и postmaster.

# nano /etc/postfix/virtual_alias_map
# Виртуальный домен example.net
postmaster@example.net    evgeniy
abuse@example.net         evgeniy

ivanov@example.net        ivanov
petrov@example.net        petrov
smirnov@example.net       smirnov
sale@example.net          ivanov,petrov
it-dept@example.net       smirnov

# Виртуальный домен example.org
postmaster@example.org    evgeniy
abuse@example.org         evgeniy

ivanov@example.org        ivanov
petrov@example.org        petrov
smirnov@example.org       smirnov
sale@example.org          ivanov,petrov
it-dept@example.org       smirnov
# postmap hash:/etc/postfix/virtual_alias_map

Наконец, редактируем файл конфигурации /etc/postfix/main.cf

# nano /etc/postfix/main.cf
myhostname = mail.example.com
mydomain = example.com
myorigin = $mydomain
mydestination = localhost

virtual_alias_domains = example.net, example.org
virtual_alias_maps = hash:/etc/postfix/virtual_alias_map
# systemctl reload postfix.service  # перечитать файлы конфигурации

Сложные отображения

Чем больше доменов виртуальных псевдонимов — тем больше вероятность того, что придется добавлять в карту одинаковые записи. Здесь помогут универсальные адреса — заданные посредством регулярных выражений и соответствий в неявном виде.

Индексированная карта hash, есть канонический домен example.com и виртуальный домен example.net

postmaster@example.net    evgeniy,evgeniy@gmail.com
abuse@example.net         evgeniy,evgeniy@gmail.com

ivanov@example.net        ivanov
petrov@example.net        petrov
smirnov@example.net       smirnov
sales@example.net         ivanov,petrov,sales@another-company.com
it-dept@example.net       smirnov,smirnov@gmail.com
# Если не было соответствия выше — попадет в ящик catch-all@example.com; с этой
# записью в карте — не будет работать ограничение reject_unlisted_recipient
@example.net              catch-all

Индексированная карта hash, есть канонический домен example.com и виртуальные домены example.net и example.org

# Виртуальный домен example.net
postmaster@example.net    evgeniy,evgeniy@gmail.com
abuse@example.net         evgeniy,evgeniy@gmail.com

ivanov@example.net        ivanov
petrov@example.net        petrov
smirnov@example.net       smirnov
sales@example.net         ivanov,petrov,sales@another-company.com
it-dept@example.net       smirnov,smirnov@gmail.com
# Если не было соответствия выше — попадет в ящик catch-all@example.com; с этой
# записью в карте — не будет работать ограничение reject_unlisted_recipient
@example.net              catch-all

# Виртуальный домен example.org
postmaster@example.org    evgeniy,evgeniy@gmail.com
abuse@example.org         evgeniy,evgeniy@gmail.com

ivanov@example.org        ivanov
petrov@example.org        petrov
smirnov@example.org       smirnov
sales@example.org         ivanov,petrov,sales@another-company.com
it-dept@example.org       smirnov,smirnov@gmail.com
# Если не было соответствия выше — попадет в ящик catch-all@example.com; с этой
# записью в карте — не будет работать ограничение reject_unlisted_recipient
@example.org              catch-all

Карта с использованием регулярных выражений, есть канонический домен example.com и виртуальные домены example.net и example.org

# Для любого домена сообщения для postmaster и abuse — попадут в ящик пользователя evgeniy
/^(postmaster|abuse)@/                            evgeniy

/^(ivanov|petrov|smirnov)@example\.(org|net)$/    ${1}
/^sale@example\.(org|net)$/                       ivanov,petrov,sales@another-company.com
/^it-dept@example\.(org|net)$/                    smirnov,smirnov@gmail.com

# Если не было соответствия выше — попадет в ящик пользователя catch-all-net или catch-all-org
/@example\.(net|org)$/                            catch-all-${1}

2. Домены виртуальных почтовых ящиков

Домены виртуальных почтовых ящиков — это домены для пользователей, у которых нет локальной учетной записи (нет в файле /etc/passwd). Все сообщения, направленные в почтовые ящики типа username@example.com (где username не имеет учетной записи ОС) — будут перенаправлены в почтовый ящик пользователя, которого мы специально для этого создадим.

Агент доставки virtual в Postfix базируется на агенте доставки local. В отличие от агента local, агент virtual не имеет доступа к системной информации о локальных пользователях (например, в файле /etc/passwd) для поиска имен получателей. Вместо этого агент доставки virtual полностью полагается на карты поиска, которые не имеют ничего общего с операционной системой.

Для использования доменов виртуальных почтовых ящиков демон master должен иметь возможность запускать демон virtual — это задается в файле конфигурации master.cf. По умолчанию такая возможность есть.

# ==========================================================================
# service  type  private unpriv  chroot  wakeup  maxproc command + args
#                (yes)   (yes)   (no)    (never) (100)
# ==========================================================================
..........
local      unix  -       n       n       -       -       local
virtual    unix  -       n       n       -       -       virtual
lmtp       unix  -       -       y       -       -       lmtp
..........

Изменения в конфигурации Postfix

Давайте сначала посмотрим на фрагмент файла конфигурации — чтобы был перед глазами и было понятно, какие изменения нужны.

myhostname = mail.example.com
mydomain = example.com
myorigin = $mydomain
mydestination = localhost

virtual_mailbox_domains = example.com
virtual_uid_maps = static:2000
virtual_gid_maps = static:2000
virtual_mailbox_base = /var/vmail
virtual_mailbox_maps = hash:/etc/postfix/virtual_mailbox_map
virtual_alias_maps = hash:/etc/postfix/virtual_alias_map

Сначала необходимо сообщить серверу Postfix о том, что он является местом конечного назначения для одного или нескольких доменов виртуальных почтовых ящиков, указав список доменов в опции virtual_mailbox_domains в файле конфигурации main.cf. Мы хотим получать письма для username@example.com для нескольких десятков или сотен пользователей — и это отличается от ситуации, когда почтовые ящики были только у пользователей операционной системы.

Чтобы не создавать несколько десятков или сотен пользователей ОС, чтобы они могли работать с почтовыми ящиками — мы канонический домен example.com делаем виртуальным. Раньше он был в mydestination, теперь переносим его в virtual_mailbox_domains. Почтовые сообщения для example.com будут попадать в ящики /var/vmail/username, сообщения для localhost будут попадать в ящики /var/mail/username.

При указании домена в качестве значения опции virtual_mailbox_domains — нельзя использовать его же в качестве значения опции mydestination или virtual_alias_domains. Postfix не будет знать, как ему поступить — доставлять почту локально или же отправлять ее на виртуальное преобразование?

Из документации Postfix: NEVER list a virtual MAILBOX domain name as a mydestination domain!
Из документации Postfix: NEVER list a virtual MAILBOX domain name as a virtual ALIAS domain!

Теперь нам нужно создать пользователя операционной системы, который будет получать всю почту для пользователей, у которых нет учетной записи в системе.

# groupadd vmail -g 2000
# useradd vmail -u 2000 -g 2000

Далее нужно создать директорию, куда агент доставки virtual будет помещать сообщения — и указать эту директорию в качестве значения опции virtual_mailbox_base.

# mkdir /var/vmail
# chown vmail:vmail /var/vmail
# chmod 770 /var/vmail

Наконец, создаем файл карты для отображения адресов. Слева — виртуальный почтовый адрес, справа — имя файла или директории внутри virtual_mailbox_base. По умолчанию почтовые ящики имеют формат mbox, но можно задать формат maildir, если добавить слэш в конец имени почтового ящика (лучше так и сделать).

# nano /etc/postfix/virtual_mailbox_map
# формат maildir для почтовых ящиков (косая черта в конце)
evgeniy@example.com    evgeniy/
sergey@example.com     sergey/

ivanov@example.com     ivanov/
petrov@example.com     petrov/
smirnov@example.com    smirnov/
# если не было соответствия выше — попадет в ящик catch-all; с этой
# записью — не будет работать ограничение reject_unlisted_recipient
@example.com           catch-all/
# postmap hash:/etc/postfix/virtual_mailbox_map
По соображениям безопасности есть ограничения на карту получателей. Карты с регулярными выражениями разрешены, но нельзя использовать подстановку выражений ($1, $2, $3 в правой части).

Для домена виртуальных почтовых ящиков тоже можно создать карту псевдонимов с использованием опции virtual_alias_maps.

# nano /etc/postfix/virtual_alias_map
postmaster@example.com    evgeniy@example.com,evgeniy@gmail.com
abuse@example.com         evgeniy@example.com,evgeniy@gmail.com

sales@example.com         ivanov@example.com,petrov@example.com,sales@another-company.com
it-dept@example.com       smirnov@example.com,smirnov@gmail.com
# postmap hash:/etc/postfix/virtual_alias_map

В карте псевдонимов нельзя использовать запись типа @example.com для виртуального домена, чтобы отловить все сообщения в catch-all.

Из документации Postfix: NEVER put a virtual MAILBOX wild-card in the virtual ALIAS file!

Редактируем файл конфигурации /etc/postfix/main.cf

myhostname = mail.example.com
mydomain = example.com
myorigin = $mydomain
mydestination = localhost

virtual_mailbox_domains = example.com
virtual_uid_maps = static:2000
virtual_gid_maps = static:2000
virtual_mailbox_base = /var/vmail
virtual_mailbox_maps = hash:/etc/postfix/virtual_mailbox_map
virtual_alias_maps = hash:/etc/postfix/virtual_alias_map
# systemctl reload postfix.service  # перечитать файлы конфигурации

Этот вариант настройки Postfix является основным в настоящее время. Тот вариант настроки, который мы рассмотрели в самом начале, когда почтовые ящики создаются для пользователей ОС — сейчас практически не используется. Любой почтовый домен должен быть виртуальным, а не каноническим — такой вариант конфигурации легко расширять при добавлении новых доменов.

Несколько виртуальных доменов

Допустим, у нас нет канонического домена, а только виртуальные — example.net и example.org.

myhostname = mail.example.com
mydomain = localhost
myorigin = localhost
mydestination = localhost

virtual_mailbox_domains = example.net, example.org
virtual_uid_maps = static:2000
virtual_gid_maps = static:2000
virtual_mailbox_base = /var/vmail
virtual_mailbox_maps = hash:/etc/postfix/virtual_mailbox_map
virtual_alias_maps = hash:/etc/postfix/virtual_alias_map

Нам по-прежнему нужен виртуальный пользователь vmail и директория /var/vmail для почтовых ящиков виртуальных пользователей.

# groupadd -g 2000 vmail
# useradd -r -u 2000 -g 2000 -s /usr/sbin/nologin vmail
# mkdir /var/vmail
# mkdir /var/vmail/example.net
# mkdir /var/vmail/example.org
# chown -R vmail:vmail /var/vmail
# chmod -R 770 /var/vmail

Создаем индекированную карту почтовых ящиков

# nano /etc/postfix/virtual_mailbox_map
# Виртуальный домен example.net
ivanov@example.net     example.net/ivanov/
petrov@example.net     example.net/petrov/
smirnov@example.net    example.net/smirnov/
# если не было соответствия выше — попадет в ящик catch-all; с этой
# записью — не будет работать ограничение reject_unlisted_recipient
@example.net              example.net/catch-all/

# Виртуальный домен example.org
sokolov@example.org    example.org/sokolov/
morozov@example.org    example.org/morozov/
fedorov@example.org    example.org/fedorov/
# если не было соответствия выше — попадет в ящик catch-all; с этой
# записью — не будет работать ограничение reject_unlisted_recipient
@example.org           example.org/catch-all/
# postmap hash:/etc/postfix/virtual_mailbox_map

Создаем индексированную карту псевдонимов

# nano /etc/postfix/virtual_alias_map
# Виртуальный домен example.net
postmaster@example.net    smirnov@gmail.com
abuse@example.net         smirnov@gmail.com
sales@example.net         ivanov@example.net,petrov@example.net
it-dept@example.net       smirnov@example.net,smirnov@gmail.com

# Виртуальный домен example.org
postmaster@example.org    fedorov@gmail.com
abuse@example.org         fedorov@gmail.com
sales@example.org         sokolov@example.org,morozov@example.org
it-dept@example.org       fedorov@example.org,fedorov@gmail.com
# postmap hash:/etc/postfix/virtual_alias_map

По поводу псевдонимов (aliases)

С псевдонимами выглядит запутанно, но на самом деле просто. Давным-давно в Unix были только пользователи системы и их почтовые ящики. Сообщения для пользователей (в том числе не существующих) можно было направить в почтовые ящики существующих пользователей с помощью файла /etc/aliases.

# Почтовые ящики для существующих пользователей системы, так было давно.
# Псевдонимы создаются в файле /etc/aliases, который остался от Sendmail.
mydomain = example.com
myorigin = $mydomain
mydestination = $mydomain, localhost

Дополнительные домены, которые задаются опцией virtual_alias_domains — позволяют создать дополнительные виртуальные адреса почты на этих доменах, которые с помощью карты virtual_alias_maps перенаправляются в почтовые ящики существующих пользователей операционной системы.

# Домены виртуальных псевдонимов, сообщения для ящиков на новых доменах
# перенаправляются в ящики существующих пользователей системы
mydomain = example.com
myorigin = $mydomain
mydestination = $mydomain, localhost
virtual_alias_domains = example.net, example.org
virtual_alias_maps = hash:/etc/postfix/virtual_alias_map

Дополнительные домены, которые задаются опцией virtual_mailbox_domains — позволяют создавать адреса почты в этих доменах для пользователей, которых нет в операционной системе. Для этого создается пользователь системы vmail, который и получает все сообщения для всех таких пользователей. Для этого случая тоже предусмотрены псевдонимы — они тоже задаются опцией virtual_alias_maps.

# Домены виртуальных почтовых ящиков. Сообщения для example.com попадают в
# ящики /var/mail/username, сообщения для example.net, example.org попадают
# в ящики /var/vmail/domain/username.
myhostname = mail.example.com
mydomain = example.com
myorigin = $mydomain
mydestination = $mydomain, localhost
virtual_mailbox_domains = example.net, example.org
virtual_mailbox_maps = hash:/etc/postfix/virtual_mailbox_map
virtual_alias_maps = hash:/etc/postfix/virtual_alias_map

Пример настройки Postfix и Dovecot

Допустим, у нас раньше был совсем простой почтовый сервер для домена example.com, который обслуживал существующих пользователей операционной системы. А теперь нужно, чтобы почтовый сервер обслуживал еще множество почтовых ящиков пользователей в доменах example.net и example.org.

Name Type TTL Value
example.com A 3600 111.111.111.111
www.example.com A 3600 111.111.111.111
mail.example.com A 3600 222.222.222.222
example.com MX 10 3600 mail.example.com
example.com TXT 3600 v=spf1 a mx -all
default._domainkey.example.com TXT 3600 v=DKIM1; h=sha256; k=rsa; p=MIIBI...TEEeN...
_dmarc.example.com TXT 3600 v=DMARC1; p=none; rua=mailto:dmarc@example.com; sp=reject; adkim=s; aspf=s; fo=1
mail.example.net A 3600 222.222.222.222
example.net MX 10 3600 mail.example.net
example.net TXT 3600 v=spf1 a mx -all
mail.example.org A 3600 222.222.222.222
example.org MX 10 3600 mail.example.org
example.org TXT 3600 v=spf1 a mx -all

Вообще говоря, добавлять A-записи для доменов mail.example.net и mail.example.net не обязательно — это для удобства пользователей. При настройке почтового клиента можно указывать mail.example.net и mail.example.org как SMTP/IMAP/POP3 сервер — в противном случае пришлось бы указывать mail.example.com. Если мы не добавляем эти две A-записи — тогда MX-записи для example.net и example.org должны указывать на mail.example.com.

# certbot certonly --standalone --cert-name mail.example.com \
> --expand -d mail.example.com -d mail.example.net -d mail.example.org

Опция expand говорит certbot, что нужно добавить новые домены к существующему сертификату, не удаляя старые. Теперь почтовые клиенты, подключаясь к mail.example.net и mail.example.org — будут доверять почтовому серверу, поскольку SSL-сертификат действует и для этих двух доменов.

Настройка сервера Postfix

Сообщаем Postfix, что он является местом конечного назначения для example.net и example.org с помощью директивы virtual_mailbox_domains.

# nano /etc/postfix/main.cf
# Домены виртуальных почтовых ящиков, сообщения для почтовых пользователей на
# новых доменах example.net и example.org перенаправляются в почтовые ящики
# /var/vmail/domain/username. Мы оставляем ящики пользователей системы на
# старом месте /var/mail/username, они будут получать почту для example.com
myhostname = mail.example.com
mydomain = example.com
myorigin = $mydomain
mydestination = $mydomain, localhost

virtual_mailbox_domains = example.net, example.org

Сообщения для системных пользователей Postfix будет сам доставлять в почтовые ящики. А вот сообщения для виртуальных пользователей Postfix будет отправлять Dovecot по протоколу LMTP через unix-сокет.

# nano /etc/postfix/main.cf
# Сообщения для виртуальных пользователей отправлять Dovecot по протоколу
# LMTP через unix-сокет /var/spool/postfix/private/dovecot-lmtp
virtual_transport = lmtp:unix:private/dovecot-lmtp

На почтовом сервере должен быть агент отправки электронной почты MSA, который отвечает за получение сообщений от MUA и передачу этих сообщений MTA. Давайте запретим запуск Postfix демонов submission(s) — и вместо них запустим Dovecot демонов submission(s).

# nano /etc/postfix/master.cf
# Choose one: enable submission for loopback clients only, or for any client.
#127.0.0.1:submission    inet    n    -    y    -    -    smtpd
#submission    inet    n    -    y    -    -    smtpd
#     -o syslog_name=postfix/submission
#     -o smtpd_tls_security_level=encrypt
#     -o smtpd_sasl_auth_enable=yes
#     -o smtpd_tls_auth_only=yes
#     ..........

# Choose one: enable submissions for loopback clients only, or for any client.
#127.0.0.1:submissions    inet    n    -    y    -    -    smtpd
#submissions    inet    n    -    y    -    -    smtpd
#     -o syslog_name=postfix/submissions
#     -o smtpd_tls_wrappermode=yes
#     -o smtpd_sasl_auth_enable=yes
#     ..........

Dovecot не может сам выступать в роли MSA, он только аутентифицирует почтового клиента и проксирует SMTP-соединение к Postfix. Чтобы Postfix обрабатывал такие SMTP-соединения отдельно от остальных — создадим отдельного демона и зададим для него другие настройки.

# nano /etc/postfix/master.cf
127.0.0.1:10025    inet    n    -    n    -    -    smtpd
     -o syslog_name=postfix/msa_auth_dovecot
     -o mynetworks=0.0.0.0/0
     -o smtpd_tls_security_level=none
     -o smtpd_client_restrictions=
     -o smtpd_helo_restrictions=
     -o smtpd_sender_restrictions=reject_sender_login_mismatch
     -o smtpd_relay_restrictions=permit_mynetworks,reject_unauth_destination
     -o smtpd_recipient_restrictions=reject_non_fqdn_recipient,reject_unknown_recipient_domain
     -o smtpd_authorized_xclient_hosts=127.0.0.1,localhost

Поскольку аутентификацией почтовых клиентов занимается Dovecot, а потом проксирует SMTP-соединение Postfix — то для проверки ограничения smtpd_sender_login_maps, проксирующий сервер должен передать SASL-логин. Директива smtpd_authorized_xclient_hosts предписывает Postfix получать SASL-логин от хоста 127.0.0.1 по протоколу XCLIENT. А директива submission_relay_trusted предписывает Dovecot отправлять SASL-логин — тоже по протоколу XCLIENT.

# nano /etc/postfix/main.cf
# Проверка соответствия между аутентифицированным пользователем и адресом
# отправителя, указанным в команде MAIL FROM во время SMTP-сессии
smtpd_sender_login_maps =
    # для канонического домена example.com
    hash:/etc/postfix/sender_login_map_example_com
    # для виртуального домена example.net
    hash:/etc/postfix/sender_login_map_example_net
    # для виртуального домена example.org
    hash:/etc/postfix/sender_login_map_example_org

Карты соответствия между адресом почты отправителя и SASL-логином для трех доменов

# nano /etc/postfix/sender_login_map_example_com
# Адрес отправителя (MAIL FROM)    Список разрешенных SASL-логинов через запятую
evgeniy@example.com                evgeniy@example.com
sergey@example.com                 sergey@example.com
# postmap hash:/etc/postfix/sender_login_map_example_com
Обратите внимание, что SASL-логин должен быть в формате user@domain — потому что доменов теперь несколько. И мы уже не можем позволить пользователю указывать только имя, без домена — могут быть два совершенно разных пользователя ivanov@example.net и ivanov@example.org.
# nano /etc/postfix/sender_login_map_example_net
# Адрес отправителя (MAIL FROM)    Список разрешенных SASL-логинов через запятую
ivanov@example.net                 ivanov@example.net
petrov@example.net                 petrov@example.net
smirnov@example.net                smirnov@example.net
# postmap hash:/etc/postfix/sender_login_map_example_net
# nano /etc/postfix/sender_login_map_example_org
# Адрес отправителя (MAIL FROM)    Список разрешенных SASL-логинов через запятую
sokolov@example.org                sokolov@example.org
morozov@example.org                morozov@example.org
fedorov@example.org                fedorov@example.org
# postmap hash:/etc/postfix/sender_login_map_example_org

Для борьбы со спамом на раннем этапе подключения — запустим Postfix демона postscreen

# nano /etc/postfix/master.cf
# ==========================================================================
# service type  private unpriv  chroot  wakeup  maxproc command + args
#               (yes)   (yes)   (no)    (never) (100)
# ==========================================================================
#smtp      inet  n       -       y       -       -       smtpd
smtp      inet  n       -       y       -       1       postscreen
smtpd     pass  -       -       y       -       -       smtpd
dnsblog   unix  -       -       y       -       0       dnsblog
tlsproxy  unix  -       -       y       -       0       tlsproxy
# nano /etc/postfix/main.cf
# Включение DNSBL (черные списки)
postscreen_dnsbl_action = enforce
postscreen_dnsbl_sites = zen.spamhaus.org bl.spamcop.net
postscreen_dnsbl_threshold = 1
# Включение теста SMTP Greeting Delay
postscreen_greet_action = enforce
postscreen_greet_wait = 6s
# Сохранение истории ip-адресов
postscreen_cache_map = btree:$data_directory/postscreen_cache
postscreen_cache_cleanup_interval = 12h
postscreen_cache_retention_time = 7d
# Реакция на нарушение протокола
postscreen_pipelining_action = enforce
postscreen_non_smtp_command_action = drop
postscreen_bare_newline_action = drop

Чтобы иметь возможность использовать ограничение reject_unlisted_recipient — создадим карты поиска virtual_mailbox_maps и virtual_alias_maps.

# nano /etc/postfix/main.cf
virtual_mailbox_domains = example.net, example.org
virtual_mailbox_maps =
    hash:/etc/postfix/virtual_mailbox_map_example_net
    hash:/etc/postfix/virtual_mailbox_map_example_org
virtual_alias_maps =
    hash:/etc/postfix/virtual_alias_map_example_net
    hash:/etc/postfix/virtual_alias_map_example_org
# nano /etc/postfix/virtual_mailbox_map_example_net
# Где почтовые ящики для домена example.net
ivanov@example.net     example.net/ivanov/
petrov@example.net     example.net/petrov/
smirnov@example.net    example.net/smirnov/
# postmap hash:/etc/postfix/virtual_mailbox_map_example_net
# nano /etc/postfix/virtual_alias_map_example_net
# Алиасы почтовых ящиков для домена example.net
postmaster@example.net    admin@example.net
abuse@example.net         admin@example.net
dmarc@example.net         admin@example.net

sales@example.net         ivanov@example.net,petrov@example.net
admin@example.net         smirnov@gmail.com
# postmap hash:/etc/postfix/virtual_alias_map_example_net
# nano /etc/postfix/virtual_mailbox_map_example_org
# Где почтовые ящики для домена example.org
sokolov@example.org     example.org/sokolov/
morozov@example.org     example.org/morozov/
fedorov@example.org     example.org/fedorov/
# postmap hash:/etc/postfix/virtual_mailbox_map_example_org
# nano /etc/postfix/virtual_alias_map_example_org
# Алиасы почтовых ящиков для домена example.org
postmaster@example.org    admin@example.org
abuse@example.org         admin@example.org
dmarc@example.org         admin@example.org

sales@example.org         ivanov@example.org,petrov@example.org
admin@example.org         fedorov@gmail.com
# postmap hash:/etc/postfix/virtual_alias_map_example_org

Установим и подключим SpamAssassin и ClamAV — для борьбы со спамом и вирусами. Файлы конфигурации внешних фильтров уже обсуждали, не будем здесь повторять. Только сделаем связь Postfix c ClamAV-милтером через сокет — для этого сокет должен быть внутри chroot-директории, иначе Postfix не сможет получить к нему доступ.

# apt install spamassassin spamc spamass-milter # SpamAssassin
# apt install clamav-daemon clamav-freshclam clamav-milter # ClamAV
# freshclam # обновить базы вирусов (предварительно изменить серверы обновлений)
# systemctl enable --now clamav-daemon.service # демон сканирования на вирусы
# systemctl enable --now clamav-freshclam.service # демон для обновления базы вирусов
# файл конфигурации /etc/clamav/clamav-milter.conf
MilterSocket /var/spool/postfix/clamav/milter.ctl
MilterSocketGroup postfix
MilterSocketMode 660
# nano /etc/postfix/main.cf
# Применять фильтры для сообщений, которые попадают в Postfix через демона smtpd
smtpd_milters =
    unix:clamav/milter.ctl
    unix:spamass/spamass.sock
# Применять фильтры для писем, которые попадают в Postfix не через демона smtpd
non_smtpd_milters = $smtpd_milters

# Действие по умолчанию, если milter временно недоступен или вернул ошибку
milter_default_action = tempfail

# Таймауты для внешних фильтров в секундах (это не обязательно, но желательно)
milter_connect_timeout = 30s
milter_command_timeout = 30s
milter_content_timeout = 300s

# Версия протокола Milter, 6-я версия рекомендуется для современных фильтров
milter_protocol = 6

Установим и подключим OpenDKIM и OpenDMARC — файлы конфигурации не будем здесь обсуждать, все уже подробно рассмотрели раньше.

# apt install opendkim opendkim-tools
# apt install opendmarc
# nano /etc/postfix/main.cf
# Применять фильтры для сообщений, которые попадают в Postfix через демона smtpd
smtpd_milters =
    # проверка на вирусы
    unix:clamav/milter.ctl
    # проверка на спам
    unix:spamass/spamass.sock
    # подпись OpenDKIM
    inet:localhost:8891
    # политика DMARC
    inet:localhost:8893
# Применять фильтры для писем, которые попадают в Postfix не через демона smtpd
non_smtpd_milters = $smtpd_milters

Доставкой сообщений системным пользователям занимается Postfix — и следит за рамером почтового ящика. Postfix информирует отправителя о превышении квоты, но не предупреждает об этом получателя. Так что лучше доверить доставку сообщений в почтовые ящики Dovecot.

# nano /etc/postfix/main.cf
# ОГРАНИЧЕНИЯ НА РАЗМЕР ПОЧТОВОГО ЯЩИКА И РАЗМЕР СООБЩЕНИЯ

# Ограничение на размер ящика системного пользователя 500 Мбайт (если доставкой
# сообщений для системных пользователей занимается агент доставки local)
mailbox_size_limit = 524288000
# Ограничение на размер ящика виртуального пользователя 500 Мбайт (если доставкой
# сообщений для виртуальных пользователей занимается агент доставки virtual)
virtual_mailbox_limit = 524288000
# Максимальный размер входящего сообщения 15 Мб. При этом следует помнить, что
# кодирование бинарных данных увеличивает размер (вложение рамером 10 Мб будет
# весить 15 Мб после кодирования)
message_size_limit = 15728640

В итоге получился такой файл конфигурации /etc/postfix/main.cf

# Какие настройки по умолчанию использовать. У разных версий Postfix разные
# настройки по умолчанию — они изменяются от версии к версии.
compatibility_level = 3.6

# Установка значения yes (вместо значения по умолчению no) приводит к тому,
# что ответы с кодами 5xx преобразуются в 4xx. Соответственно, почтовые
# серверы будут пытаться доставить сообщения на наш почтовый сервер позже. За
# это время можно проверить логи и убедиться, правильно ли работают правила.
soft_bounce = no

# Местоположение очередей и корневой каталог демонов Postfix, которые
# запускаются в среде с измененным корневым каталогом (chroot).
queue_directory = /var/spool/postfix

# Местоположение всех утилит Postfix — postdrop, postmap, postqueue и другие.
command_directory = /usr/sbin

# Местоположение всех демонов Postfix, перечисленных в файле конфигурации
# master.cf. Эта директория должна принадлежать пользователю root.
daemon_directory = /usr/lib/postfix/sbin

# Местоположение, куда Postfix будет записывать данные в процессе работы,
# например кэши. Эта директория должна принадлежать $mail_owner, см.ниже.
data_directory = /var/lib/postfix

# Задает владельца очередей и большинства процессов демона Postfix. Нужно
# задать аккаунт в системе, который больше нигде не используется и которому
# не принадлежат другие файлы или процессы. Нельзя использовать nobody или 
# daemon.
mail_owner = postfix

# Используется для указания имени хоста почтового сервера. Типичные примеры
# имён хостов почтовых серверов — smtp.example.com или mail.example.com.
myhostname = mail.example.com

# Позволяет указать почтовый домен, обслуживанием которого занимается сервер,
# например — example.com.
mydomain = example.com

# Позволяет указать доменное имя, используемое в почтовых сообщениях,
# отправленных с сервера. Когда пользователи отправляют почту без указания
# имени домена в адресах конверта (SMTP ENVELOPE) или заголовках (MAIL
# HEADER), эта опция определяет, какое имя домена должно быть добавлено.
# По умолчанию используется значение опции $myhostname.
myorigin = $mydomain

# Задает сетевой интерфейс, который будет прослушиваться Postfix. По умолчанию
# прослушиваются все сетевые интерфейсы на сервере (значение all).
inet_interfaces = $myhostname, localhost

# Содержит список доменов, которые сервер будет считать конечными пунктами
# назначения для входящей почты. Другими словами — сервер не будет такие
# письма пересылать дальше, а будет оставлять у себя. Значение по
# умолчанию — $myhostname, localhost.$mydomain, localhost
mydestination = $mydomain, localhost

# Определяет локальных получателей, которые определены в файле паролей Linux
# и картах псевдонимов. Письма другим получателям будут отклоняться (reject).
# Сообщения предназначено локальному получателю, если доменная часть адреса
# получателя совпадает с $mydestination или $inet_interfaces + была найдена
# запись в /etc/passwd или в $alias_maps.
local_recipient_maps = proxy:unix:passwd.byname $alias_maps

# Код ответа сервера, если локальный получатель не найден, см. предыдущую
# опцию конфигурации. Значение по умолчанию 550, но для начальном этапе
# настройки есть смысл установить значение 450 (попробуйте снова позже).
unknown_local_recipient_reject_code = 550

# Позволяет указать, с каких ip-адресов можно отправлять почту через этот
# сервер. Обычно разрешают отправлять только из своей локальной сети.
mynetworks = 127.0.0.0/8

# Опция указывает на необходимость приема почты для этих доменов несмотря на
# то, что этот сервер не является местом их конечного назначения. Самое
# безопасное значение опции — пустое.
relay_domains =

# Задает список карт псевдонимов, используемых агентом доставки почты (MDA).
# Карта позволяет перенаправить почту для каких-то получателей (даже не
# существующих) другому получателю или получателям.
alias_maps = hash:/etc/aliases

# Задает список карт псевдонимов, которые создаются командой newaliases. Это
# отдельная опция конфигурации, поскольку alias_maps может указывать на карты,
# которые не обязательно находятся под контролем Postfix.
alias_database = hash:/etc/aliases

# Вся почта будет попадать в директорию /var/mail, где имеется файл для
# каждого пользователя. Если в конце добавить слэш — то сообщения будут
# сохраняться в виде отдельных файлов внутри /var/mail/username.
mail_spool_directory = /var/mail/

# Задает имя файла для хранения сообщений в домашних директориях пользователей.
# Можно задать значение опции в виде Maildir/ — в этом случае сообщения будут
# сохраняться в виде отдельных файлов в директории /home/username/Maildir.
#home_mailbox = Postbox
#home_mailbox = Postdir/

# Использовать для локальной доставки почты указанного агента доставки почты.
#mailbox_command = /usr/bin/procmail

# Позволяет задать ответ, который возвращает сервер при подключении клиентов.
# Лучше всего установить значение, чтобы оно не указывало — какой почтовый
# сервер используется. В начале должно быть $myhostname — это требование RFC.
smtpd_banner = $myhostname ESMTP $mail_name

# Задает путь к утилите sendmail, которая входит в поставку Postfix и
# предоставляет совместимый с Sendmail интерфейс для приложений, способных
# только вызывать /usr/sbin/sendmail.
sendmail_path = /usr/sbin/sendmail

# Задает путь к команде newaliases, которая входит в поставку Postfix и
# предназначенной для создания карты алиасов. Команда newaliases является
# символической ссылкой на Postfix утилиту sendmail.
newaliases_path = /usr/bin/newaliases

# Задает путь к команде mailq, которая входит в поставку Postfix и
# предназначена для просмотра очередей. Команда mailq является символической
# ссылкой на Postfix утилиту sendmail.
mailq_path = /usr/bin/mailq

# Задает имя группы, которая используется для выполнения команд отправки почты
# и управления очередями. Это должна быть группа, которая не используется
# другими учетными записями, даже учетной записью Postfix.
setgid_group = postdrop

# Задает версию ip-протокола, которую будет использовать сервер при
# установлении соединений. По умолчанию имеет значение all.
inet_protocols = ipv4

# Добавлять важные заголовки, если их нет — например Date или Message-ID
always_add_missing_headers = yes

# Максимальное время для установки соединения с удалённым SMTP-сервером
smtp_connect_timeout = 20s
# Mакс.время ожидания ответа от удалённого SMTP-сервера на команду HELO/EHLO
smtp_helo_timeout = 10s

# ДОМЕНЫ ВИРТУАЛЬНЫХ ПОЧТОВЫХ ЯЩИКОВ EXAMPLE.NET и EXAMPLE.ORG

virtual_mailbox_domains = example.net, example.org
virtual_mailbox_maps =
    hash:/etc/postfix/virtual_mailbox_map_example_net
    hash:/etc/postfix/virtual_mailbox_map_example_org
virtual_alias_maps =
    hash:/etc/postfix/virtual_alias_map_example_net
    hash:/etc/postfix/virtual_alias_map_example_org

# Сообщения для виртуальных пользователей отправлять Dovecot по протоколу
# LMTP через unix-сокет /var/spool/postfix/private/dovecot-lmtp
virtual_transport = lmtp:unix:private/dovecot-lmtp

# ОГРАНИЧЕНИЯ НА ПРИЕМ ПОЧТЫ ЭТИМ ПОЧТОВЫМ СЕРВЕРОМ

smtpd_helo_required = yes
strict_rfc821_envelopes = yes
disable_vrfy_command = yes

# Не принимать почту на адреса, для которых у нас нет почтовых ящиков. Вместо
# этой опции можно использовать правило reject_unlisted_recipient в триггере
# smtpd_recipient_restrictions. В любом случае нужно создать список почтовых
# адресов получателей, которые нужно принимать.
#smtpd_reject_unlisted_recipient = yes

# Ограничения на этапе установке подключения
smtpd_client_restrictions =
    # Отклонять клиентов, у которых доменное имя из PTR-записи
    # не разрешается в тот же ip-адрес по A-записи
    reject_unknown_client_hostname

# Ограничения на этапе команды HELO/EHLO
smtpd_helo_restrictions =
    # Отклонять клиентов, использующих неправильный синтаксис домена
    reject_invalid_helo_hostname
    # Отклонять клиентов, указывающих не полное доменное имя FQDN
    reject_non_fqdn_helo_hostname
    # Отклонять клиентов, ДНС-имя которых из команды HELO/EHLO не
    # имеет A- или MX-записи или ip-адрес не является правильным
    reject_unknown_helo_hostname

# Проверка соответствия между аутентифицированным пользователем и
# адресом отправителя, указанным в команде MAIL FROM
smtpd_sender_login_maps =
    # для канонического домена example.com
    hash:/etc/postfix/sender_login_map_example_com
    # для виртуального домена example.net
    hash:/etc/postfix/sender_login_map_example_net
    # для виртуального домена example.org
    hash:/etc/postfix/sender_login_map_example_org

# Ограничения на этапе команды MAIL FROM
smtpd_sender_restrictions =
    # Отклонять почту от отправителей с неполным доменным именем
    reject_non_fqdn_sender
    # Отклонять почту от отправителей из несуществующих доменов
    reject_unknown_sender_domain
    # Запросить сервер, обслуживающий указанный адрес отправителя,
    # на предмет существования на нём пользователя с этим адресом
    reject_unverified_sender

# Ограничения на этапе команды RCPT TO (relay)
smtpd_relay_restrictions =
    # Отклонять почту, если домена получателя нет в $mydestination,
    # $virtual_alias_domains, $virtual_mailbox_domains, $relay_domains
    reject_unauth_destination
    # Отклонять почту для получателя, если наш сервер не является
    # конечной точкой, а домен получателя не имеет A- и MX-записей
    reject_unknown_recipient_domain

# Ограничения на этапе команды RCPT TO (spam)
smtpd_recipient_restrictions =
    # Отклонять почту для получателей с неполным доменным именем
    reject_non_fqdn_recipient
    # Всегда принимать почту для учетных записей postmaster и abuse
    check_recipient_access hash:/etc/postfix/permit_recipients
    # Отклонять почту на адреса, для которых нет почтовых ящиков
    reject_unlisted_recipient

# Ограничения на команду ETRN от почтовых серверов
smtpd_etrn_restrictions = reject

# ОГРАНИЧЕНИЯ НА ПРИЁМ СООБЩЕНИЙ ОТ SMTP-КЛИЕНТОВ (ДЕМОН ANVIL)

# Период времени, на протяжении которого действуют ограничения для клиента,
# перечисленые ниже (за этим следит демон anvil, входит в поставку Postfix)
anvil_rate_time_unit = 60s

# клиент может отправить 30 сообщений за anvil_rate_time_unit секунд
smtpd_client_message_rate_limit = 30
# не больше 30 получателей сообщений за anvil_rate_time_unit секунд
smtpd_client_recipient_rate_limit = 30
# количество одновременно разрешенных подключений от одного клиента
smtpd_client_connection_count_limit = 10
# количество разрешенных подключений за anvil_rate_time_unit секунд
smtpd_client_connection_rate_limit = 30

# Клиенты, на которых не действуют ограничения. По умолчанию $mynetworks,
# действует для всех, в том числе для smtpd на 25 порту, поэтому отключаем 
smtpd_client_event_limit_exceptions =

# Максимальное количество получателей сообщения, то есть количество
# записей RCPT TO: (по умолчанию 1000, это явно избыточно)
smtpd_recipient_limit = 100

# БОРЬБА СО СПАМОМ НА РАННЕМ ЭТАПЕ ПОДКЛЮЧЕНИЯ, ДЕМОН POSTSCREEN

# Включение DNSBL (черные списки)
postscreen_dnsbl_action = enforce
postscreen_dnsbl_sites = zen.spamhaus.org bl.spamcop.net
postscreen_dnsbl_threshold = 1
# Включение теста SMTP Greeting Delay
postscreen_greet_action = enforce
postscreen_greet_wait = 6s
# Сохранение истории ip-адресов
postscreen_cache_map = btree:$data_directory/postscreen_cache
postscreen_cache_cleanup_interval = 12h
postscreen_cache_retention_time = 7d
# Реакция на нарушение протокола
postscreen_pipelining_action = enforce
postscreen_non_smtp_command_action = drop
postscreen_bare_newline_action = drop

# ОГРАНИЧЕНИЯ НА ОТПРАВКУ ПОЧТЫ И ПОПЫТКИ АУТЕНТИФИКАЦИИ

# Задержка в 1 сек между отправкой писем. Наш сервер не сможет отправить
# больше 60 писем за минуту (подходит для сервера небольшой организации)
smtp_transport_rate_delay = 1s

# Максимальное кол-во попыток аутентификации в минуту с одного ip-адреса
# (для защиты от подбора паролей)
smtpd_client_auth_rate_limit = 10

# ОГРАНИЧЕНИЯ НА РАЗМЕР ПОЧТОВОГО ЯЩИКА И РАЗМЕР СООБЩЕНИЯ

# Ограничение на размер ящика системного пользователя 500 Мбайт (доставкой
# сообщений для системных пользователей занимается агент доставки local)
mailbox_size_limit = 52428800
# Ограничение на размер ящика виртуального пользователя 500 Мбайт (доставкой
# сообщений для виртуальных пользователей занимается агент доставки virtual)
virtual_mailbox_limit = 52428800
# Максимальный размер входящего сообщения 15 Мб. При этом следует помнить,
# что кодирование бинарных данных увеличивает размер (вложение рамером 10 Мб
# будет весить 15 Мб после кодирования)
message_size_limit = 15728640

# ОБРАБОТКА СООБЩЕНИЯ ВНЕШНИМИ ФИЛЬТРАМИ (НА ВИРУСЫ, СПАМ)

# Применять фильтр для сообщений, которые попадают в Postfix через демона smtpd
smtpd_milters =
    # проверка на вирусы
    unix:clamav/milter.ctl
    # проверка на спам
    unix:spamass/spamass.sock
    # подпись OpenDKIM
    inet:localhost:8891
    # политика DMARC
    inet:localhost:8893
# Применять фильтр для писем, которые попадают в Postfix не через демона smtpd
non_smtpd_milters = $smtpd_milters
# Действие по умолчанию, если milter временно недоступен или вернул ошибку
milter_default_action = tempfail
# Таймауты для внешних фильтров в секундах (это не обязательно, но желательно)
milter_connect_timeout = 30s
milter_command_timeout = 30s
milter_content_timeout = 300s
# Версия протокола Milter, 6-я версия рекомендуется для современных фильтров
milter_protocol = 6

# АУТЕНТИФИКАЦИЯ SMTP-КЛИЕНТОВ С ИСПОЛЬЗОВАНИЕМ DOVECOT SASL,
# НЕ ИСПОЛЬЗУЕТСЯ, ЗАПУЩЕНЫ DOVECOT ДЕМОНЫ SUBMISSION(S) 

# Включаем аутентификацию клиентов с использованием Cyrus или Dovecot
#smtpd_sasl_auth_enable = yes
# Может принимать значения cyrus или dovecot, сейчас используем dovecot
#smtpd_sasl_type = dovecot

# Путь к unix-сокету для связи Postfix с механизмом SASL-аутентификации
# Dovecot. Указывается относительно директории очередей $queue_directory,
# так что фактический путь будет /var/spool/postfix/private/auth
#smtpd_sasl_path = private/auth
# Добавлять доменное имя к логину клиента, который не имеет доменной
# части, то есть преобразовать «username» в «username@example.com»
#smtpd_sasl_local_domain = $mydomain
# Запрещаем анонимную аутентификацию клиентов на почтовом сервере
#smtpd_sasl_security_options = noanonymous

# TLS/SSL ПРИ ПОЛУЧЕНИИ СООБЩЕНИЙ ОТ SMTP-КЛИЕНТОВ

# Приватный ключ + сертификат почтового сервера + сертификат вышестоящего
# CA, который подписал сертификат и которому доверяют почтовые клиенты
smtpd_tls_cert_file = /etc/letsencrypt/live/mail.example.com/fullchain.pem
smtpd_tls_key_file = /etc/letsencrypt/live/mail.example.com/privkey.pem
# Уровень подробности логирования при использовании TLS/SSL шифрования
smtpd_tls_loglevel = 1
# Включить информацию о протоколе и используемом шифре, а также CommonName
# клиента и эмитента в заголовок сообщения Received (полезно при отладке)
smtpd_tls_received_header = yes
# Опция предписывает Postfix сообщать удаленным SMTP-клиентам о поддержке
# STARTTLS, но не требует от клиентов использования шифрования TLS/SSL. Если
# установить значение «encrypt» — Postfix не будет принимать сообщения без
# шифрования, для публичного сервера такое поведение нежелательно (RFC 2487).
smtpd_tls_security_level = may
# Postfix поддерживает возобновление сеанса TLS RFC 5077, если  клиент SMTP
# также поддерживает RFC 5077. Это экономит ресурсы и сокращает задержку.
# Значение ноль отключает кэширование.
smtpd_tls_session_cache_timeout = 3600s
# Аутентфикация SASL возможна только при использовании TLS/SSL шифрования
smtpd_tls_auth_only = yes

В файле конфигурации /etc/postfix/master.cf — запуск демонов postscreen и smtpd (сокет 127.0.0.1:10025)

# ==========================================================================
# service type  private unpriv  chroot  wakeup  maxproc command + args
#               (yes)   (yes)   (no)    (never) (100)
# ==========================================================================
#smtp      inet  n       -       y       -       -       smtpd
smtp      inet  n       -       y       -       1       postscreen
smtpd     pass  -       -       y       -       -       smtpd
dnsblog   unix  -       -       y       -       0       dnsblog
tlsproxy  unix  -       -       y       -       0       tlsproxy

127.0.0.1:10025    inet    n    -    n    -    -    smtpd
     -o syslog_name=postfix/msa_auth_dovecot
     -o mynetworks=0.0.0.0/0
     -o smtpd_tls_security_level=none
     -o smtpd_client_restrictions=
     -o smtpd_helo_restrictions=
     -o smtpd_sender_restrictions=reject_sender_login_mismatch
     -o smtpd_relay_restrictions=permit_mynetworks,reject_unauth_destination
     -o smtpd_recipient_restrictions=reject_non_fqdn_recipient,reject_unknown_recipient_domain
     -o smtpd_authorized_xclient_hosts=127.0.0.1,localhost

Настройка сервера Dovecot

Системный пользователь с минимальными правами, который будет владеть директорией, где расположены виртуальные ящики

# groupadd -g 2000 vmail
# useradd -r -u 2000 -g 2000 -s /usr/sbin/nologin vmail

Далее нужно создать директорию, где расположены виртуальные почтовые ящики, назначить ее владельцем пользователя vmail

# mkdir /var/vmail
# mkdir /var/vmail/example.net
# mkdir /var/vmail/example.org
# chown -R vmail:vmail /var/vmail
# chmod -R 750 /var/vmail

Настроим обязательное шифрование при передаче данных между почтовым клиентом и почтовым сервером

# nano /etc/dovecot/conf.d/10-ssl.conf
# Обязательно SSL/TLS при передаче данных клиент-сервер
ssl = required

ssl_cert = </etc/letsencrypt/live/mail.example.com/fullchain.pem
ssl_key = </etc/letsencrypt/live/mail.example.com/privkey.pem

# Использовать только современные протоколы шифрования
ssl_min_protocol = TLSv1.2
ssl_cipher_list = HIGH:!aNULL:!MD5:!RC4

# Рекомендуется для улучшения Perfect Forward Secrecy,
# sudo openssl dhparam -out /etc/dovecot/dh.pem 4096
ssl_dh = </etc/dovecot/dh.pem
# openssl dhparam -out /etc/dovecot/dh.pem 4096

На почтовом сервере должен быть агент отправки электронной почты MSA, который отвечает за получение сообщений от MUA и передачу этих сообщений MTA. Мы запретили запуск Postfix демонов submission(s) — вместо них запустим Dovecot демонов submission-login и submission. Первый будет аутентифицировать почтовых клиентов, а второй — проксировать SMTP-соединение Postfix демону smtpd на порту 10025.

# apt install dovecot-submissiond
# nano /ect/dovecot/conf.d/10-master.conf
service submission-login {
    client_limit = 25
    process_limit = 50
    process_min_avail = 2
    vsz_limit = 64M

    inet_listener submission {
        port = 587
    }
    inet_listener submissions {
        port = 465
        ssl = yes # немедленное SSL-соединение (не STARTTLS)
    }
}

service submission {
    service_count = 1
    client_limit = 1
    process_limit = 50
    process_min_avail = 1
    vsz_limit = 128M
}

service auth {
    # Внутренняя аутентификация IMAP/POP3-клиентов по умолчанию
    unix_listener auth-userdb {
        # права доступа, владелец и группа unix-сокета
        mode = 0660
        user = dovecot
        group = dovecot
    }
}

service imap-login {
    client_limit = 25 # количество клиентов на один процесс
    process_limit = 50 # ограничение на количество процессов
    process_min_avail = 2 # всегда два процесса в режиме ожидания
    vsz_limit = 64M # ограничение на использования памяти
    
    inet_listener imap {
        port = 143 # обычный IMAP, но ssl=required требует STARTTLS
    }
    inet_listener imaps {
        port = 993
        ssl = yes # немедленное SSL-соединение (не STARTTLS)
    }
}

service pop3-login {
    client_limit = 15 # количество клиентов на один процесс
    process_limit = 30 # ограничение на количество процессов
    process_min_avail = 1 # всегда один процесс в режиме ожидания
    vsz_limit = 64M # ограничение на использования памяти
    
    inet_listener pop3 {
        port = 110 # обычный POP3, но ssl=required требует STARTTLS
    }
    inet_listener pop3s {
        port = 995
        ssl = yes # немедленное SSL-соединение (не STARTTLS)
    }
}

# Postfix будет отправлять Docecot сообщения для локальных пользователей по
# протоколу LMTP через unix-сокет /var/spool/postfix/private/dovecot-lmtp
service lmtp {
    unix_listener /var/spool/postfix/private/dovecot-lmtp {
        mode = 0660
        user = postfix
        group = postfix
    }
}

service imap {
    service_count = 1 # процесс завершается после обслуживания одного клиента
    client_limit = 1 # процесс обслуживает одновременно только одного клиента
    process_limit = 100 # максимум 100 одновременных IMAP-соединений
    process_min_avail = 2 # поддерживать два процесса в режиме ожидания
    vsz_limit = 256M # ограничить использование памяти службой 256 МБ
}

service pop3 {
    service_count = 1 # процесс завершается после обслуживания одного клиента
    client_limit = 1 # процесс обслуживает одновременно только одного клиента
    process_limit = 50 # максимум 50 одновременных POP3-соединений
    process_min_avail = 1 # поддерживать один процесс в режиме ожидания
    vsz_limit = 128M # ограничить использование памяти службой 128 МБ
}
Postfix отправляет сообщения Dovecot по протоколу LMTP через unix-сокет, но есть воможность отправки по сети — для этого нужно установить пакет dovecot-lmtpd. В нашем случае это не требуется, потому что серверы Postfix и Dovecot запущены на одной машине. И есть еще один способ передачи сообщений от Postfix к Dovecot — использовать утилиту dovecot-lda. Можно запускать команду из директивы mailbox_command или создать транспорт dovecot.
# nano /ect/dovecot/conf.d/20-submission.conf
hostname = mail.example.com
# Отправлять сообщения от почтовых клиентов Postfix демону smtpd на 127.0.0.1:10025
submission_relay_host = 127.0.0.1
submission_relay_port = 10025
# Отправлять SASL-логин почтового клиента Postfix демону smtpd по протоколу XCLIENT
submission_relay_trusted = yes

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

# nano /etc/dovecot/conf.d/auth-passwdfile.conf.ext
# Аутентификация пользователей для каждого домена из своего файла
passdb {
    driver = passwd-file
    args = scheme=PLAIN username_format=%u /etc/dovecot/users.%d
}

# Для домена example.com (системные пользователи)
userdb {
    driver = passwd-file
    args = username_format=%u /etc/dovecot/users.example.com
    default_fields = mail=maildir:/var/mail/%n
}

# Для доменов example.net и example.org (виртуальные пользователи)
userdb {
    driver = static
    args = uid=vmail gid=vmail mail=maildir:/var/vmail/%d/%n
    # Пропустить поиск в этом блоке, если было совпадение в блоке userdb
    # выше (если найден системный пользователь — дальше искать не нужно)
    skip = found
}

Создаем файлы логинов и паролей почтовых пользователей для каждого домена

# nano /etc/dovecot/users.example.com
evgeniy@example.com:{PLAIN}qwerty:1000:1000
sergey@example.com:{PLAIN}123456:1001:1001
# chmod 640 /etc/dovecot/users.example.com
# chown dovecot:dovecot /etc/dovecot/users.example.com
# nano /etc/dovecot/users.example.net
ivanov@example.net:{PLAIN}ivanov
petrov@example.net:{PLAIN}petrov
smirnov@example.net:{PLAIN}smirnov
# chmod 640 /etc/dovecot/users.example.net
# chown dovecot:dovecot /etc/dovecot/users.example.net
# nano /etc/dovecot/users.example.org
sokolov@example.org:{PLAIN}sokolov
morozov@example.org:{PLAIN}morozov
fedorov@example.org:{PLAIN}fedorov
# chmod 640 /etc/dovecot/users.example.org
# chown dovecot:dovecot /etc/dovecot/users.example.org

Задаем механизмы аутентификации для SMTP и IMAP/POP3 клиентов — PLAIN, LOGIN, CRAM-MD5, DIGEST-MD5. Тут нужно помнить, что при использовании PLAIN и LOGIN — нужно обязательно требовать от клиентов TLS/SSL, чтобы не передавать пароль по сети в открытом виде.

# nano /etc/dovecot/conf.d/10-auth.conf
# Запрещается использование механизмов PLAIN и LOGIN без TLS/SSL
disable_plaintext_auth = yes

# Добавлять доменное имя к логину клиента, который не имеет доменной
# части, то есть преобразовать «username» в «username@example.com»
auth_default_realm = example.com

# Список область аутентификации (REALMS), которые сервер объявляет
# для SASL-механизмов, которые этого требуют (DIGEST-MD5, CRAM-MD5)
auth_realms = example.com example.net example.org

# Какие механизмы аутентификации будут доступны IMAP/POP3-клиентам
auth_mechanisms = plain login digest-md5 cram-md5

# Преобразовать логин клиента перед поиском в базе passdb/userdb. Это
# происходит после того, как auth_default_realm мог добавить домен
auth_username_format = %Lu

# Задержка перед отправкой ответа на неудачную попытку аутентификации
auth_failure_delay = 10 secs

# Для хранения логинов и паролей клиентов будем использовать файл
!include auth-passwdfile.conf.ext

Даем указание Dovecot автоматически создавать стандартный набор папок (Входящие, Отправленные, Черновики, Спам, Корзина)

# nano /etc/dovecot/conf.d/15-mailboxes.conf
namespace inbox {
    # Основная конфигурация namespace inbox (type, location, separator, inbox)
    # должна быть заранее определена в файле конфигурации conf.d/10-mail.conf

    mailbox Drafts { # Черновики
        auto = subscribe
        special_use = \Drafts
    }
    mailbox Junk { # Спам
        auto = subscribe
        special_use = \Junk
    }
    mailbox Trash { # Корзина
        auto = subscribe
        special_use = \Trash
    }
    mailbox Sent { # Отправленные
        auto = subscribe
        special_use = \Sent
    }
}

Чтобы настроить квоты для почтовых ящиков — редактируем файлы 10-mail.conf, 20-lmtp.conf, 20-imap.conf, 20-pop3.conf для подключения плагина + задаем ограничения на почтовый ящик.

# nano /etc/dovecot/conf.d/90-quota.conf
plugin {
    # Драйвер квоты, для Maildir обычно используется maildir
    # (служебный файл maildirsize в каждом почтовом ящике)
    quota = maildir:User quota
    # Квота на размер почтового ящика для всех пользователей,
    # можно потом переопределить в файле passwd-file
    quota_rule = *:storage=524288000
    # Максимальное количество сообщений в почтовом ящике
    quota_rule2 = *:messages=10000
    # Предупреждение, которое Dovecot демон lmtpd отправит
    # Postfix, когда почтовый ящик заполнен полностью
    quota_exceeded_message = Mailbox full, try again later.
    # Максимальный размер сообщения, которое можно сохранять
    quota_max_mail_size = 15M
    # Предупреждение об использовании места в процентах от максимума
    quota_warning = storage=80%% quota-warning 80 %u storage %d
    quota_warning2 = storage=90%% quota-warning 90 %u storage %d
    # Предупреждение о количестве сообщений в процентах от максимума
    quota_warning3 = messages=80%% quota-warning 80 %u messages %d
    quota_warning4 = messages=90%% quota-warning 90 %u messages %d
}

service quota-warning {
    executable = script /usr/local/bin/quota-warning.sh
    user = dovecot
    unix_listener quota-warning {
        user = vmail
    }
}

Dovecot будет проверять место в почтовом ящике виртуального пользователя перед тем, как принять входящее сообщение от Postfix. Если квота превышена — Postfix отправит сообщение отправителю «Mailbox full, try again later». Когда квота близка к исчерпанию, Dovecot предупредит пользователя сообщением «Your mailbox is currently 90% full».

Доставкой сообщений в почтовые ящики системных пользователей занимается Postfix агент local, Dovecot ничего об этом не знает. Postfix извещает отправителя с помощью NDR (Non-Delivery Report), но но не информирует получателя о превышении квоты.

Демоны Postfix (master и прочие)

Postfix состоит из утилит, которые взаимодействуют с пользователями (postdrop, postmap, postqueue, postsuper и т.д.) и демонов, которые работают в фоновом режиме. Все программы, работающие в фоновом режиме, контролируются демоном master. Работа демона master — определить, какую задачу нужно выполнить, и запустить соответствующую программу для ее выполнения.

Демон master

Демон master — управляющая программа Postfix, осуществляет контроль над всеми остальными демонами. Демон master ожидает поступления входящих заданий для передачи их подчиненным демонам. Если работы много, то master может вызвать несколько экземпляров демона. Можно задать количество одновременно работающих экземпляров демона, частоту их повторного использования и величину периода простоя, после которого произойдет остановка экземпляра.

Демоны bounce и defer

Агент передачи сообщений должен уведомить отправителя о невозможности доставить сообщения. В Postfix эту задачу решают демоны bounce (возврат) и defer (отсрочка), а инициируется она диспетчером очередей qmgr. Существует два вида событий, которые могут вызвать отправку уведомлений — неисправимые ошибки и адресат, которого невозможно достичь в течение долгого периода времени. В последнем случае отправляется предупреждение о задержке.

Демон error

Демон error является агентом доставки сообщений, как local или smtp. Этот агент доставки сообщений всегда вызывает возврат сообщений. Обычно он используется, только если нужно настроить домен как недоступный для доставки, направляя почту агенту доставки error. Если сообщение отправляется демону error, то он информирует демон bounce о необходимости отметить, что адресату невозможно доставить почту.

Демон trivial-rewrite

Демон trivial-rewrite действует по запросу демона cleanup для преобразования нестандартных адресов в стандартную форму user@fqdn. Этот демон также занимается разрешением имен адресатов по запросу от диспетчера очередей qmgr.

Демон trivial-rewrite выполняет следующие задачи

  1. Определяет, является ли адрес локальным (домен в mydestination, virtual_mailbox_domains и др.)
  2. Определяет, нужно ли переслать письмо через внешний relay-сервер (согласно значению директивы relay_domains)
  3. Поиск транспорта в таблице поиска, которая задана в transport_maps для переопределения транспорта по умолчанию
  4. Выбор транспорта для доставки в локальный почтовый ящик (в том числе виртуальный) или для доставки на удалённый сервер

Директивы, которые управляют поведением демона

  1. mydestination — список доменов, которые считаются локальными для сервера (локальная доставка)
  2. relay_domains — определяет список доменов, для которых сервер выполняет функцию relay (пересылки)
  3. transport_maps — переопределение транспорта по умолчанию для определённых доменов/адресов
  4. virtual_alias_maps — определяет переадресацию виртуальных адресов на реальные получателей
  5. virtual_mailbox_domains и virtual_mailbox_maps — поддержка виртуальных почтовых ящиков
  6. sender_dependent_default_transport_maps — задать транспорт доставки в зависимости от отправителя

Демон qmgr

Демон qmgr управляет очередями, это сердце почтовой системы Postfix. Он распределяет задачи доставки между демонами local, virtual, smtp, lmtp и pipe. После того как исполнитель задачи выбран, демон qmgr передает ему информацию об имени и пути файла из очереди, адрес отправителя сообщения, хост назначения (для сообщений удаленным пользователям) и один или несколько адресов получателей.

  • Менеджер qmgr поддерживает маленькую очередь active, в которой ожидают отправки буквально несколько сообщений. Эта очередь фактически выполняет роль небольшого окна для потенциально бoльших очередей incoming и deferred, предотвращая нехватку памяти.
  • Если Postfix не может незамедлительно доставить сообщение, то qmgr помещает его в очередь deferred. Хранение временно недоставимых сообщений в отдельной очереди гарантирует, что нормальный доступ к очереди не будет тормозиться большим объемом почты, обработка которой не завершена.

Демон local

Демон local отвечает за доставку в локальные почтовые ящики, то есть когда домен получателя соответствует опции mydestination. Если в mydestination указано несколько значений, они рассматриваются как псевдонимы одного домена. Демон local может записывать данные в почтовые ящики форматов mbox (unix-style mailbox) и maildir (qmail-style maildir).

Демон smtp

Демон smtp — это SMTP-клиент, который передает исходящие сообщения удаленным адресатам. Этот демон ищет почтовые серверы назначения, сортирует список по предпочтениям и проверяет каждый адрес до тех пор, пока не найдет сервер, который ответит. При большой нагрузке в системе Postfix обычно работает несколько демонов smtp одновременно.

Демон lmtp

Демон lmtp взаимодействует с локальным и удаленным серверами почтовых ящиков по протоколу LMTP (Local Mail Transfer Protocol). Протокол может использоваться вместо SMTP в тех случаях, когда на принимающей стороне не запущен демон SMTP.

Демон pipe

Демон pipe вызывает внешную утилиту или скрипт и отправляет тело сообщения по каналу (pipe) на их стандартный ввод.

Демон pickup

Демон pickup забирает сообщения, помещенные в очередь maildrop локальной клиентской программой отправки почты, например sendmail. После выполнения ряда проверок pickup передает сообщения демону cleanup.

Работа pickup состоит из следующих этапов

  1. Сканирование каталога maildrop в поисках новых файлов сообщений
  2. Блокировка сообщения для избежания конфликтов с другими процессами
  3. Чтение содержимого письма и заголовков конверта (From, To и прочие)
  4. Проверка целостности структуры сообщения — неполное или поврежденое письмо отклоняется
  5. Передача сообщения в демону cleanup для дальнейшей обработки
  6. После прохождения через cleanup сообщение помещается в активную очередь

Демон smtpd

Демон smtpd обеспечивает взаимодействие с сетевыми почтовыми клиентами, отправляющими сообщения через Postfix по SMTP. Демон smtpd проводит ряд проверок для защиты остальной части системы Postfix. Его можно настроить так, чтобы он занимался проверкой входящей почты на спам (локальные или сетевые черные списки, поиск в DNS и т.д.). Демон smtpd передает сообщение демону cleanup.

Демон cleanup

Демон cleanup реализует последний этап обработки новых сообщений. Он добавляет недостающие заголовки, корректирует преобразование адреса и (если надо) извлекает адреса получателей из заголовков сообщений. Демон cleanup помещает результат в очередь incoming, затем уведомляет менеджера очередей о прибытии нового сообщения.

Работа cleanup состоит из следующих этапов

  • Проверка структуры сообщения — анализ заголовков и тела письма на соответствие стандартам
  • Нормализация заголовков — добавляет недостающие или исправляет существующие
  • Переписывание адресов — применяет правила из canonical_maps, recipient_canonical_maps, sender_canonical_maps
  • Применение политик безопасности — например, ограничение размера сообщений с использованием message_size_limit
  • Проверка и применение фильтрации содержимого, если настроены внешние или встроенные фильтры
  • Формирование ENVELOPE (конверта) сообщения, который будет использоваться при последующей доставке
  • Определяет, надо ли разделить многоадресное сообщение на отдельные записи в очереди

На работу демона влияют директивы конфигурации

  • canonical_maps — карты для переписывания адресов отправителей и получателей в заголовке и конверте
  • recipient_canonical_maps — переписывание только адресов получателей
  • sender_canonical_maps — переписывание только адресов отправителей
  • header_checks — проверка (и возможно изменения) заголовков сообщения
  • mime_header_checks — проверка MIME-заголовков писем
  • nested_header_checks — проверка заголовков вложенных сообщений
  • body_checks — проверка содержимого тела сообщения
  • message_size_limit — ограничение на максимальный размер письма
  • receive_override_options — позволяет принудительно отключить или включить некоторые обработки
  • cleanup_service_name — позволяет использовать другую службу вместо cleanup

Если сообщение превышает установленное ограничение размера во время обработки, оно немедленно отклоняется. Отправляющему SMTP-клиенту возвращается ошибка SMTP, обычно с кодом 552 (Exceeded storage allocation).

Демон anvil

Демон anvil — внутренний сервис Postfix, задачей которого является отслеживание и контроль активности SMTP-клиентов. Он играет важную роль в защите сервера от злоупотреблений, перегрузки, атак методом перебора паролей, а также обеспечивает возможность применения различных ограничений на уровне ip-адресов.

Когда клиент устанавливает SMTP-соединение, Postfix запоминает ip-адрес клиента. Любой процесс Postfix (например, smtpd) может отправить запрос к anvil, чтобы узнать кол-во подключений с ip-адреса, с какой скоростью поступают сообщения. В зависимосости от запроса — anvil возвращает информацию или увеличивает счётчики.

# Период времени, на протяжении которого действуют
# ограничения для клиента, перечисленые ниже
anvil_rate_time_unit = 60s

# клиент может отправить 30 сообщений за 60 секунд
smtpd_client_message_rate_limit = 30
# не больше 30 получателей сообщений за 60 секунд
smtpd_client_recipient_rate_limit = 30
# количество одновременно разрешенных подключений
smtpd_client_connection_count_limit = 10
# количество разрешенных подключений за 60 секунд
smtpd_client_connection_rate_limit = 30

# Клиенты, на которых не действуют ограничения. По
# умолчанию $mynetworks, но у нас исключений нет
smtpd_client_event_limit_exceptions =

Очередь сообщений Postfix

Postfix имеет четыре основные очереди — maildrop, incoming, active и deferred. Локально отправленная почта (с помощью команды sendmail) сначала помещается в очередь maildrop, потом перемещается в очередь incoming после очистки демоном cleanup. Очередь incoming предназначена для почты, которая все еще поступает или которую менеджер очереди еще не просмотрел. Очередь active — для почты, с которой менеджер очереди работает в текущий момент. Почта, которая не может быть доставлена, отправляется в очередь deferred, чтобы не мешать другим доставкам.

Менеджер очередей qmgr хранит в памяти информацию только об активной очереди. Размер активной очереди ограничен намеренно — менеджер очередей никогда не должен исчерпывать рабочую память из-за пиковой нагрузки. Всякий раз, когда в активной очереди есть место, менеджер очередей пропускает одно сообщение из incoming и одно из deferred. Это гарантирует, что новая почта будет доставлена ​​даже при большом объеме невыполненных задач.

Очередь maildrop

Переданные утилитой sendmail сообщения ожидают обработки в очереди maildrop. Можно добавлять сообщения в очередь maildrop даже тогда, когда Postfix не запущен. Демон pickup просматривает и забирает сообщени из очереди maildrop периодически, а также по уведомлению от postdrop. Утилита postdrop — это setgid-помощник, который позволяет непривилегированной утилите sendmail ставить сообщения в очередь maildrop и уведомлять службу pickup о поступлении сообщения.

Очередь incoming

Все новые сообщения, попадающие в систему очередей Postfix, отправляются демоном cleanup в очередь incoming. Новые файлы очередей создаются от имени пользователя postfix с правами доступа 0600. Как только файл очереди готов к дальнейшей обработке, демон cleanup изменяет права на 0700.

Диспетчер очередей просматривает очередь incoming, перемещая файлы с правами 0700 в очередь active, и следит за размером очереди. По умолчанию очередь active может содержать не более 20000 сообщений, но можно установить другое значение с помощью директивы qmgr_message_active_limit.

Очередь deferred

Если сообщение не удается доставить по какой-либо временной причине — Postfix помещает его в очередь deferred. Диспетчер очередей периодически просматривает очередь deferred, чтобы перемещать отложенные сообщения обратно в очередь active. Интервал между просмотрами определяется опцией queue_run_delay.

Очередь active

Очередь active в некотором роде является аналогом очереди запуска процессов операционной системы. Сообщения в очереди active готовы к отправке, но не обязательно находятся в процессе отправки.

Очередь hold

Заданные в настройках правила доступа и проверки тела и заголовков — могут привести к тому, что сообщения будут автоматически исключены из нормальной обработки и помещены на неопределенное время в очередь hold. Сообщения остаются в очереди hold до тех пор, пока не вмешается администратор. Периодические попытки отсылки сообщений из очереди hold не предпринимаются. Используя команду postsuper, администратор может вручную поместить сообщения в очередь hold или вернуть их оттуда, переместив в очередь deferred.

Теоретически сообщения могут оставаться в очереди hold более долгий срок, чем указано в опции maximal_queue_lifetime для времени жизни файла очереди (после чего недоставленные сообщения возвращаются обратно отправителю). Если необходимо вернуть из очереди hold более старые сообщения, то можно использовать команду postsuper -r для перемещения их в очередь maildrop. В этом случае сообщение получит новую временную метку и еще один шанс быть доставленным.

Очередь corrupt

Очередь corrupt содержит поврежденные файлы сообщений. Вместо того чтобы удалять их, Postfix сохраняет их для того, чтобы администратор почтовой системы мог исследовать их с помощью postcat.

Утилиты управления Postfix

Postfix поставляется с набором утилит командной строки, которые помогают решать административные задачи. Они выполняют разнообразные функции (обращение к картам, просмотр файлов очередей, постановка сообщений в очередь и извлечение из очереди, изменени конфигурации), но имеют одну общую характеристику — их имена начинаются с «post».

Утилита postfix

Утилита останавливает, запускает и перезагружает конфигурацию с помощью параметров stop, start и reload. Кроме того, позволяет проверить конфигурацию Postfix с помощью параметра check.

Утилита postalias

Утилита создает индексированную карту псевдонимов из файла псевдонимов, когда ключ и значение отделяются двоеточием. Нужна главным образом для для создания /etc/aliases.db из /etc/aliases — этот файл достался Postfix от Sendmail.

# nano /etc/aliases
# файл псевдонимов /etc/aliases, куда перенаправлять почту
# для пользователей root и postmaster
root:          evgeniy
postmaster:    evgeniy
# postalias hash:/etc/aliases # создание индексированной карты псевдонимов /etc/aliases.db

Утилита postcat

Утилита выводит содержимое сообщения, находящегося в почтовой очереди.

Утилита postmap

Главная задача утилиты — построение индексированных карт на основе обычных текстовых файлов. Пример создания карты /etc/postfix/virtual.db на основе /etc/postfix/virtual.

# postmap hash:/etc/postfix/virtual

Тип создаваемой карты hash может быть опущен, тогда тип определается значением опции default_database_type файла конфигурации. По умолчанию эта опция имеет значение hash.

# postconf -d default_database_type
default_database_type = hash

Кроме того, утилита позволяет тестировать карты любого вида, поддерживаемые текущей конфигурацией Postfix. Это полезно при отладке конфигурации, в которой поиск по карте не работает. Можно проверить, доступны ли в действительности серверу Postfix ключи и значения карты.

Утилита postdrop

Утилита считывает почту из стандартного ввода и записывает результат в каталог maildrop; работает в связке с утилитой sendmail.

Утилита postkick

Утилита отправляет запрос демону Postfix по локальному транспортному каналу, делая межпроцессное взаимодействие Postfix доступным для сценариев оболочки и других программ.

Утилита postlock

Утилита предоставляет монопольный доступ к файлам mbox, в которые выполняет запись Postfix, а затем исполняет команду, удерживая блокировку. Получаемая от postlock блокировка совместима с агентом доставки Postfix local. Postfix не трогает файл во время исполнения утилиты.

Утилита postlog

Утилита позволяет внешним программам, таким как сценарии командного интерпретатора, писать сообщения в журнал Postfix.

Утилита postqueue

Утилита postqueue — это пользовательский интерфейс для очередей Postfix, предоставляющий возможности, обычно доступные в рамках выполнения sendmail.

Опция -f предписывает доставить всю стоящую в очереди почту, что является эквивалентом postfix flush или sendmail -q.

# postqueue -f

Опция -p выводит содержимое очереди, это эквивалент mailq.

# postqueue -p

Опция -s domain предписывает доставить всю стоящую в очереди почту для домена domain, что является эквивалентом sendmail -q domain.

# postqueue -s example.com

Утилита postsuper

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

  • Опция -d удаляет сообщение с указанным идентификатором из указанной очереди (очередей) сообщений
  • Опция -h помещает сообщение на удержание, так чтобы не предпринимались попытки его доставки
  • Опция -H освобождает сообщение, в настоящее время находящиеся на удержании
  • Опция -p очищает временные файлы, оставшиеся после аварий
  • Опция -r повторно ставит в очередь сообщение с указанным идентификатором из указанной очереди сообщений
  • Опция -s проверяет и исправляет структуру очереди

Карты (таблицы) поиска Postfix

Карты — это файлы и базы данных, которые Postfix использует для поиска информации. Карты могут иметь разное назначение, но у них есть общий признак: левая сторона (left-hand side, LHS), называемая ключом, и правая сторона (right-hand side, RHS), называемая значением.

Postfix может использовать различные виды карт. Доступные форматы зависят от того, каким образом был скомпилирован Postfix. Определить поддерживаемые форматы можно с помощью следующей команды.

# postconf -m
btree
cidr
environ
fail
hash
..........

Индексированные карты — это двоичные базы данных, созданные из обычных текстовых файлов посредством таких команд, как newaliases, postalias и postmap. Двоичные карты индексированы, так что Postfix может быстро извлекать значение, сопоставленное ключу. Для улучшения производительности демоны Postfix открывают такие карты при запуске и не перечитывают их заново до тех пор, пока не заметят изменения файлов карт в файловой системе. Для перезагрузки карты демон завершает свою работу, и демон master запускает новый демон.

Чаще всего используются индексированные карты, созданные из текстовых файлов. Идентифицировать файл карты можно по его имени, состоящему из имени исходного файла и суффикса, обозначающего формат индекса. Например, файл карты aliases, созданный командой postalias, называется aliases.db.

Линейные карты — это обычные текстовые файлы. Postfix читает такие файлы сверху вниз, что отличает их от индексированных карт. Это весьма важное отличие, т.к. первое совпадение в файле определяет то действие, которое выполнит Postfix. Последующие записи Postfix игнорирует вне зависимости от того, удовлетворяют ли они запросу или нет.

Для того чтобы демоны Postfix заметили изменения в линейных картах — нужно выполнить команду postfix reload. Если скорость работы не очень важна, то можно установить опцию max_use для определения времени жизни демонов. Как только демон обработает то количество задач, которое указано в данной опции, он будет остановлен и перезапущен демоном master. После перезапуска все необходимые карты будут считаны заново.

По мере роста линейных карт демоны Postfix будут тратить больше времени на их обработку. Это особенно верно в отношении проверок тела или заголовка, т.к. демон cleanup должен сравнить каждую строку тела и заголовка с каждой строкой карты. Это вызывает значительное снижение скорости работы, особенно если в файле конфигурации задано много опций *_checks, которые используют карты типа regexp и pcre. Если такое случается, обычно это означает, что пришло время поручить комплексную фильтрацию спама внешнему приложению.

Индексированная карта hash

Файл баз данных создается с помощью утилиты postmap или postalias и имеет расширение db. Имя базы данных, используемое в конфигурации hash:/path/to/file, представляет собой имя файла базы данных без расширения db.

В исходном файле данные разделены двоеточием

# nano /etc/aliases
# файл псевдонимов /etc/aliases, куда перенаправлять почту
# для пользователей root, abuse и postmaster
root:          evgeniy
abuse:         evgeniy
postmaster:    evgeniy
# postalias hash:/etc/aliases # создание индексированной карты псевдонимов /etc/aliases.db

В исходном файле данные разделены пробелами

# nano /etc/postfix/check_sender_access
# карта доступа /etc/postfix/check_sender_access, кто может отправлять почту
support@shop.com    PERMIT
shop.com            REJECT
# postmap hash:/etc/postfix/check_sender_access # создание карты /etc/postfix/check_sender_access.db
Утилита postmap создает индесированный файл, когда ключ и значение отделяются пробелами. Утилита postalias создает индексированный файл, когда ключ и значение отделяются двоеточием. Вместо postalias можно успользовать утилиту newaliases — отличие в том, что newaliases создает индексированные файлы для всех баз данных, указанных в опции alias_database.

Индексированная карта lmdb

Файл баз данных создается с помощью утилиты postmap или postalias и имеет расширение lmdb. Имя базы данных, используемое в конфигурации lmdb:/path/to/file, представляет собой имя файла базы данных без расширения lmdb. Рекомендуется как более современная замена hash, требуется установить пакет postfix-lmdb.

# apt install postfix-lmdb

Линейная карта texthash

Для этой карты не нужно запускать команду postmap перед использованием в конфигурации. Чтобы Postfix обнаружил изменения — нужно выполнить команду postfix reload. Для указания того, что это линейная карта, в файле конфигурации используется префикс texthash (для индексированной карты используется префикс hash).

# nano /etc/postfix/permit_trusted_recipients
# карта доступа, которая разрешает принимать почту для abuse и
# postmaster, чтобы почтовый сервер отвечал требованиям RFC
postmaster@    PERMIT
abuse@         PERMIT

Линейная карта regexp

Карта поиска на основе POSIX Regular Expressions. Входные данные сравниваются с шаблоном регулярного выражения. Подробнее здесь и здесь

/^Subject: make money fast/    REJECT
/^To: friend@example\.com/     REJECT

Линейная карта pcre

Карта поиска на основе Perl Compatible Regular Expressions. Входные данные сравниваются с регулярным выражением. Подробнее здесь и здесь

/^mail\.example\.com$/        550 Don't use my hostname
/^\[222\.222\.222\.222\]$/    550 Don't use my ip address

Для поддержки карт pcre нужно установить пакет postfix-pcre

# apt install postfix-pcre

Линейная карта cidr

Линейная карта для поиска ip-адресов с учетом маски подсети. Позволяет обрабатывать диапазоны ip-адресов, а не только конкретные адреса.

# nano /etc/postfix/permit_loopback_client
# Разрешить клиентов для loopback
127.0.0.1/8    PERMIT

Как Postfix обращается к картам

Когда Postfix читает карту — он обращается к карте несколько раз, пока не будет найдено совпадение. Сначала Postfix пробует найти полное совпадение, потом совпедение с доменом, потом совпадение без домена.

  1. localpart@domainpart — точное совпадение с указанным почтовым адресом.
  2. domainpart — совпадение domainpart с доменной частью почтового адреса. Шаблону domainpart также соответствуют поддомены, если директива конфигурации parent_domain_matches_subdomains содержит значение smtpd_access_maps (по умолчанию это так). В противном случае нужно добавить точку, то есть указать .domainpart для поиска совпадающих поддоменов.
  3. localpart@ — совпадает со всеми почтовыми адресами с указанной localpart, вне зависимости от того, какому домену они принадлежат.
  4. Нет совпадения. Если совпадение не найдено, Postfix возвращает no match found и запрос завершается с ошибкой.

В картах virtual_alias_maps, virtual_mailbox_maps, canonical_maps, relocated_maps допускается использовать ключ @domainpart — это означает совпадение с любым адресом почты для домена.

В некоторых картах невозможен поиск пустого адреса отправителя. По умолчанию в качестве ключа поиска для пустого адреса отправителя Postfix использует <>. Значение задается с помощью директивы smtpd_null_access_lookup_key файла конфигурации.

Проверка карт доступа

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

some-domain.com      smtp:[smtp.other-domain.com]
alert@example.com    telegram
# postmap -q "alert@example.com" hash:/etc/postfix/transport
telegram
192.168.1.0/24    PERMIT
10.0.0.0/8        REJECT
# postmap -q "192.168.1.55" cidr:/etc/postfix/client_access
PERMIT
/^Subject: .*viagra/i        REJECT
/^From: .*@important.com/    PREPEND X-Important: YES
# postmap -q "Subject: get viagra now" pcre:/etc/postfix/header_checks
REJECT

Ограничения Postfix

Ограничения позволяют почтовому серверу принять или отвергнуть сообщения на основе данных SMTP-соединения между клиентом и сервером. Информация, полученная из этого диалога, позволяет Postfix наложить или отменить ограничения на клиента, отправителя и получателя.

1. Простые ограничения

smtpd_helo_required

Это ограничение требует от клиентов отправки команды HELO/EHLO в начале сеанса SMTP — согласно документам RFC 821 и RFC 2821.

strict_rfc821_envelopes

Это ограничение регулирует степень терпимости Postfix к ошибкам в адресах, указанных в команде MAIL FROM или RCPT TO.

disable_vrfy_command

SMTP-команда VRFY позволяет клиентам проверять существование получателя. Это ограничение позволяет запретить команду VRFY.

2. Триггеры ограничений

Каждый новый шаг SMTP-соединения отмечает момент, когда демон smtpd получает очередную порцию информации о клиенте и сообщении, которое тот хочет передать. Postfix использует эти этапы для инициирования триггеров ограничений; для каждого этапа существует отдельная опция файла конфигурации, имя которой образовано из названия активного демона, названия этапа и его предназначения.

# Ограничения на этапе установке подключения
smtpd_client_restrictions = правило, правило, правило
# Ограничения на этапе команды HELO/EHLO
smtpd_helo_restrictions = правило, правило, правило
# Ограничения на этапе команды MAIL FROM
smtpd_sender_restrictions = правило, правило, правило
# Ограничения на этапе команды RCPT TO
smtpd_relay_restrictions = правило, правило, правило
# Ограничения на этапе команды RCPT TO
smtpd_recipient_restrictions = правило, правило, правило
# Ограничения на этапе команды DATA
smtpd_data_restrictions = правило, правило, правило
# Ограничения на команду ETRN
smtpd_etrn_restrictions = правило, правило, правило

smtpd_client_restrictions

Этот триггер применяется к ip-адресу или имени хоста клиента либо к ним обоим. По умолчанию никаких ограничений нет.

# postconf -d smtpd_client_restrictions  # значение по умолчанию
smtpd_client_restrictions =

smtpd_helo_restrictions

Этот триггер применяется к аргументу HELO/EHLO клиента и к ip-адресу и (или) имени хоста клиента. По умолчанию никаких ограничений нет.

# postconf -d smtpd_helo_restrictions  # значение по умолчанию
smtpd_helo_restrictions =

smtpd_sender_restrictions

Это первый набор триггеров, который относится к частям конверта. Postfix применяет его к отправителю конверта, аргументу HELO/EHLO и клиенту. По умолчанию никаких ограничений нет.

# postconf -d smtpd_sender_restrictions  # значение по умолчанию
smtpd_sender_restrictions =

smtpd_relay_restrictions

Этот триггер применяется к получателю конверта, отправителю конверта, аргументу HELO/EHLO и к ip-адресу и (или) имени хоста клиента. Это ограничение ретрансляции почты для доменов, указанных в опции relay_domain.

# postconf -d smtpd_relay_restrictions  # значение по умолчанию
smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated, defer_unauth_destination

В версиях Postfix до 2.10 правила для ретрансляции и блокировки спама были объединены в smtpd_recipient_restrictions, что приводило к конфигурациям, подверженных ошибкам. Начиная с Postfix 2.10, правила ретрансляции лучше задавать в smtpd_relay_restrictions, так что разрешающая политика блокировки спама в smtpd_recipient_restrictions больше не будет приводить к разрешающей политике ретрансляции почты.

Триггеры smtpd_recipient_restrictions и smtpd_relay_restrictions срабатывают в момент получения команды RCPT TO, но сначала smtpd_relay_restrictions, а уже потом smtpd_recipient_restrictions. Это задается опцией конфигурации smtpd_relay_before_recipient_restrictions, которая по умолчанию имеет значение yes (начиная с версии 3.6).

smtpd_recipient_restrictions

Этот триггер применяется к получателю конверта, отправителю конверта, аргументу HELO/EHLO и к ip-адресу и (или) имени хоста клиента.

# postconf -d smtpd_recipient_restrictions  # значение по умолчанию
smtpd_recipient_restrictions =

smtpd_data_restrictions

Этот триггер выявляет клиентов, которые отправляют содержимое письма прежде, чем Postfix успевает ответит на команду DATA. По умолчанию никаких ограничений нет.

# postconf -d smtpd_data_restrictions  # значение по умолчанию
smtpd_data_restrictions =

smtpd_etrn_restrictions

Этот триггер может запрещать или разрешать клиентам выполнять команду ETRN. Команда была разработана для SMTP-серверов с непостоянным подключением к интернету. С помощью ETRN можно сообщить почтовому серверу своего провайдера — «Пожалуйста, доставьте всю мою почту сейчас». Postfix ищет почту в очереди для клиента и доставляет ее, подключаясь к SMTP-серверу клиента.

По умолчанию разрешение есть у любого клиента. Но выполянять команду Postfix будет только для тех пунктов назначения, которые перечислены в опции fast_flush_domains. Значение по умолчанию для этой опции равно $relay_domains.

# postconf -d fast_flush_domains  # значение по умолчанию
fast_flush_domains = $relay_domains

3. Правила для триггеров

Все правила по результатам проверки возвращают действие — либо OK (или PERMIT), либо REJECT, либо DUNNO (не знаю).

Если правило вернуло REJECT, никакие правила в данном ограничении больше не проверяются. Более того, никаких других проверок в последующих ограничениях не проводится также. Письмо отбрасывается, точнее происходит завершение соединения с клиентским компьютером или другим почтовым сервером с выдачей соответствующего кода.

Если правило вернуло OK (PERMIT), проверка считается успешной, и все остальные правила в данном ограничении не поверяются. Происходит переход на следующее ограничение.

Если правило вернуло DUNNO, проверка продолжается в следующем правиле для данного ограничения. Если ВСЕ правила пройдены с результатом DUNNO, считается, что проверка для данного ограничения завершилась УСПЕШНО, и происходит переход на следующее ограничение.

Порядок правил для триггера очень важен, т.к. первое совпадение, возвращающее OK (PERMIT) или REJECT, сразу же останавливает оценку текущего набора правил (при этом REJECT означает, что клиент, получатель или отправитель безоговорочно отвергается). Postfix читает и применяет правила сверху вниз или, если они записаны в одну строку, слева направо.

Правила, которые начинаются с reject_ — возвращают либо REJECT, либо DUNNO. Правила, которые начинаются с permit_ — возвращают либо OK (PERMIT), либо DUNNO.

4. Момент оценки

Вообще говоря, Postfix не осуществляет оценку и выполнение ограничений сразу же после наступления соответствующего этапа SMTP-взаимодействия. Postfix ждет того момента, когда клиент отправит данные первого получателя конверта RCPT TO. Наличие этой задержки объясняется тем, что существует достаточно большое количество клиентских программ, которые не обращают внимания на сообщения почтового сервера до команды RCPT TO.

Поэтому разработчики Postfix предусмотрели специальную опцию smtpd_delay_reject, которая включена по умолчанию и предписывает отрабатывать весь сеанс, от установления соединения до RCPT TO включительно и лишь после этого запускать проверки. Побочным эффектом такого поведения является возможность в ограничениях, находящихся ВЫШЕ, использовать правила из ограничений, находящихся НИЖЕ.

Может показаться, что в файле конфигурации достаточно использовать только одно ограничение smtpd_recipient_restrictions, потому что в нем можно использовать любые правила из вышестоящих ограничений — но это не так. Давайте рассмотрим два фрагмента конфигурации.

# Ограничения на этапе команды MAIL FROM
smtpd_sender_restrictions =
    # Если правило возвращает PERMIT — остальные правила в этом ограничении
    # не проверяются и происходит переход к следующему ограничению
    check_sender_access hash:/etc/postfix/check_sender_access
    ..........

# Ограничения на этапе команды RCPT TO
smtpd_recipient_restrictions =
    check_recipient_access hash:/etc/postfix/check_recipient_access
    ..........
# Ограничения на этапе команды RCPT TO
smtpd_recipient_restrictions =
    # Если правило возвращает PERMIT — остальные правила в этом ограничении не
    # проверяются и происходит переход к следующему ограничению (которого нет)
    check_sender_access hash:/etc/postfix/check_sender_access
    # Это правило и все следующие будут пропущены, если предыдущее вернуло
    # PERMIT — нельзя оценить получателя после оценки отправителя
    check_recipient_access hash:/etc/postfix/check_recipient_access
    ..........

5. Специальные адреса почты

На почтовом сервере имеются два адреса, для которых всегда нужно принимать сообщения; они необходимы для того, чтобы работа почтового сервера соответствовала RFC.

Нужно всегда принимать почту, адресованную администратору почтовой системы postmaster — это центр обработки информации для вопросов, связанных с электронными сообщениями. Пользователи должны иметь возможность обратиться к администратору за помощью.

Обязательный прием почты для адресата abuse гарантирует, что пользователи смогут уведомить о возможных почтовых злоупотреблениях, исходящих от нашего почтового сервера.

Для этого нужно создать карту /etc/postfix/permit_trusted_recipients и использовать правило check_recipient_access в триггере smtpd_recipient_restrictions.

# nano /etc/postfix/permit_trusted_recipients
# карта доступа, которая разрешает принимать почту для abuse и
# postmaster, чтобы почтовый сервер отвечал требованиям RFC
postmaster@    PERMIT
abuse@         PERMIT

Дальше можем создать индексированную карту, если выполним команду postmap — тогда в файле конфигурации указываем hash:/etc/postfix/permit_postmaster_abuse. Или можем использовать линейную карту — тогда нужно выполнить команду postfix reload. Изменения в индексированных картах Postfix обнаруживает самостоятельно и не требует выполнения команды postfix reload.

# Ограничения на этапе команды RCPT TO
smtpd_recipient_restrictions =
    ..........
    # Всегда принимать почту для учетных записей postmaster и abuse
    check_recipient_access texthash:/etc/postfix/permit_postmaster_abuse
    ..........

6. Пример конфигурации

Для начала добавляем опции конфигурации smtpd_helo_required, strict_rfc821_envelopes и disable_vrfy_command. Потом для каждого триггера добавляем правила. Если есть сомнения по поводу какого-то правила — добавляем перед ним WARN_IF_REJECT. На начальном этапе настройки есть смысл включить опцию soft_bounce — чтобы не отбрасывать лишнего.

Правило permit_mynetworks обозначает важную границу между клиентами внутренней сети и внешними клиентами. Правила, заданные выше этого элемента (включая его самого), относятся как к внутренним, так и к внешним клиентам, в то время как параметры ниже permit_mynetworks применяются только к внешним клиентам.

smtpd_helo_required = yes
strict_rfc821_envelopes = yes
disable_vrfy_command = yes

# При попытке клиента отправить письмо несуществующему пользователю по
# умолчанию сервер отвечает кодом 550 с сообщением  «user unknown in
# local recipient table» (или другой таблице поиска). При установке
# значения «no» сервер будет отвечать сообщением «user unknown».
show_user_unknown_table_name = no

# Не принимать почту на адреса, для которых у нас нет почтовых ящиков.
# Вместо этой опции можно использовать правило reject_unlisted_recipient
# в триггере smtpd_recipient_restrictions. В любом случае нужно создать
# список почтовых адресов получателей, которые нужно принимать.
#smtpd_reject_unlisted_recipient = yes

# Правила для ограничений применяются слева направо (если они записаны в
# одну строку) или сверху вниз (если каждое правило на отдельной строке)

# Ограничения на этапе установке подключения
smtpd_client_restrictions =
    # Разрешить клиентов из доверенных сетей (опция $mynetworks)
    permit_mynetworks
    # Разрешить клиентов, прошедших аутентификацию по RFC 4954
    permit_sasl_authenticated
    # Отклонять клиентов, у которых доменное имя из PTR-записи
    # не разрешается в тот же ip-адрес по A-записи
    reject_unknown_client_hostname

# Ограничения на этапе команды HELO/EHLO
smtpd_helo_restrictions =
    # Отклонять клиентов, использующих неправильный синтаксис домена
    reject_invalid_helo_hostname
    # Отклонять клиентов, указывающих не полное доменное имя FQDN
    reject_non_fqdn_helo_hostname
    # Разрешить клиентов из доверенных сетей (опция $mynetworks)
    permit_mynetworks
    # Разрешить клиентов, прошедших аутентификацию по RFC 4954
    permit_sasl_authenticated
    # Отклонять клиентов, ДНС-имя которых из команды HELO/EHLO не
    # имеет A- или MX-записи или ip-адрес не является правильным
    reject_unknown_helo_hostname

# Ограничения на этапе команды MAIL FROM
smtpd_sender_restrictions =
    # Отклонять почту от отправителей с неполным доменным именем
    reject_non_fqdn_sender
    # Отклонять почту от отправителей из несуществующих доменов
    reject_unknown_sender_domain
    # Разрешить клиентов из доверенных сетей (опция $mynetworks)
    permit_mynetworks
    # Разрешить клиентов, прошедших аутентификацию по RFC 4954
    permit_sasl_authenticated
    # Запросить сервер, обслуживающий указанный адрес отправителя,
    # на предмет существования на нём пользователя с этим адресом
    reject_unverified_sender

# Ограничения на этапе команды RCPT TO (relay)
smtpd_relay_restrictions =
    # Разрешить клиентов, прошедших аутентификацию по RFC 4954
    permit_sasl_authenticated
    # Разрешить клиентов из доверенных сетей (опция $mynetworks)
    permit_mynetworks
    # Отклонять почту, если домена получателя нет в $mydestination,
    # $virtual_alias_domains, $virtual_mailbox_domains, $relay_domains
    reject_unauth_destination
    # Отклонять почту для получателя, если наш сервер не является
    # конечной точкой, а домен получателя не имеет A- и MX-записей
    reject_unknown_recipient_domain

# Ограничения на этапе команды RCPT TO (spam)
smtpd_recipient_restrictions =
    # Отклонять почту для получателей с неполным доменным именем
    reject_non_fqdn_recipient
    # Всегда принимать почту для учетных записей postmaster и abuse
    check_recipient_access hash:/etc/postfix/permit_recipients
    # Разрешить клиентов, прошедших аутентификацию по RFC 4954
    permit_sasl_authenticated
    # Разрешить клиентов из доверенных сетей (опция $mynetworks)
    permit_mynetworks
    # Отклонять почту на адреса, для которых нет почтовых ящиков
    reject_unlisted_recipient

# Отклонять почту, если клиент отправляет команды раньше времени
smtpd_data_restrictions = reject_unauth_pipelining

# Ограничения на команду ETRN от почтовых серверов
smtpd_etrn_restrictions = REJECT

Правило reject_unknown_client_hostname требует, чтобы ip-адрес клиента резолвился в имя через PTR-запись, а имя через A-запись резолвилось в тот же ip-адрес. Код ответа клиенту задается опцией unknown_client_reject_code, по умолчанию 450.

# postconf -d unknown_client_reject_code
unknown_client_reject_code = 450

Получить имя по ip-адресу и ip-адрес по имени — можно с помощью команды host

$ host 94.100.180.160
160.180.100.94.in-addr.arpa domain name pointer smtp.mail.ru.
$ host smtp.mail.ru
smtp.mail.ru has address 94.100.180.160

Правило reject_unknown_helo_hostname требует, чтобы имя хоста в команде HELO/EHLO имело A- или MX- запись. Код ответа клиенту задается опцией unknown_hostname_reject_code, по умолчанию 450.

# postconf -d unknown_hostname_reject_code
unknown_hostname_reject_code = 450

Правило reject_unverified_sender требует убедиться в существовании пользователя с этим адресом. Наш сервер открывает встречную SMTP-сессию, пытаясь отправить письмо по адресу отправителя. Если удаётся успешно пройти этап RCPT TO с этим адресом (принимающий сервер не заявил, что указанного ящика нет) — считается, что адрес существует. Код ответа клиенту задается опцией unverified_sender_reject_code, по умолчанию 450.

# postconf -d unverified_sender_reject_code
unverified_sender_reject_code = 450

Правило reject_unknown_recipient_domain требует — если домен из RCPT TO не является конечным пунктом назначения для нашего сервера, то домен должен иметь A- и MX- записи. Такое письмо нужно будет отправить дальше — до конечного пункта назначения. А если ДНС-записей нет — непонятно, куда отправлять. Код ответа клиенту задается опцией unknown_address_reject_code, по умолчанию 450.

# postconf -d unknown_address_reject_code
unknown_address_reject_code = 450

Правило reject_unauth_destination требует — домен из RCPT TO должен быть конечным пунктом назначения (соответствует $mydestination, $virtual_alias_domains или $virtual_mailbox_domains) или наш сервер должен быть ретранслятором для этого домена (соответствует $relay_domains или его поддомену). Код ответа клиенту задается опцией relay_domains_reject_code, по умолчанию 554.

# postconf -d relay_domains_reject_code
relay_domains_reject_code = 554

Правило reject_unlisted_recipient требует — проверить, что такой получатель существует. По умолчанию проверяются пользователи в файле /etc/passwd и алиасы в индексированной карте /etc/aliases. Но если есть виртуальные почтовые пользователи — Postfix будет дополнительно проверять по картам virtual_alias_maps и virtual_mailbox_maps.

$ postconf -d local_recipient_maps
local_recipient_maps = proxy:unix:passwd.byname $alias_maps
# Карты локальных получателей сообщения (виртуальные)
virtual_alias_maps = hash:/etc/postfix/virtual_alias_map
virtual_mailbox_maps = hash:/etc/postfix/virtual_mailbox_map

# Карты локальных получателей сообщения (системные)
local_recipient_maps = proxy:unix:passwd.byname $alias_maps

Порядок проверки существования виртуального пользователя задается опцией virtual_maps. Если опция не задана — Postfix сначала проверит virtual_alias_maps, а затем virtual_mailbox_maps. Важно следить за актульностю этих двух карт и обновлять их при добавлении новых почтовых пользователей.

Код ответа сервера Postfix, когда запрос клиента отклонен правилами reject_non_fqdn_helo_hostname, reject_non_fqdn_sender или reject_non_fqdn_recipient — задается опцией non_fqdn_reject_code (по умолчанию 504).

Код ответа сервера, когда запрос клиента отклонен правилом reject_invalid_helo_hostname — задается опцией invalid_hostname_reject_code (по умолчанию 501).

7. Проверка адреса отправителя

Правило reject_unverified_sender требует убедиться в существовании пользователя с этим адресом. Для этого Postfix формирует и отправляет пробное сообщение отправителю. Если удаленный почтовый сервер принимает получателя на этапе RCPT TO — Postfix принимает сообщение от исходного клиента. В противном случае — отклоняет. Эта функциональность является весьма дорогостоящей, т.к. проверка занимает много времени и требует дополнительных системных ресурсов.

По умолчанию пробные сообщения имеют адрес отправителя double-bounce@$myorigin. Это безопасно, поскольку Postfix SMTP не отклоняет почту для этого адреса. Изменить адрес отправителя пробного сообщения можно с помощью директивы address_verify_sender.

Из сказанного выше следует, что для любого адреса, с которого выполняется рассылылка почты, должен существовать ящик. Иначе письма не пройдут проверку обратного адреса на стороне получателя и не будут доставлены по назначению. Это актуально для всяких рассылок и прочей вроде как односторонней коммуникации, не требующей ответа.

С другой стороны, если письма нужно получать не только от живых людей, но и от роботов, то придется убрать правило reject_unverified_sender — письма часто отправляют с несуществующих адресов. Это случается, когда админ сервера не озаботился тем, чтобы создать почтовый ящик для адреса, с которого выполняется рассылка.

По умолчанию Postfix хранит проверенные адреса отправителей в оперативной памяти — эти данные теряются при перезагрузке. Для постоянного хранения проверенных адресов можно использовать базу данных, которая задается в опции конфигурации address_verify_map.

address_verify_map = btree:$data_directory/verified_senders
# интервал запуска процесса очистки (удаление устаревших записей кэша)
address_verify_cache_cleanup_interval = 12h
# время, по истечении которого успешная проверка удаляется из кэша
address_verify_positive_expire_time = 31d
# время, по истечении которого необходимо обновить успешную проверку
address_verify_positive_refresh_time = 7d
# время, по истечении которого неудачная проверка удаляется из кэша
address_verify_negative_expire_time = 3d
# время, по истечении которого необходимо обновить неудачную проверку
address_verify_negative_refresh_time = 3h

Подробнее о проверке адреса отправителя см. здесь.

8. Недопустимые HELO/EHLO

Некоторые спамеры пытаются скрыть источник сообщения, используя имя хоста нашего почтового сервера как свое собственное. Для предотвращения этой ситуации нужно создать файл карты /etc/postfix/check_helo_access и добавить новое правило в smtpd_helo_restrictions.

/^mail\.example\.com$/        550 Don't use my hostname
/^\[222\.222\.222\.222\]$/    550 Don't use my ip address

Теперь добавим еще одно правило check_helo_access в ограничение smtpd_helo_restrictions.

# Ограничения на этапе команды HELO/EHLO
smtpd_helo_restrictions =
    ..........
    # Запрет на использование в HELO/EHLO имени хоста этого сервера
    check_helo_access pcre:/etc/postfix/check_helo_access

9. Карты разрешить/запретить

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

Правила check_*_access кроме OK (PERMIT), REJECT или DUNNO, могут возвращать коды 4xx («временные трудности, попробуете позже»), 5xx («тяжелая проблема у меня или у вас»). Еще такие правила могут возвращать действия для менеджера очередей — FILTER, HOLD, REDIRECT, DISCARD, DEFER_IF_REJECT, DEFER_IF_PERMIT.

Если на выходе check_*_access получается что-нибудь отличное от OK (PERMIT), REJECT или DUNNO — это трактуется как DUNNO. Цифровые коды любого вида кроме 4xx и 5xx — считаются OK (PERMIT), а 4xx и 5xx — считаются REJECT.

Действиe DEFER_IF_REJECT обычно используют, когда возникают проблемы в «белых списках». Если после действия DEFER_IF_REJECT какое-нибудь правило ниже вернет REJECT — клиенту (другому почтовому серверу) сообщается о временной проблеме (т.е. «попробуйте позже»). Действие DEFER_IF_PERMIT используется с точностью наоборот, т.е. когда возникают проблемы с «черными списками».

# Ограничения на этапе установке подключения
smtpd_client_restrictions =
    ..........
    # Разрешить или запретить согласно записям в карте доступа
    check_client_access hash:/etc/postfix/check_client_access
    ..........

# Ограничения на этапе команды HELO/EHLO
smtpd_helo_restrictions =
    ..........
    # Разрешить или запретить согласно записям в карте доступа
    check_helo_access pcre:/etc/postfix/check_helo_access
    ..........

# Ограничения на этапе команды MAIL FROM
smtpd_sender_restrictions =
    ..........
    # Разрешить или запретить согласно записям в карте доступа
    check_sender_access hash:/etc/postfix/check_sender_access
    ..........

# Ограничения на этапе команды RCPT TO
smtpd_recipient_restrictions =
    ..........
    # Разрешить или запретить согласно записям в карте доступа
    check_recipient_access hash:/etc/postfix/check_recipient_access
    ..........

Пример карты доступа /etc/postfix/check_client_access — разрешить доступ для ip-адреса, для домена и поддоменов.

# nano /etc/postfix/check_client_access
100.100.100.100    PERMIT
yandex.ru          PERMIT
# postmap hash:/etc/postfix/check_client_access

Пример карты доступа /etc/postfix/check_helo_access — не использовать в HELO/EHLO наш hostname и наш ip-адрес.

# nano /etc/postfix/check_helo_access
/^mail\.example\.com$/        550 Don't use my hostname
/^\[222\.222\.222\.222\]$/    550 Don't use my ip address

Пример карты доступа /etc/postfix/check_sender_access — запрет сообщений от shop.com, кроме support@shop.com.

# nano /etc/postfix/check_sender_access
support@shop.com    PERMIT
shop.com            REJECT
# postmap hash:/etc/postfix/check_sender_access

Пример карты доступа /etc/postfix/check_recipient_access — для кого наш сервер может принимать сообщения. Мы уже знаем, что есть правило reject_unauth_destination (принимать почту только для «своих» доменов) и правило reject_unlisted_recipient (принимать почту, если есть получатель). Такая карта доступа позволит сделать исключения из этих двух правил — и принимать сообщение до того, как оно будет отброшено.

# nano /etc/postfix/check_recipient_access
# Хотя это наш домен, но ящика нет, должны отклонять — однако принимаем
noreply@example.com    PERMIT
# Это не наш домен, должны отклонять — однако принимаем такие сообщения
gmail.com              PERMIT
# postmap hash:/etc/postfix/check_recipient_access

Карты доступа могут содержать не только действия и числовые коды, но и правила (которые возвращают действия). В примере ниже проверяется отправитель из MAIL FROM — разрешается отправка с трех адресов из внутренней сети куда угодно. В этом случае правило permit_mynetworks вернет PERMIT — и произойдет переход к следующему ограничению. Когда отправка сообщения с этих трех адресов идет из внешней сети — правило permit_mynetworks вернет DUNNO и произойдет переход к оценке правила reject_unauth_destination, которое оценивает получателя. Правило вернет REJECT, если домен получателя не указанан в опциях файла конфигурации $mydestination, $virtual_alias_domains, $virtual_mailbox_domains или $relay_domains.

# nano /etc/postfix/check_sender_access
ivanov@example.com     permit_mynetworks,reject_unauth_destination
petrov@example.com     permit_mynetworks,reject_unauth_destination
smirnov@example.com    permit_mynetworks,reject_unauth_destination
# postmap hash:/etc/postfix/check_sender_access

10. Тестирование правил

В Postfix имеются несколько возможностей, которые помогают тестировать правила доступа.

Опция soft_bounce по умолчанию имеет значение no. Установка значения yes приводит к тому, что ответы с кодами 5xx (тяжелая, постоянная ошибка) преобразуются в 4xx (временная ошибка). Соответственно, почтовые серверы будут пытаться доставить сообщения на наш почтовый сервер позже. За это время можно проверить логи и убедиться, правильно ли работают правила.

soft_bounce = yes

Модификатор WARN_IF_REJECT перед правилом предписывает серверу вместо действия REJECT добавить запись в журнал. Почта не будет отклоняться, вместо этого в лог-файл добавляются записи, которые можно изучить и внести корректировки.

smtpd_sender_restrictions =
    ..........
    WARN_IF_REJECT
    reject_unverified_sender
    ..........

Первый пример

Допустим, у нас в файле конфигурации есть такой фрагмент

# Ограничения на этапе установке подключения
smtpd_client_restrictions =
    # Отклонять клиентов, у которых доменное имя из PTR-записи
    # не разрешается в тот же ip-адрес по A-записи
    reject_unknown_client_hostname

Давайте с помощью telnet отправим письмо на наш сервер

$ telnet mail.example.com 25
Trying 222.222.222.222...
Connected to mail.example.com.
Escape character is '^]'.
220 mail.example.com ESMTP Postfix
HELO smtp.mail.ru
250 mail.example.com
MAIL FROM: <somebody@mail.ru>
250 2.1.0 Ok
RCPT TO: <evgeniy@example.com>
450 4.7.25 Client host rejected: cannot find your hostname, [123.123.123.123]
QUIT
221 2.0.0 Bye

После чего посмотрим логи почтового сервера /var/log/mail.log

# cat /var/log/mail.log
...postfix/smtpd[63668]: connect from unknown [123.123.123.123]
...postfix/smtpd[63668]: NOQUEUE: reject: RCPT from unknown [123.123.123.123]: 450 4.7.25
   Client host rejected: cannot find your hostname, [123.123.123.123]; from=<somebody@mail.ru>
   to=<evgeniy@example.com> proto=SMTP helo=<smtp.mail.ru>
...postfix/smtpd[63668]: disconnect from unknown [123.123.123.123] helo=1 mail=1 rcpt=0/1...

Если перед правилом reject_unknown_client_hostname добавить WARN_IF_REJECT — письмо не отклоняется, добавляется в очередь, доставляется в почтовый ящик. При этом в лог-файл добавляется строка reject_warning — что сообщение принято, хотя правило вернуло REJECT.

$ telnet mail.example.com 25
Trying 222.222.222.222...
Connected to mail.example.com.
Escape character is '^]'.
220 mail.example.com ESMTP Postfix
HELO smtp.mail.ru
250 mail.example.com
MAIL FROM: <somebody@mail.ru>
250 2.1.0 Ok
RCPT TO: <evgeniy@example.com>
250 2.1.5 Ok
DATA
354 End data with <CR><LF>.<CR><LF>
Test post message
.
250 2.0.0 Ok: queued as E29C72658
QUIT
# cat /var/log/mail.log
...postfix/smtpd[63724]: connect from unknown [123.123.123.123]
...postfix/smtpd[63724]: NOQUEUE: reject_warning: RCPT from unknown [123.123.123.123]: 450 4.7.25
   Client host rejected: cannot find your hostname, [123.123.123.123]; from=<somebody@mail.ru>
   to=<evgeniy@example.com> proto=SMTP helo=<smtp.mail.ru>
...postfix/smtpd[63724]: E29C72658: client=unknown [123.123.123.123]
...postfix/cleanup[63729]: E29C72658: message-id=<>
...postfix/qmgr[63703]: E29C72658: from=<somebody@mail.ru>, size=208, nrcpt=1 (queue active)
...postfix/local[63730]: E29C72658: to=<evgeniy@example.com>, relay=local, delay=0.08, delays=0.08/0/0/0,
   dsn=2.0.0, status=sent (delivered to mailbox)
...postfix/qmgr[63703]: E29C72658: removed
...postfix/smtpd[63724]: disconnect from unknown [123.123.123.123] helo=1 mail=1 rcpt=1 data=1 quit=1...

Второй пример

Допустим, у нас в файле конфигурации есть такой фрагмент

# Ограничения на этапе команды RCPT TO
smtpd_recipient_restrictions =
    # Отклонять почту на адреса, для которых нет почтовых ящиков
    reject_unlisted_recipient

Попробуем с помощью telnet отправить письмо

$ telnet mail.example.com 25
Trying 222.222.222.222...
Connected to mail.example.com.
Escape character is '^]'.
220 mail.example.com ESMTP Postfix
HELO smtp.mail.ru
250 mail.example.com
MAIL FROM: <somebody@mail.ru>
250 2.1.0 Ok
RCPT TO: <andrey@example.com>
550 5.1.1 <andrey@example.com>: Recipient address rejected: User unknown in local recipient table
QUIT

Письмо отклоняется с кодом 550, потому что не найден получатель — об этом есть запись в лог-файле

# cat /var/log/mail.log
...postfix/smtpd[65967]: connect from unknown[123.123.123.123]
...postfix/smtpd[65967]: NOQUEUE: reject: RCPT from unknown [123.123.123.123]: 550 5.1.1
   <andrey@andrey>: Recipient address rejected: User unknown in local recipient
   table; from=<somebody@mail.ru> to=<andrey@example.com> proto=SMTP helo=<smtp.mail.ru>
...postfix/smtpd[65967]: disconnect from unknown [123.123.123.123] helo=1 mail=1 rcpt=0/1...

Если мы добавим в конфигурацию DEFER_IF_REJECT — то ответ будет с кодом 450 («попробуйте позже»)

# Ограничения на этапе команды RCPT TO
smtpd_recipient_restrictions =
    DEFER_IF_REJECT
    # Отклонять почту на адреса, для которых нет почтовых ящиков
    reject_unlisted_recipient
$ telnet mail.example.com 25
Trying 222.222.222.222...
Connected to mail.example.com.
Escape character is '^]'.
220 mail.example.com ESMTP Postfix
HELO smtp.mail.ru
250 mail.example.com
MAIL FROM: <somebody@mail.ru>
250 2.1.0 Ok
RCPT TO: <andrey@example.com>
450 4.7.0 <andrey@example.com>: Recipient address rejected: defer_if_reject requested
QUIT
# cat /var/log/mail.log
...postfix/smtpd[65993]: connect from unknown [123.123.123.123]
...postfix/smtpd[65993]: NOQUEUE: reject: RCPT from unknown [123.123.123.123]: 450 4.7.0
   <andrey@example.com>: Recipient address rejected: defer_if_reject requested;
   from=<somebody@mail.ru> to=<andrey@example.com> proto=SMTP helo=<smtp.mail.ru>
...postfix/smtpd[65993]: disconnect from unknown [123.123.123.123] helo=1 mail=1 rcpt=0/1...

Файлы конфигурации Postfix

1. Файл конфигурации main.cf

Главный конфигурационный файл — это /etc/postfix/main.cf. Содержит множество директив, рассмотрим некоторые из них.

Директива compatibility_level

Определяет, какие настройки по умолчанию будут использованы для файлов конфигурации main.cf и master.cf. У разных версий Postfix разные настройки по умолчанию — они изменяются от версии к версии. Например, мы установили версию Postfix 3.6, но у нас есть рабочий файл конфигурации для Postfix 3.2. Достаточно изменить значение этой опции на 3.2 — и можно использовать старый файл конфигурации для новой установки.

compatibility_level = 3.6
# postconf -d | grep mail_version  # установленная версия
mail_version = 3.6.4

Директива queue_directory

Задает местоположение очередей Postfix. Это также корневой каталог демонов Postfix, которые запускаются в среде с измененным корневым каталогом (chroot).

queue_directory = /var/spool/postfix

Директива command_directory

Директива задает местоположение всех утилит Postfix — postdrop, postmap, postqueue, postsuper и другие.

command_directory = /usr/sbin

Директива daemon_directory

Указывает местоположение всех демонов Postfix, перечисленных в файле конфигурации master.cf. Эта директория должна принадлежать root.

daemon_directory = /usr/lib/postfix/sbin

Директива data_directory

Указывает местоположение, куда Postfix будет записывать данные в процессе работы, например кэши. Эта директория должна принадлежать $mail_owner, см. ниже.

data_directory = /var/lib/postfix

Директива mail_owner

Задает владельца очередей и большинства процессов демона Postfix. Должен быть аккаунт в системе, который больше нигде не используется и которому не принадлежат другие файлы или процессы. Нельзя использовать nobody или daemon.

mail_owner = postfix

Директива myhostname

Используется для указания имени хоста почтового сервера. Типичные примеры имён хостов почтовых серверов — smtp.example.com и mail.example.com.

myhostname = mail.example.com

Директива mydomain

Позволяет указать почтовый домен, обслуживанием которого занимается сервер, например — example.com.

mydomain = example.com

Директива myorigin

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

myorigin = $mydomain

Когда пользователи отправляют почту без указания имени домена в адресе конверта (из SMTP-диалога) или заголовке самого сообщения, опция myorigin определяет, какое имя домена должно быть добавлено. По умолчанию используется значение опции myhostname. Если почтовый сервер работает в системе с именем хоста mail.example.com, сообщения от пользователя username будут иметь адрес username@mail.example.com.

Директива inet_interfaces

Задает сетевой интерфейс, который будет прослушиваться Postfix. По умолчанию прослушиваются все сетевые интерфейсы на сервере.

#inet_interfaces = all
#inet_interfaces = $myhostname
inet_interfaces = $myhostname, localhost

Директива mydestination

Содержит список доменов, которые сервер будет считать конечными пунктами назначения для входящей почты. Другими словами — сервер не будет такие письма пересылать дальше, а будет оставлять у себя.

mydestination = $mydomain, $myhostname, localhost

Директива mynetworks

Позволяет указывать то, с каких ip-адресов можно отправлять почту через этот сервер. Обычно разрешают передачу почты только от локальных клиентских компьютеров.

mynetworks = 127.0.0.0/8, 192.168.100.0/24

Директива relay_domains

Указывает на необходимость приема почты для этих доменов несмотря на то, что этот сервер не является местом их конечного назначения. Самое безопасное значение — пустое. При этом, Postfix не будет пересылать почту для домена, если основная или резервная MX-запись для этого домена указывает на этот сервер.

# самое безопасное — никогда не пересылать почту
relay_domains =
# пересылать почту на мой домен и поддомены
relay_domains = $mydomain

Директива relayhost

Предписывет все сообщения, для которых этот сервер не является конечным местом назначения, отправлять на указанный почтовый сервер — вместо того, чтобы пытаться доставить их напрямую по месту назначения. Значение этой опции может быть перезаписано значением опции $sender_dependent_relayhost_maps. По умолчанию не имеет значения.

Директива alias_maps

Задает список карт (баз данных, таблиц поиска) псевдонимов, используемых локальным агентом доставки почты (Local Delivery Agent — LDA). После изменении базы данных псевдонимов — нужно запустить команду postalias hash:/etc/aliases или просто newaliases — для создания /etc/aliases.db.

alias_maps = hash:/etc/aliases

В системах с NIS по умолчанию сначала выполняется поиск по локальной базе данных псевдонимов, затем по базе данных псевдонимов NIS.

NIS, что является сокращением от Network Information Services (Сетевые Информационные Службы), которые были разработаны компанией Sun Microsystems для централизованного администрирования систем Unix (первоначально SunOS). В настоящее время эти службы практически стали промышленным стандартом; все основные версии Unix (Linux, NetBSD, OpenBSD, FreeBSD) поддерживают NIS.

Это система клиент/сервер на основе вызовов RPC, которая позволяет группе машин в одном домене NIS совместно использовать общий набор конфигурационных файлов. Системный администратор может настроить клиентскую систему NIS только с минимальной настроечной информацией, а затем добавлять, удалять и модифицировать настроечную информацию из одного места.

Директива alias_database

Задает список карт (баз данных, таблиц поиска) псевдонимов, которые создаются командами newaliases или sendmail -bi. Это отдельная директива конфигурации, поскольку alias_maps может указывать на карты, которые не обязательно все находятся под контролем Postfix.

alias_database = hash:/etc/aliases

Директива mail_spool_directory

Указывает, что вся почта будет попадать в центральную директорию очередей /var/spool/mail, где имеется файл для каждого пользователя. Если в конце значения опции добавить слэш — то сообщения будут сохраняться в виде отдельных файлов.

# сообщения в формате mbox (unix-style mailbox)
mail_spool_directory = /var/spool/mail
# сообщения в формате maildir (qmail-style maildir)
mail_spool_directory = /var/spool/mail/

Директива home_mailbox

Задает имя файла для хранения сообщений в домашних директориях пользователей. Можно задать значение опции в виде maildir/ — в этом случае сообщения будут сохраняться в виде отдельных файлов в директории /home/user/maildir. При этом Postfix создает внутри /home/user/maildir еще три директории — new, cur и tmp. Директория new предназначена для новых (непрочитанных) сообщений. После прочтения сообщения перемещаются в директорию cur.

#home_mailbox = mailbox
#home_mailbox = maildir/

Директива mailbox_command

Предписывает использовать для локальной доставки почты указанного агента доставки почты (MDA — Mail Delivery Agent).

#mailbox_command = /usr/bin/procmail

Директива smtpd_banner

Позволяет задать ответ, который возвращает сервер при подключении клиентов. Лучше всего поменять это значение так, чтобы оно не указывало на то, какой почтовый сервер используется. В начале обязательно должно быть $myhostname — это требование RFC.

smtpd_banner = $myhostname ESMTP Server

Директива sendmail_path

Задает путь к утилите sendmail, которая входит в поставку Postfix.

sendmail_path = /usr/sbin/sendmail
MTA Sendmail в настоящее время считается устаревшим и практически не используется. Но в прошлом использовался повсеместно — и поэтому Postfix реализован таким образом, чтобы быть максимально совместимым с Sendmail. Утилита sendmail входит в поставку Postfix и подменяет собой MTA Sendmail. Ее задача заключается в предоставлении совместимого с Sendmail интерфейса для приложений, способных только вызывать /usr/sbin/sendmail.

Директива newaliases_path

Задает путь к Postfix команде newaliases, предназначенной для создания карты алиасов. Команда newaliases является символической ссылкой на Postfix утилиту sendmail.

newaliases_path = /usr/bin/newaliases

Директива mailq_path

Задает путь к Postfix команде mailq, которая предназначена для просмотра очередей. Команда mailq является символической ссылкой на Postfix утилиту sendmail.

mailq_path = /usr/bin/mailq

Директива setgid_group

Задает имя группы, которая используется для выполнения команд отправки почты и управления очередями. Это должна быть группа, которая не используется другими учетными записями, даже учетной записью Postfix.

setgid_group = postdrop

Директива inet_protocols

Задает версию ip-протокола, которую будет использовать сервер при установлении соединений. По умолчанию имеет значение all.

inet_protocols = ipv4

Директива virtual_alias_domains

Позволяет указать дополнительные домены, для которых почтовый сервер будет местом назначения. Это позволяет создавать виртуальные адреса почты на этих доменах и перенаправить сообщения для этих адресов — существующим пользователям системы с помощью карты поиска virtual_alias_maps.

virtual_alias_domains = example.net, example.org

Директива virtual_alias_maps

Если виртуальные домены заданы в директиве virtual_alias_domains — тогда эта директива задает карту поиска, которая сопоставляет виртуальные адреса и существующих пользователей системы. Если виртуальные домены заданы в директиве virtual_mailbox_domains — тогда эта директива задает карту поиска, которая перенаправляет сообщения с одного адреса почты на другой. При этом, слева могут быть как существующие локальные ящики, так и не существющие. А справа могут быть как локальные ящики, так и внешние — расположенные на другом почтовом сервере.

virtual_alias_maps = hash:/etc/postfix/virtual_alias_map

Директива virtual_mailbox_domains

Позволяет указать дополнительные домены, для которых почтовый сервер будет местом назначения. Это позволяет создавать адреса почты на этих доменах для пользователей, у которых нет учетной записи в операционной системе почтового сервера. Классический вариант настройки почтового сервера подразумевает канонический домен, например example.com и почтовые ящики для системных пользователей, например evgeniy@example.com. Вариант настройки с использованием virtual_mailbox_domains — более современный, почтовые ящики не связаны с пользователями операционной ситемы.

virtual_mailbox_domains = example.net, example.org

Директива virtual_mailbox_maps

Позволяет задать карту поиска, которая связывает адрес почты на виртуальном домене и директорию, где расположен почтовый ящик (относительно директории virtual_mailbox_base).

virtual_mailbox_maps = hash:/etc/postfix/virtual_mailbox_map

2. Файл конфигурации master.cf

Этот файл конфигурации определяет, какие службы Postfix должны запускаться и как они должны работать. В отличие от main.cf (который содержит параметры конфигурации), master.cf управляет процессами и службами.

Каждая строка состоит из восьми полей, разделенных пробелами или табуляцией. Если строка начинается с пробела или пробелов — она является продолжением предыдущей строки.

service type private unpriv chroot wakeup maxproc command + args
  1. Название демона, который запускается и контролируется демоном master
  2. Способ взаимодействия с демоном, inet — через сокет tcp/ip, unix — через unix-сокет
  3. Это внутренний демон (по умолчанию y) или возможен доступ через инструменты командной строки
  4. Демон работает от имени пользователя $mail_owner (по умолчанию y) или с привилегиями root
  5. Демон работает chroot-окружении в директории почтовой очереди /var/spool/postfix (по умолчанию n)
  6. Автоматически пробуждать демона после указанного количества секунд (по умолчанию ноль, то есть не пробуждать)
  7. Максимальное количество процессов, которое может запустить демон, по умолчанию $default_process_limit
  8. Команда и аргументы для запуска демона, путь к команде относительно $daemon_directory

Пример фильтрации сообщений с использованием демона amavisd-new (прослушивает tcp-сокет 127.0.0.1:10024) — позволяет проверить на спам и вирусы с использованием SpamAssassin и ClamAV + добавить цифровую подпись DKIM для исходящих и проверить подпись DKIM для входящих.

# Настройка фильтрации сообщений через amavisd
smtp-amavis    unix    -    -    n    -    2    smtp
    -o smtp_data_done_timeout=1200
    -o smtp_send_xforward_command=yes
    -o disable_dns_lookups=yes
    -o max_use=20
# Возврат сообщений после фильтрации amavisd
127.0.0.1:10025    inet    n    -    n    -    -    smtpd
  -o content_filter=
  -o local_recipient_maps=
  -o relay_recipient_maps=
  -o smtpd_restriction_classes=
  -o smtpd_delay_reject=no
  -o smtpd_client_restrictions=permit_mynetworks,reject
  -o smtpd_helo_restrictions=
  -o smtpd_sender_restrictions=
  -o smtpd_recipient_restrictions=permit_mynetworks,reject
  -o smtpd_data_restrictions=reject_unauth_pipelining
  -o smtpd_end_of_data_restrictions=
  -o mynetworks=127.0.0.1
  -o smtpd_authorized_xforward_hosts=127.0.0.1
# Отправляем сообщения для фильтрации демону amavisd-new
content_filter = smtp-amavis:[127.0.0.1]:10024
# Отключаем преобразования адреса получателя, чтобы фильтр
# мог проверить оригинальное сообщение, до любых изменений
receive_override_options = no_address_mappings

Последнее поле command может принимать значениe (это встроенные демоны Postfix)

  • smtpd — SMTP-сервер
  • smtp — SMTP-клиент
  • qmgr — менеджер очередей
  • pickup — обработка локальной почты
  • cleanup — обработка сообщений
  • spawn — запуск внешних программ
  • pipe — передача через pipe
  • local — локальная доставка
  • virtual — виртуальная доставка
  • error — обработка ошибок
  • discard — удаление сообщений

Примеры использования spawn

# SPF проверка
policy-spf  unix  -       n       n       -       -       spawn
  user=policyd-spf argv=/usr/bin/policyd-spf
# Внешний фильтр
filter      unix  -       n       n       -       -       spawn
  user=filter argv=/usr/local/bin/content-filter.pl
# Скрипт проверки
check-script unix -       n       n       -       -       spawn
  user=nobody argv=/usr/local/bin/check.sh $sender $recipient

Примеры использования pipe

# Доставка в Telegram
telegram  unix  -       n       n       -       -       pipe
  flags=R user=telegram argv=/usr/local/bin/telegram.sh $sender $recipient
# Сохранение в базу данных
save-to-db unix -       n       n       -       -       pipe
  flags=DRhu user=maildb argv=/usr/local/bin/save-mail.py $sender $recipient
# Автоответчик
autoreply unix  -       n       n       -       -       pipe
  flags=Fq user=nobody argv=/usr/local/bin/autoreply.pl $sender $recipient

Назначение и особенности spawn

  • Для политик и проверок (policy service)
  • Запускает процесс и держит его в памяти
  • Обменивается атрибутами (не содержимым письма)
  • Процесс может обработать множество запросов
  • Используется в restriction lists

Назначение и особенности pipe

  • Для доставки почты (delivery agent)
  • Запускает новый процесс для каждого письма
  • Передает содержимое письма через stdin
  • Процесс завершается после обработки письма
  • Используется в transport maps

Особенности MUA Thunderbird

При работе с Thunderbird следует знать о довольно запутанной терминологии. В IMAP протоколе удаление сообщения происходит в два этапа — сначала пометка флагом Deleted, потом выполнение команды EXPUNGE. При удалении сообщения в папке Входящие — оно помечается флагом Deleted и перемещается в папку Корзина. При удалении из Корзины — сообщение скрывается для показа, но остается на сервере.

Как восстановить показ удаленных из Корзины сообщений в Thunderbird — мне не удалось разобраться, можно только на сервере выполнить команду, чтобы сбросить флаг Deleted.

# doveadm flags remove -u ivanov@example.net '\Deleted' mailbox Trash ALL

В контекстом меню каждой папки есть пункт Сжать (Compact) — выполнить команду EXPUNGE для сообщений, помеченных флагом Deleted, то есть физически удалить на сервере. Кроме того, в контекстном меню папки Корзина есть пункт Опустошить (Empty Trash) — выполнить EXPUNGE для всех сообщений в Корзине. Все сообщения в Корзине имеют флаг Deleted, но некоторые из них удалены дважды и поэтому скрыты.

Утилита тестирования swaks

Swaks (Swiss Army Knife for SMTP) — мощная утилита командной строки для тестирования SMTP-конфигураций. Позволяет отправлять тестовые письма, имитируя работу почтового клиента (MUA), и детально отображает весь процесс SMTP-сессии.

# apt install swaks

Часто используемые опции

  • --to адрес — адрес почты получателя
  • --from адрес — адрес почты отправителя
  • --server хост[:порт] — адрес и порт SMTP-сервера (порт по умолчанию 25)
  • --port порт — явно указать порт (альтернатива указанию в --server хост[:порт])
  • --auth AUTH — использовать указанную SMTP-аутентификацию
  • --auth-user имя — имя пользователя для аутентификации
  • --auth-password пароль — пароль для аутентификации
  • --tls — использовать STARTTLS (обычно порт 587)
  • --tlsc — использовать SMTPS (обычно порт 465)
  • --header текст — установить заголовок сообщения
  • --body текст — установить тело сообщения
  • --data файл.eml — отправить содержимое файла
  • --helo домен — задать домен для команды HELO
  • --ehlo домен — задать домен для команды EHLO
  • -q команда — прервать сессию после указанной SMTP-команды
  • --timeout секунды — установить таймаут соединения

Отправка письма через SMTP-сервер mail.example.com с аутентификацией и STARTTLS (порт 587)

$ swaks --to evgeniy@mail.ru \
>    --from evgeniy@example.com \
>    --server mail.example.com:587 \
>    --ehlo [101.101.101.101] \
>    --auth LOGIN \
>    --auth-user 'evgeniy@example.com' \
>    --auth-password 'qwerty' \
>    --tls \
>    --header 'Subject: Test message' \
>    --body 'Test message'
=== Trying mail.example.com:587...
=== Connected to mail.example.com.
<-  220 mail.example.com ESMTP Postfix
 -> EHLO [101.101.101.101]
<-  250-mail.example.com
<-  250-PIPELINING
<-  250-SIZE 15728640
<-  250-ETRN
<-  250-STARTTLS
<-  250-ENHANCEDSTATUSCODES
<-  250-8BITMIME
<-  250-DSN
<-  250-SMTPUTF8
<-  250 CHUNKING
 -> STARTTLS
<-  220 2.0.0 Ready to start TLS
=== TLS started with cipher TLSv1.3:TLS_AES_256_GCM_SHA384:256
=== TLS no local certificate set
=== TLS peer DN="/CN=mail.example.com"
 ~> EHLO [101.101.101.101]
<~  250-mail.example.com
<~  250-PIPELINING
<~  250-SIZE 15728640
<~  250-ETRN
<~  250-AUTH PLAIN LOGIN CRAM-MD5 DIGEST-MD5
<~  250-ENHANCEDSTATUSCODES
<~  250-8BITMIME
<~  250-DSN
<~  250-SMTPUTF8
<~  250 CHUNKING
 ~> AUTH LOGIN
<~  334 VXNlcm5hbWU6
 ~> ZXZnZW5peUB0b2ttYWtvdi5zdQ==
<~  334 UGFzc3dvcmQ6
 ~> cXdlcnR5
<~  235 2.7.0 Authentication successful
 ~> MAIL FROM:<evgeniy@example.com>
<~  250 2.1.0 Ok
 ~> RCPT TO:<evgeniy@mail.ru>
<~  250 2.1.5 Ok
 ~> DATA
<~  354 End data with <CR><LF>.<CR><LF>
 ~> Date: Wed, 18 Jun 2025 15:48:06 +0300
 ~> To: evgeniy@mail.ru
 ~> From: evgeniy@example.com
 ~> Subject: Test message
 ~> Message-Id: <20250618154806.115659@3455547-mail.server.com>
 ~> X-Mailer: swaks v20201014.0 jetmore.org/john/code/swaks/
 ~> 
 ~> Test message
 ~>
 ~>
 ~> .
<~  250 2.0.0 Ok: queued as E46B2211B5
 ~> QUIT
<~  221 2.0.0 Bye
=== Connection closed with remote host.

Отправка письма через SMTP-сервер mail.example.com с аутентификацией по SMTPS (порт 465)

$ swaks --to evgeniy@mail.ru \
>    --from evgeniy@example.com \
>    --server mail.example.com:465 \
>    --ehlo [101.101.101.101] \
>    --auth CRAM-MD5 \
>    --auth-user 'evgeniy@example.com' \
>    --auth-password 'qwerty' \
>    --tlsc \
>    --header 'Subject: Test message' \
>    --body 'Test message'

Проверка, принимает ли сервер письма для определенного адреса (без реальной отправки данных). Если сервер ответит 250 OK после команды RCPT TO — значит, он готов принять письмо для этого получателя.

$ swaks --to somebody@example.com --server mail.example.com -q RCPT

Поиск: Linux • Клиент • Конфигурация • Настройка • Сервер • Postfix • Dovecot • SMTP • POP3 • IMAP • Почта

Каталог оборудования
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Производители
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Функциональные группы
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.