воскресенье, 25 мая 2014 г.

Exim и Dovecot без SQL

Среди системных администраторов и инженеров распространено мнение, что чем система проще, тем она надёжнее. Конечно, не стоит воспринимать это утверждение буквально. Но, как говорится, умному - достаточно. Чем меньше компонентов в системе, чем проще взаимосвязи между компонентами, тем меньше вероятность того, что она сломается и тем выше вероятность того, что её будет просто починить.

Именно поэтому большинство системных администраторов Unix любят использовать текстовые файлы, которые легко редактируются при помощи любого текстового редактора (файлы XML к таковым обычно не относятся). Здесь я опишу настройку почтовой системы на основе Dovecot и Exim без использования базы данных. Вместо этого все данные будут помещаться в текстовых файлах.

Описание настройки основано на моих предыдущих заметках Установка и настройка Dovecot и Настройка Exim, практически повторяя их. Прошу прощения за самоплагиат - я считаю, что так будет удобнее.

1. Dovecot

Начнём с настройки Dovecot, поскольку для настройки Exim необходимо иметь уже настроенный Dovecot.

1.1. Установка Dovecot

Установим пакеты Dovecot, содержащие поддержку серверов POP3 и IMAP:
# apt-get install dovecot-core dovecot-imapd dovecot-pop3d
1.2. Подготовка системы

Создадим группу и пользователя vmail, от имени которого будет работать Dovecot и дадим этому пользователю доступ к каталогу, в котором будет храниться почта пользователей почтовой системы. На серверах для размещения почтовых ящиков, как и другой часто меняющейся информации, обычно используется раздел /var, который заранее делается достаточно большим. На настраиваемой мной системе больше всего свободного места на разделе home, поэтому я размещу почтовые ящики пользователей на нём:
# groupadd -g 120 -r vmail
# useradd -g 120 -r -u 120 vmail
# mkdir /home/vmail
# chown vmail:vmail /home/vmail
# chmod u=rwx,g=rx,o= /home/vmail
1.3. Базовая настройка Dovecot

Я буду использовать защищённые версии протоколов IMAP и POP3, поэтому настрою в файле /etc/dovecot/conf.d/10-auth.conf механизмы PLAIN и LOGIN, чтобы хранить пароли в базе данных в хэшированном виде:
disable_plaintext_auth = no
auth_default_realm = domain.tld
auth_mechanisms = plain login
!include auth-passwdfile.conf.ext
Настроим использование учётных данных из файла, подобного /etc/passwd, прописав в файле /etc/dovecot/conf.d/auth-passwdfile.conf.ext следующие секции:
passdb {
  driver = passwd-file
  args = scheme=CRYPT username_format=%u /etc/dovecot/passwd
}

userdb {
  driver = passwd-file
  args = username_format=%u /etc/dovecot/passwd

  # Поля по умолчанию, которые могут быть заменены значениями из файла passwd
  default_fields = uid=vmail gid=vmail userdb_home=/home/vmail/%Ld/%Ln userdb_location=maildir:/home/vmail/%Ld/%Ln userdb_quota_rule=*:storage=1G 

  # Поля, значения которых заменяют значения из файла passwd
  #override_fields = home=/home/vmail/%Ld/%Ln
}
Создадим в каталоге /etc/dovecot файл passwd и проставим права доступа:
# cd /etc/dovecot
# touch passwd
# chown root:dovecot passwd
# chmod u=rw,g=r,o= passwd
В файле /etc/dovecot/passwd могут быть следующие поля:
user:{plain}password:uid:gid:gecos:home:shell:extra_fields
Назначение полей:
  • user - почтовый ящик (в данном случае - вместе с доменом),
  • password - пароль (можно явным образом указывать алгоритм хэширования пароля),
  • uid - системный идентификатор владельца файлов почты,
  • gid - системный идентификатор группы владельца файлов почты,
  • gecos - справочная информация о почтовом ящике (игнорируется),
  • home - путь к каталогу почты,
  • shell - интерпретатор (игнорируется),
  • extra_fields - дополнительные настройки (квота, например).
Любое из полей может быть не определено в файле, если в настройках Dovecot указаны значения этих полей по умолчанию. При указании дополнительных полей, используемых в секции userdb необходимо перед именем поля указывать префикс «userdb_», как в примере выше, в настройках default_fields. Имеется возможность зафиксировать часть настроек почтового ящика при помощи настройки override_fields, так что значения из файла будут игнорироваться.

Подробнее о формате файла и других настройках можно прочитать на официальной wiki-странице Dovecot: Passwd-file

Изменим форматирование отметок времени, вписав в файл /etc/dovecot/conf.d/10-logging.conf следующую настройку:
log_timestamp = "%Y-%m-%d %H:%M:%S "
На время отладки также можно включить другие опции из этого файла:
auth_verbose = yes
auth_verbose_passwords = yes
auth_debug = yes
mail_debug = yes
В файле /etc/dovecot/conf.d/10-mail.conf настроим путь к почтовым ящикам и пользователя, от имени которого dovecot будет работать с ящиками:
mail_home = /home/vmail/%Ld/%Ln
mail_location = maildir:/home/vmail/%Ld/%Ln
mail_uid = vmail
mail_gid = vmail
first_valid_uid = 120
last_valid_uid = 120
first_valid_gid = 120
last_valid_gid = 120
Сейчас настроим сервис, при помощи которого Exim будет проверять учётные данные почтовых клиентов. Для этого отредактируем файл /etc/dovecot/conf.d/10-master.conf и впишем в него настройки сервиса:
service auth {
  unix_listener auth-client {
    mode = 0660
    user = Debian-exim
    #group = 
  }
}
Зададим в файле /etc/dovecot/conf.d/15-lda.conf адрес, с которого Dovecot будет отправлять сообщения об ошибках:
postmaster_address = postmaster@domain.tld
Осталось отредактировать файл /etc/dovecot/dovecot.conf, указав в нём адрес, на котором сервер будет ожидать подключений:
!include_try /usr/share/dovecot/protocols.d/*.protocol
listen = *
!include conf.d/*.conf
!include_try local.conf
Начальная настройка сервера окончена. Осталось перезапустить Dovecot, чтобы настройки вступили в силу:
# /etc/init.d/dovecot restart
1.4. Настройка плагина acl

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

В файле /etc/dovecot/conf.d/10-mail.conf включаем использование плагина:
mail_plugins = acl
В файле /etc/dovecot/conf.d/20-imap.conf включаем использование плагина в IMAP-сервере:
protocol imap {
  mail_plugins = $mail_plugins imap_acl
}
В файле /etc/dovecot/conf.d/10-mail.conf прописываем следующие настройки:
namespace inbox {
  type = private
  separator = /
  prefix =
  inbox = yes
}

namespace {
  type = shared
  separator = /
  prefix = shared/%%u/
  location = maildir:%%h:INDEX=%h/shared/%%u
  subscriptions = yes
  list = children
}
Эти настройки описывают два пространства имён: в первом хранится личная почта пользователя, а во втором будут отображаться каталоги других пользователей, к которым этот пользователь имеет доступ.

Поясню смысл настроек location для пространства имён общих каталогов:
  • maildir:%%h - означает место расположения чужого почтового ящика в формате Maildir,
  • %%h - полный путь к Maildir-каталогу чужого ящика,
  • INDEX=%h/shared/%%u - задаёт каталог, в который как бы монтируются каталоги чужой почты, к которым её владелец дал нам доступ,
  • %h - путь к Maildir-каталогу нашего ящика,
  • %%u - имя другого пользователя в виде box@domain.tld.
В файл /etc/dovecot/conf.d/90-acl.conf прописываем настройки плагина:
plugin {
  acl = vfile
  acl_shared_dict = file:/home/vmail/%Ld/shared-mailboxes.db
}
Значение vfile предписывает создавать внутри почтового ящика файл dovecot-acl, в котором и будут прописываться права доступа к нему со стороны других пользователей.

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

Заодно опишем в файле /etc/dovecot/conf.d/15-mailboxes.conf назначение различных каталогов внутри пространства имён, в котором хранится личная почта пользователя:
namespace inbox {
  mailbox Drafts {
    special_use = \Drafts
  }
  mailbox Junk {
    special_use = \Junk
  }
  mailbox Trash {
    special_use = \Trash
  }
  mailbox Sent {
    special_use = \Sent
  }
}
Назначение каталогов:
  • Drafts - каталог черновиков,
  • Junk - каталог для спама,
  • Trash - каталог для удалённых писем,
  • Sent - каталог для отправленных писем.
Современные почтовые программы смогут прямо по протоколу IMAP узнать назначение каждого из специальных каталогов, вне зависимости от их названия. Это бывает полезно, если каталог имеет нестандартное название или название на языке пользователя ящика, например "Входящие" или "Спам".

Чтобы настройки плагина acl вступили в силу, нужно перезапустить Dovecot:
# /etc/init.d/dovecot restart
1.5. Настройка плагина quota

Плагин quota позволяет назначить для почтового ящика ограничения на объём хранящихся в нём писем или даже на их общее количество. На мой взгляд, ограничение на общее количество писем имеет довольно мало смысла. Единственная польза, которая мне приходит на ум - это возможность защититься от исчерпания inode'ов в файловой системе, если кто-то намеренно решит отправить огромное количество мелких писем в ящики пользователей, с целью нарушить работу почтовой системы.

Мы настроим плагин так, чтобы он использовал значения квот, указанные в интерфейсе Postfixadmin. Эти квоты ограничивают только максимальный объём писем в ящике.

Включим использование плагина в файле /etc/dovecot/conf.d/10-mail.conf:
mail_plugins = acl quota
Жирным шрифтом показан добавленный текст, а курсивом - текст, добавленный нами при включении плагина acl. Если вы не включали плагин acl, то вписывать этот текст не нужно.

В файл /etc/dovecot/conf.d/15-lda.conf впишем, что в случае превышения квоты Dovecot должен сообщать о временной ошибке, но не отклонять письмо окончательно. Почтовый сервер отправителя (или наш MTA) будет периодически предпринимать повторные попытки в надежде на то, что адресат почистит свой ящик от ненужных писем.
quota_full_tempfail = yes
В файл /etc/dovecot/conf.d/20-imap.conf добавим поддержку квот в IMAP-сервере:
protocol imap {
  mail_plugins = $mail_plugins imap_acl imap_quota
}
Этот плагин позволит почтовым клиентам, работающим по протоколу IMAP, узнавать квоту почтового ящика и её текущее использование.

Укажем в файле /etc/dovecot/conf.d/90-quota.conf, что значения квот берутся из словаря и зададим пустое правило по умолчанию:
plugin {
  quota = dict:user::file:%h/dovecot-quota
  quota_rule = *:
}
Осталось перезапустить Dovecot, чтобы настроенный плагин начал работать:
# /etc/init.d/dovecot restart
В каталоге каждого почтового ящика будет создаваться файл dovecot-data, внутри которого будет вестись учёт текущего количества сообщений в ящике и их объёма. Чтобы принудительно пересчитать квоты всех почтовых ящиков, можно воспользоваться следующей командой:
# doveadm quota recalc -A
1.6. Настройка плагина expire

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

Для периодической очистки почтовых ящиков от устаревших сообщений можно добавить в планировщик задач выполнение, например, такой команды:
doveadm expunge -A mailbox Spam savedbefore 2w
Эта команда найдёт в почтовых ящиках пользователей каталоги Spam и удалит из них те сообщения, которые были сохранены в ящик более двух недель назад. Подобным образом можно очищать и другие каталоги, например - Trash.

1.7. Настройка SSL

Настройка SSL будет подробнее рассмотрена в одной из следующих заметок. Там будет описана настройка отдельных сертификатов SSL для разных доменов, а также будет освещён вопрос подготовки самих сертификатов - самоподписанных или подписанных центром сертификации.

Если у вас имеются готовый подписанный сертификат, можно включить поддержку SSL в файле /etc/dovecot/conf.d/10-ssl.conf и указать в нём пути к файлам сертификата:
ssl = yes
ssl_cert = </etc/ssl/mail_public.pem
ssl_key = </etc/ssl/mail_private.pem
После настройки сертификатов нужно перезапустить Dovecot, чтобы изменения вступили в силу:
# /etc/init.d/dovecot restart
1.8. Настройка плагина sieve

Sieve - это скрипты фильтрации почты, которые выполняются агентом локальной доставки (LDA) в момент получения письма от почтового сервера (MTA). Скрипты позволяют раскладывать письма в разные папки, ориентируясь на их содержимое - тему письма, получателей, отправителей и т.п. Можно удалить письмо, переслать его на другой ящик или отправить уведомление отправителю, причём использовать можно любое поле заголовка или содержимое тела письма.

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

Конечно, в наши времена больших почтовых сервисов типа Gmail или Яндекс-почты, этим никого не удивишь. Но тут плюс заключается в том, что перед нами не стоит дилемма "удобство" - "безопасность". Мы можем хранить почту у себя, не делясь ею с посторонними компаниями, имея над ней полный контроль, и в то же время можем пользоваться удобствами, характерными для больших почтовых сервисов.

Установим пакет с плагином Sieve:
# apt-get install dovecot-sieve
Включим использование плагина в файле /etc/dovecot/conf.d/15-lda.conf:
protocol lda {
  mail_plugins = $mail_plugins sieve
}
Укажем настройки плагина в файле /etc/dovecot/conf.d/90-sieve.conf:
plugin {
  sieve = /home/vmail/%Ld/%n/active.sieve # Расположение активного скрипта
  sieve_dir = /home/vmail/%Ld/%n/sieve    # Каталог для скриптов
  sieve_max_script_size = 1M              # Максимальный размер одного скрипта
  sieve_quota_max_scripts = 50            # Максимальное количество скриптов
  sieve_quota_max_storage = 1M            # Максимальный общий объём скриптов
}
Каждый пользователь может обладать собственным набором Sieve-скриптов, из которых в любой момент времени активным может быть только один. Каталог для скриптов указывается в настройке sieve_dir, а в настройке sieve указывается имя символической ссылки, которая будет указывать на активный скрипт.

После настройки плагина нужно перезапустить Dovecot, чтобы изменения вступили в силу:
# /etc/init.d/dovecot restart
Подробнее о скриптах Sieve можно почитать на Википедии, в статье Sieve.

1.9. Настройка сервиса managesieve

Плагин sieve не был бы столь полезным, если бы Sieve-скриптами нельзя было бы управлять прямо из почтового клиента. Именно эту функцию и реализует сервис ManageSieve. Он ожидает подключений клиентов на отдельном TCP-порту 4190. Для управления скриптами клиент использует учётные данные своего почтового ящика.

Для включения сервиса достаточно лишь установить дополнительный пакет:
# apt-get install dovecot-managesieve
В следующих заметках фильтрация писем при помощи Sieve будет рассмотрена подробнее - я покажу, как им пользоваться в почтовых клиентах.

1.10. Результирующий файл конфигурации

Поскольку настроек очень много, проверить их можно при помощи следующей команды:
$ doveconf -n
Опция n предписывает показывать только те настройки, которые отличаются от настроек по умолчанию. У меня со всеми плагинами, настройка которых была тут описана, команда выдаёт следующий результат:
# 2.1.7: /etc/dovecot/dovecot.conf
# OS: Linux 3.2.0-4-amd64 x86_64 Debian 7.5 ext4
auth_default_realm = domain.tld
auth_mechanisms = plain login
disable_plaintext_auth = no
first_valid_gid = 120
first_valid_uid = 120
last_valid_gid = 120
last_valid_uid = 120
listen = *
log_timestamp = "%Y-%m-%d %H:%M:%S "
mail_gid = vmail
mail_home = /home/vmail/%Ld/%Ln
mail_location = maildir:/home/vmail/%Ld/%Ln
mail_plugins = quota acl
mail_uid = vmail
managesieve_notify_capability = mailto
managesieve_sieve_capability = fileinto reject envelope encoded-character vacation subaddress comparator-i;ascii-numeric relational regex imap4flags copy include variables body enotify environment mailbox date ihave
namespace {
  list = children
  location = maildir:%%h:INDEX=%h/shared/%%u
  prefix = shared/%%u/
  separator = /
  subscriptions = yes
  type = shared
}
namespace inbox {
  inbox = yes
  location = 
  mailbox Drafts {
    special_use = \Drafts
  }
  mailbox Junk {
    special_use = \Junk
  }
  mailbox Sent {
    special_use = \Sent
  }
  mailbox Trash {
    special_use = \Trash
  }
  prefix = 
  separator = /
  type = private
}
passdb {
  args = scheme=CRYPT username_format=%u /etc/dovecot/passwd
  driver = passwd-file
}
plugin {
  acl = vfile
  acl_shared_dict = file:/home/vmail/%Ld/shared-mailboxes.db
  quota = dict:user::file:%h/dovecot-quota
  quota_rule = *:
  sieve = /home/vmail/%Ld/%Ln/active.sieve
  sieve_dir = /home/vmail/%Ld/%Ln/sieve
  sieve_max_script_size = 1M
  sieve_quota_max_scripts = 50
  sieve_quota_max_storage = 1M
}
postmaster_address = postmaster@domain.tld
protocols = " imap sieve pop3"
service auth {
  unix_listener auth-client {
    group = Debian-exim
    mode = 0660
  }
}
ssl_cert = </etc/ssl/mail.domain.tld.public.pem
ssl_key = </etc/ssl/mail.domain.tld.private.pem
userdb {
  args = username_format=%u /etc/dovecot/passwd
  default_fields = uid=vmail gid=vmail userdb_home=/home/vmail/%Ld/%Ln userdb_location=maildir:/home/vmail/%Ld/%Ln userdb_quota_rule=*:storage=1G
  driver = passwd-file
}
protocol lda {
  mail_plugins = quota acl sieve
}
protocol imap {
  mail_plugins = quota acl imap_quota imap_acl
}
2. Настройка Exim

Настройка Exim, как уже было сказано, зависит от настроенного Dovecot. Exim использует сервис Dovecot для SMTP-аутентификации и список почтовых ящиков из файла /etc/dovecot/passwd.

2.1. Установка

Установим SMTP-сервер Exim:
# apt-get install exim4-daemon-heavy
Создаём файл конфигурации /etc/exim4/exim4.conf со следующим начальным содержимым:
# Имя нашей почтовой системы
primary_hostname = mail.domain.tld

# Список доменов нашей почтовой системы
domainlist local_domains = /etc/exim4/local_domains

# Список доменов, для которых наша почтовая система является резервной
domainlist relay_domains = /etc/exim4/relay_domains

# Список узлов, почту от которых будем принимать без проверок
hostlist relay_from_hosts = 

# Правила для проверок
acl_not_smtp = acl_check_not_smtp
acl_smtp_rcpt = acl_check_rcpt
acl_smtp_data = acl_check_data

# Сокет-файл антивируса ClamAV
av_scanner = clamd:/var/run/clamav/clamd.ctl
# Сокет-файл SpamAssassin
# spamd_address =

# Отключаем IPv6, слушаем порты 25 и 587
disable_ipv6
daemon_smtp_ports = 25 : 587

# Дописываем домены отправителя и получателя, если они не указаны
qualify_domain = domain.tld
qualify_recipient = domain.tld

# Exim никогда не должен запускать процессы от имени пользователя root
never_users = root

# Проверять прямую и обратную записи узла отправителя по DNS
host_lookup = *

# Отключаем проверку пользователей узла отправителя по протоколу ident
rfc1413_hosts = *
rfc1413_query_timeout = 0s

# Только эти узлы могут не указывать домен отправителя или получателя
sender_unqualified_hosts = +relay_from_hosts
recipient_unqualified_hosts = +relay_from_hosts

# Лимит размера сообщения, 30 мегабайт
message_size_limit = 30M

# Запрещаем использовать знак % для явной маршрутизации почты
percent_hack_domains =

# Настройки обработки ошибок доставки, используются значения по умолчанию
ignore_bounce_errors_after = 2d
timeout_frozen_after = 7d

begin acl

  # Проверки для локальных отправителей
  acl_check_not_smtp:
     accept

  # Проверки на этапе RCPT
  acl_check_rcpt:
    accept hosts = :

    # Отклоняем неправильные адреса почтовых ящиков  
    deny message = Restricted characters in address
         domains = +local_domains
         local_parts = ^[.] : ^.*[@%!/|]

    # Отклоняем неправильные адреса почтовых ящиков  
    deny message = Restricted characters in address
         domains = !+local_domains
         local_parts = ^[./|] : ^.*[@%!] : ^.*/\\.\\./

    # В локальные ящики postmaster и abuse принимает почту всегда
    accept local_parts = postmaster : abuse
           domains = +local_domains

    # Проверяем существование домена отправителя
    require verify = sender

    # Принимаем почту от доверенных узлов, попутно исправляя заголовки письма
    accept hosts = +relay_from_hosts
           control = submission

    # Принимаем почту от аутентифицированных узлов, попутно исправляя заголовки письма
    accept authenticated = *
           control = submission/domain=

    # Для не доверенных и не аутентифицированных требуется, чтобы получатель был в домене,
    # ящик которого находится у нас или для которого мы являемся резервным почтовым сервером
    require message = Relay not permitted
            domains = +local_domains : +relay_domains

    # Если домен правильный, то проверяем получателя
    require verify = recipient

    accept
 
begin routers

  # Поиск транспорта для удалённых получателей
  dnslookup:
    driver = dnslookup
    domains = ! +local_domains
    transport = remote_smtp
    ignore_target_hosts = 0.0.0.0 : 127.0.0.0/8
    no_more

  # Пересылки для локальных получателей из файла /etc/aliases
  system_aliases:
    driver = redirect
    allow_fail
    allow_defer
    domains = domain.tld
    data = ${lookup{$local_part}lsearch{/etc/aliases}}

  # Пересылки для получателей в разных доменах
  aliases:
    driver = redirect
    allow_fail
    allow_defer
    data = ${lookup{$local_part@$domain}lsearch{/etc/exim4/aliases}}  

  # Получение почты на локальный ящик
  mailbox:
    driver = accept
    condition = ${lookup{$local_part@$domain}lsearch{/etc/dovecot/passwd}{yes}{no}}
    user = dovecot
    transport = dovecot_virtual_delivery
    cannot_route_message = Unknown user

begin transports

  # Транспорт для удалённых получателей
  remote_smtp:
    driver = smtp

  # Транспорт для локальных получателей из Dovecot
  dovecot_virtual_delivery:
    driver = pipe
    command = /usr/lib/dovecot/dovecot-lda -d $local_part@$domain -f $sender_address
    message_prefix =
    message_suffix =
    delivery_date_add
    envelope_to_add
    return_path_add
    log_output
    user = vmail
    temp_errors = 64 : 69 : 70: 71 : 72 : 73 : 74 : 75 : 78

  begin retry

  *   *   F,2h,15m; G,16h,1h,1.5; F,4d,6h

  begin rewrite

begin authenticators

  # Использование LOGIN-аутентификации из Dovecot
  dovecot_login:
    driver = dovecot
    public_name = LOGIN
    server_socket = /var/run/dovecot/auth-client
    server_set_id = $auth1

  # Использование PLAIN-аутентификации из Dovecot  
  dovecot_plain:
    driver = dovecot
    public_name = PLAIN
    server_socket = /var/run/dovecot/auth-client
    server_set_id = $auth1
Сразу поменяем права доступа к файлу конфигурации:
# chmod u=rw,g=r,o= /ect/exim4/exim4.conf
# chown root:Debian-exim /etc/exim4/exim4.conf
В этом случае можно дать остальным пользователям доступ на чтение, т.к. никакой особо секретной информации в файле конфигурации нет. С другой стороны - нужды давать такой доступ тоже нет.

Чтобы Exim мог читать файл /etc/dovecot/passwd, включим пользователя Debian-exim в группу dovecot:
# usermod -aG dovecot Debian-exim
Осталось запустить Exim, чтобы он начал работать в минимальной конфигурации:
# /etc/init.d/exim4 start
2.2. Настройка антивируса

Устанавливаем демон ClamAV для проверки файлов на вирусы:
# apt-get install clamav-daemon
Сразу же обновляем антивирусную базу:
# freshclam
Включим clamav в группу Debian-exim, чтобы он мог сканировать файлы, созданные Exim'ом:
# usermod -aG Debian-exim clamav
Добавим в главную секцию файла /etc/exim4/exim4.conf путь к сокет-файлу ClamAV и ACL для этапа DATA:
av_scanner = clamd:/var/run/clamav/clamd.ctl
acl_smtp_data = acl_check_data
В секцию acl файла /etc/exim4/exim4.conf добавим правило, запрещающее приём писем, содержащих вирусы:
acl_check_data:

  deny message = message contains a virus ($malware_name)
       malware = *

  accept
Перезагрузим Exim, чтобы настройки вступили в силу:
# /etc/init.d/exim4 reload
Осталось проверить, что антивирусная система используется. Для этого создадим специально предназначенный для таких целей тестовый файл EICAR:
$ echo -n 'X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' > eicar.txt
И попробуем его отправить во вложении с какого-нибудь почтового ящика почтовой системы на тот же ящик. Если письмо не пришло, значит антивирусная система работает. Для полной уверенности можно ещё заглянуть в журнал почтовой системы /var/log/exim4/mainlog, где должна появиться строчка вида:
2014-04-12 22:06:06 1WZ0RR-0007TO-RM H=localhost (domain.tld) [127.0.0.1] F= A=dovecot_plain:box@domain.tld rejected after DATA: message contains a virus (Eicar-Test-Signature)

2.3. Проверка квот

В этом варианте настройки Exim проверкой квот не занимается. Если для одного из получателей письма будет превышена квота, письмо всё равно будет принято к доставке и лишь потом отправителю вернётся рикошет с сообщением об ошибке.

Можно было бы написать небольшой скрипт для проверки квот, но делать этого я не стал. Если вы считаете, что без проверки квот на этапе RCPT протокола ESMTP обойтись ну никак нельзя, попробуйте написать этот скрипт самостоятельно. Я не думаю, что он займёт больше пары десятков строчек. Не забудьте только, что стоит использовать действие discard вместо deny, чтобы в случае нескольких получателей письмо было доставлено в те ящики, квота которых не превышена.

2.4. Настройка SSL/TLS

Изменим настройки прослушиваемых портов в главной секции файла /etc/exim4/exim4.conf, добавив в список порт 465:
daemon_smtp_ports = 25 : 465 : 587
Добавим настройки TLS в главную секцию файла /etc/exim4/exim4.conf:
tls_on_connect_ports = 465
tls_advertise_hosts = *
tls_certificate = /etc/ssl/mail.domain.tld.public.pem
tls_privatekey = /etc/ssl/mail.domain.tld.private.pem
Порт 465 используется для подключения сразу по защищённому каналу SSL, без явного согласования перехода на защищённый обмен данными. Порт 587 обычно используется для подключений со стороны почтовых клиентов, как правило, с обязательным использованием аутентификации. В рассматриваемой конфигурации порты 25 и 587 никак не различаются, поведение сервера одинаково на обоих портах.

Для задействования добавленных настроек TLS, перезапустим почтовый сервер:
# /etc/init.d/exim4 restart
2.5. Настройка DKIM-подписей

Для удобного создания ключей DKIM-подписей можно установить пакет opendkim-tools:
# apt-get install opendkim-tools
На самом деле необходимые ключи можно генерировать и при помощи openssl, т.к. пакет opendkim-tools содержит набор shell-скриптов, являющихся обёрткой над утилитой openssl.

Теперь создадим каталог для ключей и сгенерируем пару ключей для домена domain.tld:
# mkdir /etc/exim4/dkim
# cd /etc/exim4/dkim
# opendkim-genkey -D /etc/exim4/dkim/ -d domain.tld -s mail
# mv mail.private mail.domain.tld.private
# mv mail.txt mail.domain.tld.public
Далее можно сгенерировать ключи для других доменов, обслуживаемых нашей почтовой системой.

Выставим права доступа к файлам приватных ключей:
# cd /etc/exim4/dkim
# chmod u=rw,g=r,o= *
# chown root:Debian-exim *
Добавляем в секцию транспортов файла /etc/exim4/exim4.conf, в транспорт remote_smtp, настройки для добавления DKIM-подписей к письмам:
remote_smtp:
  driver = smtp
  dkim_domain = ${lc:${domain:$h_from:}}
  dkim_selector = mail
  dkim_private_key = ${if exists{/etc/exim4/dkim/$dkim_selector.$dkim_domain.private} \
                                {/etc/exim4/dkim/$dkim_selector.$dkim_domain.private}{}}
Достаточно перезагрузить конфигурацию, чтобы письма во внешние домены начали подписываться DKIM-ключами:
# /etc/init.d/exim4 reload
2.6. Проверка DKIM-подписей

Воспользуемся встроенной в Exim возможностью проверки DKIM-подписей входящих писем. Я буду проверять подписи у тех писем, в которых они есть. Плюс к тому, будем требовать наличия правильной DKIM-подписи для доменов публичных почтовых сервисов, о которых заведомо известно, что они добавляют DKIM-подписи к своим письмам. Это позволит защититься от поддельных писем, якобы исходящих из доменов этих почтовых сервисов.

Зададим в главной секции файла /etc/exim4/exim4.conf домены, для которых требуется правильная DKIM-подпись:
domainlist dkim_required_domains = gmail.com : yandex.ru : rambler.ru : \
                                   mail.ru : bk.ru : list.ru : inbox.ru
В эту же главную секцию файла /etc/exim4/exim4.conf добавим имя списка управления доступом, который будет проверять DKIM-подпись:
acl_smtp_dkim = acl_check_dkim
В секцию acl файла /etc/exim4/exim4.conf добавим описание самого списка управления доступом:
acl_check_dkim:

  # Отклоняем письма с неправильной DKIM-подписью
  deny message = Wrong DKIM signature 
       dkim_status = fail

  # Для выбранных доменов требуем наличия DKIM-подписи
  deny message = Valid DKIM signature needed for mail from $sender_domain
       sender_domains = +dkim_required_domains
       dkim_status = none

  accept
Перезагрузим файл конфигурации Exim, чтобы настройки вступили в силу:
# /etc/init.d/exim4 reload
2.7. Настройка грейлистинга

Для грейлистинга воспользуемся демоном greylistd, написанном на Python. Этот демон не настолько сложен, как milter-greylist, которым я воспользовался для настройки грейлистинга в Postfix, однако его простота с лихвой компенсируется возможностями Exim. Установим пакет greylistd:
# apt-get install greylistd
greylistd предоставляет механизм, а политику можно определить в конфигурации Exim. Я придерживаюсь политики подвергать грейлистингу те узлы, которые оказались в чёрном списке. Для того, чтобы включить грейлистинг, нужно в самый конец списка управления доступом acl_check_rcpt до финального правила accept добавить следующую проверку:
defer message = Greylisting in action, try later
      !senders = :
      !hosts = ${if exists{/etc/greylistd/whitelist-hosts}\
                          {/etc/greylistd/whitelist-hosts}{}} : \
               ${if exists{/var/lib/greylistd/whitelist-hosts}\
                          {/var/lib/greylistd/whitelist-hosts}{}}
      dnslists = zen.spamhaus.org
      condition = ${readsocket{/var/run/greylistd/socket}\
                              {--grey $sender_host_address $sender_address $local_part@$domain}\
                              {5s}{}{false}}
В поле !senders можно прописать адреса тех отправителей, которые не должны подвергаться грейлистингу. Соответственно, чтобы узел с определённым IP-адресом не подвергался грейлистингу, его можно добавить в файл /etc/greylistd/whitelist-hosts.

Включим Debian-exim в группу greylist, чтобы Exim имел доступ к сокету и файлам greylistd:
# usermod -aG greylist Debian-exim
Осталось попросить Exim перезагрузить файл конфигурации, чтобы новые настройки вступили в силу:
# /etc/init.d/exim4 reload
2.8. Настройка SPF-записи

SPF-запись - это TXT-запись следующего вида:
domain.tld. IN TXT "v=spf1 +mx ~all"
Если указанный домен обслуживает Sender Policy Framework, описывающему синтаксис SFP-записи - SPF Record Syntax. Стоит также прочесть о наиболее частых ошибках, допускаемых при создании SFP-записи - Common mistakes.

2.9. Проверка SPF-записей

Имеются разные способы проверки SPF-записей почтовой системой Exim. Сейчас в официальном дитрибутиве Debian поставляются конфигурационные файлы, проверяющие SPF-записи при помощи Perl-программы из пакета libmail-spf-perl. При этом каждая проверка инициирует новый запуск программы. На мой взгляд это довольно расточительно. Ранее существовал пакет libmail-spf-query-perl, в составе которого имелся демон, к которому можно было обратиться через Unix-сокет. Этот способ уже гораздо лучше и по сути ничем не отличается от грейлистинга при помощи демона greylistd на Python'е. Однако сейчас этот пакет не поставляется в репозитории Debian и, похоже, вообще не поддерживается авторами.

Имеется ещё один способ проверки SPF-записей - при помощи самого Exim. Однако эта опция считается экспериментальной и поэтому отключена по умолчанию. Пакеты в Debian собраны тоже без нативной поддержки проверки SPF-записей. Поддержка эта имеется в Exim уже многие годы и многие годы носит статус экспериментальной. Я решил попробовать собрать пакет, в котором поддержка проверки SPF-записей включена.

Для начала скачиваем необходимое для сборки Exim:
# apt-get build-dep exim4
# apt-get source exim4
# cd exim4-4.80
Открываем файл src/EDITME в текстовом редакторе и раскомментируем строчки, включающие поддержку SPF:
EXPERIMENTAL_SPF=yes
CFLAGS  += -I/usr/local/include
LDFLAGS += -lspf2
Вызываем команду редактирования журнала изменений пакета:
# dch -i
Отмечаем изменения, которые внесли в пакет:
exim4 (4.80-7.1) UNRELEASED; urgency=low

  * Non-maintainer upload.
  * Enabled experimental SPF support.

 -- Vladimir Stupin <vladimir@stupin.su>  Sat, 12 Apr 2014 19:45:04 +0600
Установим библиотеку и заголовочные файлы:
# apt-get intall libspf2-2 libspf2-dev
Собираем пакет:
# dpkg-buildpackage -us -uc -b -rfakeroot
Создаём файл /etc/apt/preferences.d/exim4 в текстовом редакторе и вносим настройки, фиксирующие пакет в системе:
Package: exim4-daemon-heavy
Pin: version 4.80-7.1
Pin-Priority: 1003
Зафиксировать пакет нужно для того, чтобы пакет из дистрибутива не заменил собранный нами вручную. Поскольку дистрибутивный пакет собран без поддержки SPF, он не сможет понять правило проверки SPF в файле конфигурации и не запустится. В результате почтовая система не будет работать. Если в дистрибутиве появится обновление пакета, пакет придётся пересобрать и установить самостоятельно.

Теперь установим пакет с поддержкой SPF:
# cd ..
# dpkg -i exim4-daemon-heavy_4.80-7.1_amd64.deb
SPF-записи могут классифицировать IP-адрес одним из следующих образов:
  • pass (+) - рекомендуется принять почту,
  • fail (-) - рекомендуется отклонить почту,
  • softfail (~) - рекомендуется принять почту, но пометить её как подозрительную,
  • neutral (?) - рекомендуется обрабатывать почту таким образом, как будто SPF-записи не существует.
Дополнительно, есть ещё два статуса, которые сообщают о постоянной или временной ошибке проверки IP-адреса.

Когда SPF-записи были только придуманы, некоторые системные администраторы слишком буквально воспринимали их рекомендации. Случались ситуации, когда первичный почтовый сервер не принимал почту от своего резервного сервера лишь потому, что его IP-адресу соответствовала SPF-запись, предписывающая не принимать письмо. Поэтому сложилась практика не указывать политику fail, а использовать вместо неё политики softfail или neutral. На мой взгляд, если бы не было таких прямолинейных системных администраторов, не было бы никакого смысла в политиках, отличных от pass и fail.

Нормальная почта может исходить только от серверов отправителя и должна приходить на серверы получателя без каких-либо посторонних промежуточных серверов. Сервер получателя должен проверять соответствие отправителя SPF-политике на основных и резервных серверах, а при приёме писем с резервного сервера на основной уже не обращать внимания на то, что его резервный сервер не удовлетворяет политике SPF. Именно поэтому я воспринимаю политики softfail и neutral точно так же, как воспринимаю политику fail. Я в любом случае приму почту от резервного сервера, не смотря на рекомендации SPF-записи, но на резервном сервере я обязательно их проверю.

Перед правилами проверки квот почтовых ящиков в списке управления доступом acl_check_rcpt в секции acl файла /etc/exim4/exim4.conf добавим следующее правило, соответствующее описанным выше соображениям:
deny message = Reject due SPF policy
     spf = fail : softfail : neutral
Осталось лишь перезапустить Exim, чтобы заработал демон из собранного нами пакета и вступили в силу новые настройки:
# /etc/init.d/exim4 stop
# /etc/init.d/exim4 start
2.10. Требование аутентификации

Чтобы запретить локальным пользователям отправлять почту без аутентификации, добавим в конфигурацию такое правило:
deny message = Local sender must be authenticated
     sender_domains = +local_domains
     !authenticated = *
Чтобы аутентифицированный пользователь не пытался подставить чужой адрес отправителя, добавим в конфигурацию такое правило:
deny message = Send your own mail from yourself
     condition = ${if eq{$authenticated_id}{$sender_address}{no}{yes}}
     authenticated = *
Оба правила нужно добавить в секцию acl файла /etc/exim4/exim4.conf, в правило acl_check_rcpt перед принятием почты от аутентифицированных пользователей.

Осталось перезагрузить файл конфигурации, чтобы она вступила в силу:
# /etc/init.d/exim4 reload
2.11. Итоговый файл конфигурации

В конечном итоге у меня получился такой файл конфигурации /etc/exim4/exim4.conf:
# Имя нашей почтовой системы
primary_hostname = mail.domain.tld

# Список доменов нашей почтовой системы
domainlist local_domains = /etc/exim4/local_domains

# Список доменов, для которых наша почтовая система является резервной
domainlist relay_domains = /etc/exim4/relay_domains

# Список узлов, почту от которых будем принимать без проверок
hostlist relay_from_hosts = 

# Домены, для которых требуется наличие правильной DKIM-подписи
domainlist dkim_required_domains = gmail.com : yandex.ru : rambler.ru : \
                                   mail.ru : bk.ru : list.ru : inbox.ru

# Правила для проверок
acl_not_smtp = acl_check_not_smtp
acl_smtp_rcpt = acl_check_rcpt
acl_smtp_data = acl_check_data
acl_smtp_dkim = acl_check_dkim

# Сокет-файл антивируса ClamAV
av_scanner = clamd:/var/run/clamav/clamd.ctl

# Сокет-файл SpamAssassin
# spamd_address =

# Отключаем IPv6, слушаем порты 25, 465 и 587
disable_ipv6
daemon_smtp_ports = 25 : 465 : 587
tls_on_connect_ports = 465

# Настройки сертификатов SSL/TLS
tls_advertise_hosts = *
tls_certificate = /etc/ssl/mail.domain.tld.public.pem
tls_privatekey = /etc/ssl/mail.domain.tld.private.pem

# Дописываем домены отправителя и получателя, если они не указаны
qualify_domain = domain.tld
qualify_recipient = domain.tld

# Exim никогда не должен запускать процессы от имени пользователя root
never_users = root

# Проверять прямую и обратную записи узла отправителя по DNS
host_lookup = *

# Отключаем проверку пользователей узла отправителя по протоколу ident
rfc1413_hosts = *
rfc1413_query_timeout = 0s

# Только эти узлы могут не указывать домен отправителя или получателя
sender_unqualified_hosts = +relay_from_hosts
recipient_unqualified_hosts = +relay_from_hosts

# Лимит размера сообщения, 30 мегабайт
message_size_limit = 30M

# Запрещаем использовать знак % для явной маршрутизации почты
percent_hack_domains =

# Настройки обработки ошибок доставки, используются значения по умолчанию
ignore_bounce_errors_after = 2d
timeout_frozen_after = 7d

begin acl

  acl_check_not_smtp:

    accept

  # Проверки на этапе RCPT
  acl_check_rcpt:

    accept hosts = :

    # Отклоняем неправильные адреса почтовых ящиков
    deny message = Restricted characters in address
         domains = +local_domains
         local_parts = ^[.] : ^.*[@%!/|]

    # Отклоняем неправильные адреса почтовых ящиков
    deny message = Restricted characters in address
         domains = !+local_domains
         local_parts = ^[./|] : ^.*[@%!] : ^.*/\\.\\./

    # В локальные ящики postmaster и abuse принимает почту всегда
    accept local_parts = postmaster : abuse
           domains = +local_domains

    # Проверяем существование домена отправителя
    require verify = sender

    # Принимаем почту от доверенных узлов, попутно исправляя заголовки письма
    accept hosts = +relay_from_hosts
           control = submission

    # Не даём локальным отправителям слать почту без аутентификации
    deny message = Local sender must be authenticated
         sender_domains = +local_domains
         !authenticated = *

    # Не даём локальным отправителям представляться чужим именем
    deny message = Send your own mail from yourself
         condition = ${if eq{$authenticated_id}{$sender_address}{no}{yes}}
         authenticated = *
  
    # Принимаем почту от аутентифицированных узлов, попутно исправляя заголовки письма
    accept authenticated = *
           control = submission/domain=

    # Для не доверенных и не аутентифицированных требуется, чтобы получатель был в домене,
    # ящик которого находится у нас или для которого мы являемся резервным почтовым сервером
    require message = Relay not permitted
            domains = +local_domains : +relay_domains

    # Проверяем домена удалённого получателя или адрес локального получателя
    require verify = recipient

    # Отклоняем письма, не соответствующие политике домена отправителя
    deny message = Reject due SPF policy
         spf = fail : softfail : neutral

    # Если отправитель попал в чёрный список, отправляем его в грейлистинг
    defer message = Greylisting in action, try later
          !senders = :
          !hosts = ${if exists{/etc/greylistd/whitelist-hosts}\
                              {/etc/greylistd/whitelist-hosts}{}} : \
                   ${if exists{/var/lib/greylistd/whitelist-hosts}\
                              {/var/lib/greylistd/whitelist-hosts}{}}
          dnslists = zen.spamhaus.org
          condition = ${readsocket{/var/run/greylistd/socket}\
                                  {--grey $sender_host_address $sender_address $local_part@$domain}\
                                  {5s}{}{false}}

    accept

  acl_check_data:

    # Отклоняем письма, содержащие вирусы
    deny message = Message contains a virus ($malware_name)
         malware = *

    accept

  acl_check_dkim:

    # Отклоняем письма, содержащие DKIM-подпись, если она не правильная
    deny message = Wrong DKIM signature
         dkim_status = fail

    # Отклоняем письма, не содержащие DKIM-подпись, предотвращая подделку писем
    # из крупных почтовых систем, которые всегда добавляют DKIM-подпись
    deny message = Valid DKIM signature needed for mail from $sender_domain
         sender_domains = +dkim_required_domains
         dkim_status = none

    accept

begin routers

  # Поиск транспорта для удалённых получателей
  dnslookup:
    driver = dnslookup
    domains = ! +local_domains
    transport = remote_smtp
    ignore_target_hosts = 0.0.0.0 : 127.0.0.0/8
    no_more

  # Пересылки для получателей в домене domain.tld
  system_aliases:
    driver = redirect
    allow_fail
    allow_defer
    domains = domain.tld
    data = ${lookup{$local_part}lsearch{/etc/aliases}}

  # Пересылки для получателей в разных доменах
  aliases:
    driver = redirect
    allow_fail
    allow_defer
    data = ${lookup{$local_part@$domain}lsearch{/etc/exim4/aliases}}
  
  # Получение почты на локальный ящик
  mailbox:
    driver = accept
    condition = ${lookup{$local_part@$domain}lsearch{/etc/dovecot/passwd}{yes}{no}}
    user = dovecot
    transport = dovecot_virtual_delivery
    cannot_route_message = Unknown user

begin transports

  # Транспорт для удалённых получателей
  # Добавляем к исходящим письмам DKIM-подпись
  remote_smtp:
    driver = smtp
    dkim_domain = ${lc:${domain:$h_from:}}
    dkim_selector = mail
    dkim_private_key = ${if exists{/etc/exim4/dkim/$dkim_selector.$dkim_domain.private} \
                                  {/etc/exim4/dkim/$dkim_selector.$dkim_domain.private}{}}

  # Транспорт для локальных получателей из Dovecot
  dovecot_virtual_delivery:
    driver = pipe
    command = /usr/lib/dovecot/dovecot-lda -d $local_part@$domain -f $sender_address
    message_prefix =
    message_suffix =
    delivery_date_add
    envelope_to_add
    return_path_add
    log_output
    user = vmail
    temp_errors = 64 : 69 : 70: 71 : 72 : 73 : 74 : 75 : 78

begin retry

  *   *   F,2h,15m; G,16h,1h,1.5; F,4d,6h

begin rewrite

begin authenticators

  # Использование LOGIN-аутентификации из Dovecot
  dovecot_login:
    driver = dovecot
    public_name = LOGIN
    server_socket = /var/run/dovecot/auth-client
    server_set_id = $auth1

  # Использование PLAIN-аутентификации из Dovecot
  dovecot_plain:
    driver = dovecot
    public_name = PLAIN
    server_socket = /var/run/dovecot/auth-client
    server_set_id = $auth1
Заключение

В описанном варианте настройки мне не нравится то, что файлы с настройками не удалось передать в собственность отдельной группы пользователей. Это позволило бы предоставлять определённым системным пользователям доступ на редактирование этих файлов. Кроме того, можно было бы написать даже веб-приложение для управления доменами, почтовыми ящиками и пересылками. Не удалось это сделать по той простой причине, что Dovecot наотрез отказывается использовать вторичные группы при доступе к файлам. Возможно это недоработка самого Dovecot, а может быть - предусмотренное поведение. Сейчас у меня нет острой необходимости разобраться в этом, поэтому я оставил всё как получилось: пользователь Debian-exim состоит в группах dovecot и greylist, а в группе Debian-exim состоит пользователь clamav.

Для проверки квот на этапе RCPT сеанса ESMTP можно было бы воспользоваться не только самописным скриптом, но и сервисом проверки квот quota-status, который появился в Dovecot 2.2 (в Debian Wheezy поставляется Dovecot версии 2.1.7), благо Exim позволяет отправлять запросы в юникс-сокеты и читать из них ответ.

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

46 комментариев:

Анонимный комментирует...

Отличная статья! Внятнее нигде не нашел.

Анонимный комментирует...

Статья и правда отличная.. мне как ламеру как раз самое то..
у меня осталось немного вопросов. Почитаю ману но времене в обрез т.к стары йпочтовик скоро ляжет.. помогите инструкцией по настройке днсов и так что б все бегало и как заводить зверей?
буду очень благодарен..
если есть что почитать вышлите на почту elddh@ukr.net

Анонимный комментирует...

А как пользователей добавить?
Я, как понял, надо прописывать их в файле /etc/dovecot/passwd

morbo комментирует...

Да, туда и добавлять. Об этом написано начиная со строки "В файле /etc/dovecot/passwd могут быть следующие поля:"

Юрий комментирует...

Отличная статья. Спасибо! У меня такой вопрос - в статье упоминается про будущую статью, в которой будет описан процесс создания сертификатов SSL. Статья еще не написана, или я ее просто найти не могу? :)

Юрий комментирует...

Похоже ошибка в статье:
Dovecot не принимает такой опции
auth_verbose_passwords = yes

Потому что:
# In case of password mismatches, log the attempted password. Valid values are
# no, plain and sha1.

morbo комментирует...

>в статье упоминается про будущую статью, в которой будет описан процесс создания сертификатов SSL

Статья тут:
http://vladimir-stupin.blogspot.ru/2014/02/ssltls-postfix-dovecot-lighttpd.html

Эта фраза получилась при копировании частей другой статьи. Вообще, в будущем думаю переделать статью, т.к. в этой много недостатков: есть более простые способы генерации сертификатов, есть тонкости с цепочками сертификатов.

Slavon комментирует...

статья на уровне, спасибо!

не до конца только понял процесс созданию юзеров, например мне нужно завести 3 пользователей, я должен сначала их трёх добавить с помощью скрипта:
/usr/share/doc/exim4-base/examples/exim-adduser

а затем ручками скопировать поле пароль в зашифрованном виде в /etc/dovecot/passwd, например
user1@mydomain:$1$HrHG$KBM2Hozk7GcNa6wa2mH7K0::
user2@mydomain...................................
user3@mydomain.................................
?
и хотя в dovecot wiki написано, что в /etc/dovecot/passwd достаточно минимум 2 поля: user и password, без 3-го пустого поля, разделенного двоеточием не работало...

и еще интересно почему при отсылке из telnet с самого сервера 550 ошибка Relay not permitted, хотя в relay_from_hosts = localhost в Exim

еще раз спасибо за статью!

morbo комментирует...

Для локального приёма почты без аутентификации я использую такую вот конструкцию:

# Принимает почту от доверенных отправителей, попутно исправляя заголовки
accept hosts = 127.0.0.1
senders = zabbix@stupin.su
control = submission

Для генерации хэшей паролей, пригодных для использования в Dovecot, можно воспользоваться утилитой doveadm:

$ doveadm pw -s SHA512 -p my_secret_password

Slavon комментирует...

спасибо! я правильно понял, что например, если мне нужно завести 3 пользователей, я должен сначала их трёх добавить с помощью скрипта:
/usr/share/doc/exim4-base/examples/exim-adduser

а затем трижды исп-ть
$ doveadm pw -s SHA512 -p my_secret_password
?

Slavon комментирует...

еще такой момент хочется уточнить:
создал в Icedove ящик удачно, но почему-то отсутствует папка Junk, куда спам складывать-хотя в конфигурации dovecot она описана... куда копать?

Анонимный комментирует...

Например создал в файле dovecot.passwd запись для почтового адреса, естественно пароль зашифрован при помощи doveadm pw
Пытаюсь зайти на почтовый порт по telnet:
OPEN ip 110
dovecot выдает приглашение, затем пытаюсь залогиниться в почтовый аккаунт:
USER test@test.com
ok
затем нужно указать пароль, пишу
PASS password
выдает ошибку
Не могу понять, почему не получается залогиниться. Найти толковый мануал с внятными примерами пока не получилось.

morbo комментирует...

https://ru.wikipedia.org/wiki/POP3#PASS У меня получилось войти.

Unknown комментирует...

Полезная статья, спасибо.
Только знакомлюсь с exim, долгое время использовал sendmail и пока остался неясным вопрос, как принимать почту для нескольких доменов, есть ли какие-то рецепты по этому поводу? Имена пользователей могут пересекаться. Например: есть 2 домена domain1.org и domain2.org почта для пользователей user@domain1.org и user@domain2.org должна попадать в разные maildir-ы. При отправке почты, в заголовках для пользователей домена domain1.org должен присутствовать domain1.org для пользователей domain2.org соответственно domain2.org
В sendmail это реализуется при помощи файлов подстановок virtusertable и genericstable, как подобного достичь в exim?

morbo комментирует...

Dmitry Mic, а в чём проблема? Здесь никаких файлов не нужно, конфигурация уже готова к такому использованию. Вписываете дополнительный домен в файл /etc/exim4/local_domains и нужные ящики в файл /etc/dovecot/passwd, всё.

Unknown комментирует...

Проблема в том, что изначально заточил dovecot и exim под PAM авторизацию, использовал системных пользователей. Впрочем не вижу препятствий переделать с использованием dovecot passwd
Можно, для прояснения сознания, пример записи для пользователя с доменом из passwd? Как это должно выглядеть?

morbo комментирует...

Вот пример одной строчки:

user@domain.tld:{plain}my_secret_password:::Description for mailbox:::

Где:
* user@domain.tld - полный адрес ящика
* my_secret_password - пароль открытым текстом.
* Description for mailbox - произвольный текст с описанием почтового ящика. Можно не заполнять. В тексте не должны использоваться двоеточия.

Если не хотите хранить пароль открытым текстом, тогда:
1. С помощью команды dovecot pw -l смотрите список доступных способов хэширования пароля,
2. Например, вам понравился SHA512. С помощью команды dovecot pw -s SHA512 генерируете хэш пароля и вписываете его. Вместо {plain} будет соответственно название алгоритма хэширования. В нашем случае - SHA512.

Unknown комментирует...

Спасибо за помощь, таки озарение снизошло. Правда немного подумав над перспективами, хранение пользовательских учеток доверил SQLite3, по своей сути - это тоже самое хранение данных в файле, но с возможностью доступа к ним с использованием SQL запросов, что позволяет впоследствии интегрировать какой-нибудь собственный модуль управления учетными записями куда душа пожелает, не заморачиваясь с парсингом passwd. dovecot нормально авторизует, теперь попробую подружить его с exim-ом )

morbo комментирует...

Dmitry Mic, по сути - любое хранение данных на компьютере - это хранение в файле :) Вопрос в удобстве использования и восстановления. Текстовый файл безо всяких дополнительных сущностей легко редактируется текстовым редактором. Чтобы снять с него резервную копию - нужно его просто скопировать.

Сравните с базой данных - редактировать можно только при помощи дополнительной программы (как минимум - клиента базы данных и зная структуру или SQL-запросы), резервное копирование - отдельный вопрос. Опять же - дополнительная нагрузка на компьютер и больше точек отказа. Зацикленность на базах данных - это бич современности. Если не нужно сложного разделения доступа к учётным данным, возможности одновременного редактирования, данных не много, зависимости между ними простые, то база данных как правило не нужна.

Анонимный комментирует...

Здравствуйте, настраивал по вашей статье. Почему могут быть такие ошибки?
2016-04-20 14:27:34 auth-worker(3765): Info: pam(user@domain.net,127.0.0.1): pam_authenticate() failed: Authentication failure (password mismatch?)
2016-04-20 14:27:34 auth-worker(3765): Info: pam(user@domain.net,127.0.0.1): unknown user
2016-04-20 14:27:34 auth: Error: plain(user@domain.net,127.0.0.1,): user not found from any userdbs
2016-04-20 14:27:34 pop3: Error: Authenticated user not found from userdb, auth lookup id=4137549825 (client-pid=3761 client-id=1)
2016-04-20 14:27:34 pop3-login: Info: Internal login failure (pid=3761 id=1) (internal failure, 1 successful auths): user=, method=PLAIN, rip=127.0.0.1, lip=127.0.0.1, mpid=3767, secured, session=

morbo комментирует...

Alexey, убедитесь, что в файле /etc/dovecot/conf.d/10-auth.conf выбраны только необходимые способы аутентификации. У меня в этом файле подключается только аутентификация по файлу:
#!include auth-deny.conf.ext
#!include auth-master.conf.ext

#!include auth-system.conf.ext
#!include auth-sql.conf.ext
#!include auth-ldap.conf.ext
!include auth-passwdfile.conf.ext
#!include auth-checkpassword.conf.ext
#!include auth-vpopmail.conf.ext
#!include auth-static.conf.ext

Анонимный комментирует...

Да, если оставить только
!include auth-passwdfile.conf.ext
То подключение к Dovecot работает. Спасибо.

Unknown комментирует...

Есть Ubuntu 14 с Vestacp с несколькими сайтами. Нужно показать, как настраивается почтовый сервер с синхронизацией с Яндекс Почта или как угодно. PTR записи у провайдера еще не просил, так как не очень понимаю что просить.
Спасибо.

morbo комментирует...

Ubuntu, Vestacp, Яндекс-почта, "как угодно"... Не понимаю, что нужно. Попробуйте посмотреть сюда: http://vladimir-stupin.blogspot.ru/2014/06/postfix-smtp.html

Unknown комментирует...

Добрый день. Настраиваю на Ubunte 14.4 Dovecot, по вашей инструкции. Буквально сразу у меня после auth_verbose_passwords = yes не стартует Dovecot. Я эту опцию комментирую и опять работает демон dovecot. Иду дальше, дохожу до service auth {
unix_listener auth-client {
mode = 0660
user = Debian-exim
#group =
}
}
и опять демон не работает. в чем может быть дело ?
service dovecot status
dovecot stop/waiting
...........................
dovecot -F
Fatal: service(auth) User doesn't exist: Debian-exim (See service auth { unix_listener /var/run/dovecot/auth-client { user } } setting)
.......................................
dovecot -n
# 2.2.9: /etc/dovecot/dovecot.conf
# OS: Linux 4.4.0-31-generic i686 Ubuntu 14.04.5 LTS ext4
auth_debug = yes
auth_default_realm = okb.loc
auth_mechanisms = plain login cram-md5 digest-md5
disable_plaintext_auth = no
first_valid_gid = 120
first_valid_uid = 120
last_valid_gid = 120
last_valid_uid = 120
log_timestamp = "%Y-%m-%d %H:%M:%S "
mail_debug = yes
mail_gid = vmail
mail_home = /home/vmail/%Ld/%Ln
mail_location = maildir:/home/vmail/%Ld/%Ln
mail_uid = vmail
namespace inbox {
inbox = yes
location =
mailbox Drafts {
special_use = \Drafts
}
mailbox Junk {
special_use = \Junk
}
mailbox Sent {
special_use = \Sent
}
mailbox "Sent Messages" {
special_use = \Sent
}
mailbox Trash {
special_use = \Trash
}
prefix =
}
passdb {
args = scheme=CRYPT username_format=%u /etc/dovecot/passwd
driver = passwd-file
}
protocols = " imap pop3"
service auth {
unix_listener auth-client {
mode = 0600
user = Debian-exim
}
}
ssl_cert = </etc/dovecot/dovecot.pem
ssl_key = </etc/dovecot/private/dovecot.pem
userdb {
args = username_format=%u /etc/dovecot/passwd
default_fields = uid=vmail gid=vmail userdb_home=/home/vmail/%Ld/%Ln userdb_location=maildir:/home/vmail/%Ld/%Ln userdb_quota_rule=*:storage=1G
driver = passwd-file
}

morbo комментирует...

Alex Voloshin, насчёт auth_verbose_passwords = yes прочитайте комментарий Юрия.

Насчёт второй проблемы, эта строчка явным образом сообщает о причине:

Fatal: service(auth) User doesn't exist: Debian-exim (See service auth { unix_listener /var/run/dovecot/auth-client { user } } setting)

В системе нет пользователя Debian-exim.

kudrik_tt комментирует...

Здравствуйте, прочитал вашу статью решил попробовать, застрял на пункте 1.5. При вводе команды "doveadm quota recalc -A" выдает ошибку
"# 2.2.10: /etc/dovecot/dovecot.conf
doveconf: Fatal: Error in configuration file /etc/dovecot/conf.d/10-mail.conf line 106: Unknown setting: mail_uid
"
Никак не могу разобраться, в чем может быть проблема.
mail_uid = vmail
mail_gid = vmail

Спасибо.

kudrik_tt комментирует...

Здравствуйте, очень интересует тема Exim без MySQL, но находится только эта статья. Пробовал вашу конфигурацию на Centos 7, но дальше пункта 1.5 не могу уйти. После ввода команды:
doveadm quota recalc -A

выводиться ошибка
# 2.2.10: /etc/dovecot/dovecot.conf
doveconf: Fatal: Error in configuration file /etc/dovecot/conf.d/10-mail.conf line 106: Unknown setting: mail_uid
в 10-mail.conf как и в инструкции:
mail_uid = vmail
mail_gid = vmail
не могу понять в чем может быть ошибка.

morbo комментирует...

Вот тут https://www.dovecot.org/list/dovecot/2011-August/130355.html и тут https://www.dovecot.org/list/dovecot/2011-August/060478.html пишут:

Вопрошающий: Why is mail_uid and mail_gid an unknown setting? On my other box the configuration is identical (but Dovecot is upgraded from early 2.0.x versions to the current one)

Отвечающий: You probably forgot to set an } somewhere. Can you show us the 10-mail.conf file?

Вопрошающий: Ya, would have to be one of the namespace }'s is missing, as they are
right before mail_uid/gid

Приведу сразу перевод:

Вопрошающий: Почему mail_uid и mail_gid - неизвестные настройки? На другом моём компьютере используется точно такая же конфигурация (только Dovecot обновлён с более ранней версии 2.0.x до текущей)

Отвечающий: Возможно вы забыли где-то указать }. Можете показать ваш файл 10-mail.conf?

Вопрошающий: Ага, действительно } в одном из разделов namespace оказалась пропущенной, прямо перед mail_uid/gid

kudrik_tt комментирует...

Большое спасибо за ответ, но ничего внятного я не нашел. Поэтому решил начать с начала и проверять, до конца настроек пока не дошел, но выяснилось что как только я ввожу в конфиг /etc/dovecot/conf.d/10-logging.conf строку
auth_verbose_passwords = yes

сервис dovecot:
dovecot.service - Dovecot IMAP/POP3 email server
Loaded: loaded (/usr/lib/systemd/system/dovecot.service; disabled; vendor preset: disabled)
Active: failed (Result: signal) since Thu 2018-01-25 12:44:11 +06; 1s ago
Process: 1810 ExecStart=/usr/sbin/dovecot -F (code=killed, signal=SEGV)
Process: 1803 ExecStartPre=/usr/libexec/dovecot/prestartscript (code=exited, status=0/SUCCESS)
Main PID: 1810 (code=killed, signal=SEGV)

Jan 25 12:44:11 mail systemd[1]: Starting Dovecot IMAP/POP3 email server...
Jan 25 12:44:11 mail systemd[1]: Started Dovecot IMAP/POP3 email server.
Jan 25 12:44:11 mail systemd[1]: dovecot.service: main process exited, code=killed, status=11/SEGV
Jan 25 12:44:11 mail systemd[1]: Unit dovecot.service entered failed state.
Jan 25 12:44:11 mail systemd[1]: dovecot.service failed.

dovecot -n

Segmentation fault
Пока убрал эту строку, чтобы все работало.

kudrik_tt комментирует...

Нашел в чем могла быть причина неисправности, в файле /etc/dovecot/conf.d/10-mail.conf при введении конфига acl, закрывающую скобку мог не заметить раскоментировать , в блоке
namespace {
type = shared
separator = /
prefix = shared/%%u/
location = maildir:%%h:INDEX=%h/shared/%%u
subscriptions = yes
list = children
}
Спасибо. А вы не подскажите аналог sieve и managesieve для Centos 7 есть?
Еще раз спасибо.

morbo комментирует...

Насчёт настройки auth_verbose_passwords = yes выше в комментариях Юрий уже отписался.

А вот Segmentation fault из-за ошибки в файле конфигурации - это очень неожиданно :)

morbo комментирует...

Вообще, Sieve и ManageSieve - это плагины Dovecot. Они могут идти как в составе основного пакета, так и в отдельных пакетах.

CentOS не пользуюсь, поэтому подсказать насчёт неё ничего не могу. Вот тут https://serveradmin.ru/nastroyka-postfix-dovecot-centos-7/ другой системный администратор по имени Владимир описывает настройку почтового сервера под CentOS 7, пишет там про настройки Sieve и ManageSieve, из чего делаю вывод, что нужное вам в CentOS 7 таки есть.

Bi комментирует...

Ещё один блог сисадмина: Exim и Dovecot без SQL



Добрый день,

Спасибо за статью, очень помогла!

Использую Ваш конфиг как пример, все насетапил, входящая почта работает, при попытке отправить сообщение к имени отправителя добавляется домен отправителя, т.е. получаю вместо user@domai.ltd такую конструкцию - "user@domai.ltd"@domain.ltd

2018-03-17 17:10:06 1exEPG-00099N-D9 <= "user@domai.ltd"@domain.ltd H=d911-164.XXX.XX ([192.168.0.105]) [XX.XX.XX.XX] P=esmtpa A=dovecot_plain:user@domain.ltd S=846 id=1da4638c-fa92-bf4d-9365-d078e8cbb1f6@domain.ltd

Подскажите пжлст где исправить...
Спасибо!

Bi комментирует...

Ещё один блог сисадмина: Exim и Dovecot без SQL


Добрый день,
Спасибо за статью, очень помогла!

Использую Ваш конфиг как пример, все насетапил, входящая почта работает, при попытке отправить сообщение к имени отправителя добавляется домен отправителя, т.е. получаю вместо user@domai.ltd такую конструкцию - "user@domai.ltd"@domain.ltd

2018-03-17 17:10:06 1exEPG-00099N-D9 <= "user@domai.ltd"@domain.ltd H=d911-164.XXX.XX ([192.168.0.105]) [XX.XX.XX.XX] P=esmtpa A=dovecot_plain:user@domain.ltd S=846 id=1da4638c-fa92-bf4d-9365-d078e8cbb1f6@domain.ltd

Подскажите пжлст где исправить...
Спасибо!

Bi комментирует...

Разобрася... проблема была в control = submission

accept authenticated = *
#control = submission
control = submission/sender_retain

Сейчас все ОК

Slavko комментирует...

Hi,

did find you a way how to use dovecot's quota-status service from exim's ACL, please?

morbo комментирует...

Slavko, стало интересно, получится ли у меня сделать это. Получилось.

В файл /etc/dovecot/conf.d/90-quota.conf вписываем:
plugin {
quota_status_success = OK
quota_status_nouser = NOUSER
quota_status_overquota = OVER
}

service quota-status {
executable = quota-status -p postfix

unix_listener exim-quota-status {
mode = 0660
user = Debian-exim
group = Debian-exim
}

client_limit = 1
}

В файл конфигурации /etc/exim4/exim4.conf перед ACL грейлистинга вставил такую проверку:
defer message = 422 Mailbox $local_part@$domain is over quota
domains = +local_domains
condition = ${if eq{${extract{action}\
{${readsocket{/var/run/dovecot/exim-quota-status}\
{recipient=$local_part@$domain\n\n}\
{5s}\
{ }\
{action=FAIL}}}}}\
{OVER}\
{yes}\
{no}}

Письма на переполненный ящик отбиваются с таким сообщением в журнале:
2019-06-11 22:48:31 H=forward100p.mail.yandex.net [77.88.28.100] X=TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256 CV=no F= temporarily rejected RCPT : 422 Mailbox xxx@stupin.su is over quota

Тестировал на Debian Stretch. Поскольку до комментариев доберётся не каждый, пожалуй, как нибудь напишу отдельную статью с описанным выше решением.

Slavko комментирует...

Thank you for explanation and config. BTW, you can add the "size=..." line, which will check, if message will cause over quota after delivery, in ACL where size is available. But i only found this in dovecot's sources and not tested yet.

I afraid about this part:

${readsocket{/var/run/dovecot/exim-quota-status}{recipient=$local_part@$domain\n\n}{5s}

I afraid, because I did setup dovecot's quota-status part, it is relative simple and tested it via netcat (unix socket), but i noticed that the connection is not closed after it returns response. I see 5s timeout in your setup, but IMO this can impact the performance (eg. max. connection count), when server is busy (a lot of email arrived) and connections stays open for 5s? I do not know...

Yes, separate article will be nice and appreciated. There is near anything about this, or at least i was not able to find anything and dovecot's docs mentions postfix only.

morbo комментирует...

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

Я тоже заглянул в исходники этого плагина и увидел там разбор поля size. Поэтому фрагмент {recipient=$local_part@$domain\n\n} из указанной мной конфигурации можно переписать так: {size=$message_size\nrecipient=$local_part@$domain\n\n}

Судя по описанию readsocket на странице https://www.exim.org/exim-html-current/doc/html/spec_html/ch-string_expansions.html, Exim пишет запрос и сразу закрывает ту половинку сокета, которая используется для передачи данных в направлении Dovecot. После этого он читает ответ из оставшейся половинки сокета, в которую Dovecot пишет ответ для Exim. Чтобы Exim не закрывал свою половинку сокета, через которую отправляет данные в Dovecot, нужно после таймаута явным образом указать опцию shutdown=no, вот так: {5s:shutdown=no}. Поведение по умолчанию в нашем случае как раз подходящее, поэтому эту опцию писать не нужно.

Если посмотреть со стороны Dovecot, то он может отреагировать на поведение Exim одним из двух способов: 1. либо сразу обнаружить закрытие половинки сокета и закрыть вторую половинку, не отправляя ответ, 2. либо сначала отправить ответ, а потом закрыть свою половинку сокета.

Во-первых, я попробовал сымитировать ситуацию при помощи printf и socat:
printf "recipient=xxx@stupin.su\n\n" | socat STDIO UNIX:/var/run/dovecot/exim-quota-status
action=OVER

Как видно, ответ пришёл.

Во-вторых, я попробовал оттрассировать процесс dovecot/quota-status -p postfix при помощи strace и увидел, что сразу после ответа клиентское подключение через Unix-сокет закрывается. "Невооружённым взглядом" заметно, что таймаута в 5 секунд нет, т.к. вся проверка отрабатывает меньше чем за секунду.

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

Спасибо Вам за интересные вопросы и подсказки.

Slavko комментирует...

You did great debugging, now it is clear, that the dovecot sends response and closes socket. My initial assumption was wrong, and you are right. I did tests with openbsd's version of netcat, but what i simulated was exims' "shutdown=no" behavior, because i omit "-C" option for nc, when ask nc to close connection after write, all seems OK:

echo -en "recipient=slavko@example.tld\n\n" | nc -NCU /run/dovecot/quota-status
action=OK

The connection is closed after reading response (verified by netstat), and i found this thanks your debugs ;-)

BTW, i like you software selection, it is mostly as my choices, eg. Exim, Dovecot, Prosody, XFCE... Nor English, nor Russian are my native languages, but it was nice talk with you. Have nice day and stay writing articles :-)

morbo комментирует...

Slavko, по Вашему нику я подумал что Вы - русский, у которого сейчас нет русской клавиатуры. Например, это бывает в командировках за границу. Но Вы меня удивили :) Оставьте, пожалуйста, ссылку на Ваш блог или сайт, если он у Вас есть.

Slavko комментирует...

No problem, my site is https://www.slavino.sk - as you can see, my native language is Slovak. I didn't advertise my site, because it was not related to asked problem ;-)

Our communication shows, that when people want to communicate, the language needn't to be problem, if they don't want, then common language doesn't help...

I learn Russian in school, 30 - 40 years ago. In last tens years i have minimal chance to use it and i am not familiar with Russian keyboard layout too, then writing in Russian is now problem for me, especially technical Russian. But i still understand.

Slavko комментирует...

For now i don't know, if you decide to not publish my last post, where i post my site URL for you, or i did some mistake and it was not posted, then i try again (to be sure you get it): https://www.slavino.sk

If you do not want to publish it, please confirm it by few words...

morbo комментирует...

Slavko, извините. Не опубликовал из-за ошибки: я сразу же отправился изучать сайт, а подтвердить публикацию комментария забыл :)

nick комментирует...

Доброго всем времени суток. Так как эта статья чуть ли не единственная в своём роде, то напишу здесь.
Может кому пригодится на будущее, ибо мозг я основательно сломал на этом.
Начиная с exim 4.94 данный конфиг exim работать не будет из-за добавленной защиты от "tainted" переменных, к которых относятся и $local_part с $domain.
Ошибка вида:
user@dimain.tld R=dovecot_user T=dovecot_virtual_delivery: Tainted arg 2 for dovecot_virtual_delivery transport command: 'user@dimain.tld.'
com'

Для обхода нужно использовать "untainted variables". То есть не те, которые берутся из письма, а которые можно добыть из локальной базы, в данном случае из файла. Я не придумал как через lookup выдергивать сам ключ - первое значение в строке. А опция data вроде как не работает в route accept. Поэтому добавил extra field в /etc/dovecot/passwd
myusername@domain.tld:{SHA512}MYHASH:::::: localpart=myusername
пробел перед localpart обязателен
Ну нужно переделать route для выдёргивания нужных переменных и в transport-е поменять сами переменные на не "tainted"
...
begin routers
...
# Получение почты на локальный ящик
dovecot_user:
driver = accept
domains = +local_domains
#condition = ${lookup{$local_part@$domain}lsearch{/etc/dovecot/passwd}{yes}{no}}
local_parts=${extract{localpart}{${lookup{$local_part@$domain}lsearch{/etc/dovecot/passwd}}}}
user = vmail
transport = dovecot_virtual_delivery
cannot_route_message = Unknown user
...
begin transports
....
# Транспорт для локальных получателей из Dovecot
dovecot_virtual_delivery:
driver = pipe
command = "/usr/libexec/dovecot/dovecot-lda -d $local_part_data@$domain_data"
.....

Если кто-то знает как сделать более изящно, прошу прокоментировать.