Корпоративная почта

Итак.
Вводная. Имеем организацию, допустим, предоставляющая риэлторские услуги. Есть обширный штат, разбросанный по филиалам города N. Также есть несколько более мелких филиалов по всей Родине, включая районы, где есть только модемное соединение. Организация, как и положено грамотной организации, оснащена контроллером домена. Везде, где позволяют условия (безлимитный интернет) настроено туннелирование и интранет. Но на удалённых местах с некачественной связью такой фокус не проходит.
Задача: настроить почтовый сервер.
Хорошенько подумав, решил завязать авторизацию почтовых клиентов на контроллер. Но как быть с теми, кто не в домене (деревни),и в домене никогда не появится? Создавать с одной задачей - почта? Смысла нет. Переложить авторизацию на базу данных? Доменные пользователи начнут путаться в паролях - уже пройденный этап. Значит будет так: Кто в домене - авторизация через домен, а кто не в домене - через базу данных.
Посмотрев множество решений, убедился, что почти все почтовые сервера (чаще всего MTA & MDA объеденены или уже настроены друг на друга) не умеют работать одновременно с несколькими источниками данных. К тому же, глядя на кризис (покупка новой лицензии в случае Windows плюс абонентское годовое обслуживание) платить наотрез не пожелал. Кстати, очень удивился факту, что большинство системных администраторов считают, что почтовый сервер - это один сервис О_о.
Бороздя просторы интернета и нырнув в пучину документации, возводя тестовые серверные гиганты и руша их в порыве злости, я пришёл к следующей схеме: Dovecot 2 (MDA) + Postfix (MTA, авторизация через Dovecot) + Mysql + LDAP.
В этой статье я рассмотрю установку и настройку этого хозяйства.
В следующей статье я рассмотрю методы защиты. Собираюсь настроить Spamassassin, Clam, Amavis, TLS, DKIM и приклеить некоторые полезные плагины на Dovecot.
Итак, стартуем.
Mysql

У нас уже есть установленная и настроенная серверная версия FreeBSD 10.
На всякий случай, обновляемся
# /etc/update.sh

Сначала ставим Mysql server
# cd /usr/ports/databases/mysql56-server/
# make config-recursive
# make install clean
# rehash

Всё оставляем по умолчанию.
В качестве клиента лично я использую бесплатную HeldiSQL. Прекрасно работает под Wine. Но до этого ещё нужно поднастроить MySql.
# sysrc mysql_enable="YES"
# service mysql-server start
# mysql_secure_installation

http://it.icmp.ru/postimages/8426/7632/full/mysql_secure_installation.jpeg

Из соображений безопасности лично я запрещаю вход руту из сети. Только localhost. Только хардкор!
Для манипуляций с базой пилю себе учётку с входом только из локальной сети:
mysql -u root -p
Enter password:
mysql> grant all privileges on *.* to <someuser>@'<somenet>' identified by '<somepass>' with grant option;
mysql> flush privileges;
mysql> quit;

По схеме. Я немного параноик. Я хочу иметь историю активности пользователей, шифрованные пароли, время последнего редактирования, имя автора изменений и т. д. Имея некоторый опыт в построении баз данных, решил слепить свою схему с азартными играми и дамами несложного поведения уникальной архитектурой. Начнём.
Схема базы данных

Перво-наперво нужено создать базу данных =) Лезем на сервер любимым клиентом.
CREATE DATABASE `mail` /*!40100 COLLATE 'utf8_general_ci' */

Создаю базу "mail". Чтобы проще. По умолчанию использую UTF-8.
Далее идёт список поддерживаемых доменов.
CREATE TABLE `Domains` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`domain` VARCHAR(128) NOT NULL COLLATE 'utf8_unicode_ci',
`editdate` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`edituser` VARCHAR(128) NOT NULL DEFAULT '' COLLATE 'utf8_unicode_ci',
PRIMARY KEY (`id`),
UNIQUE INDEX `UK_Domains_Domain` (`domain`)
)
COLLATE='utf8_unicode_ci'
ENGINE=InnoDB;
DELIMITER //
CREATE TRIGGER `tr_Domains_bi`
BEFORE INSERT
ON `Domains`
FOR EACH ROW
BEGIN
Set New.edituser = User();
END//
DELIMITER ;
DELIMITER //
CREATE TRIGGER `tr_Domains_bu`
BEFORE UPDATE
ON `Domains`
FOR EACH ROW
BEGIN
Set New.edituser = User();
END//
DELIMITER ;

Логика следующая:
  • id - Ключ. Первичный. Заполнять вручную нет смысла. Данные подставляются автоматом. Это типичное поле. Далее я его описывать не буду.
  • domain - Имя домена. Уникальный ключ. Дубликаты - непозволительная роскошь =)
  • editdate - Дата-время последней правки. Заполнять вручную нет смысла. Данные подставляются автоматом. Это типичное поле. Далее я его описывать не буду.
  • edituser - Кто правил последний? За заполнение поля следит триггер. Заполнять вручную нет смысла. Данные подставляются автоматом. Это типичное поле. Далее я его описывать не буду.

Добро. Теперь пользователи.
CREATE TABLE IF NOT EXISTS `Logins` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`id_domain` int(10) unsigned NOT NULL,
`login` varchar(128) COLLATE utf8_general_ci NOT NULL,
`password` varchar(128) DEFAULT NULL,
`name` varchar(256) COLLATE utf8_general_ci NOT NULL,
`home` varchar(256) COLLATE utf8_general_ci DEFAULT NULL,
`quota` varchar(50) DEFAULT NULL,
`editdate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`edituser` varchar(128) COLLATE utf8_general_ci NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
UNIQUE KEY `UK_Logins_IddomainLogin` (`id_domain`,`login`),
CONSTRAINT `FK_Logins_Iddomain` FOREIGN KEY (`id_domain`) REFERENCES `Domains` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;

DELIMITER //
CREATE TRIGGER `tr_Logins_bi`
BEFORE INSERT
ON `Logins`
FOR EACH ROW
BEGIN
if New.password is null then
Set New.password = SHA2(New.login, 512);
elseif New.password = '' then
Set New.password = SHA2(New.login, 512);
else
Set New.password = SHA2(New.Password, 512);
end if;
Set New.edituser = User();
END//
DELIMITER ;

DELIMITER //
CREATE TRIGGER `tr_Logins_bu`
BEFORE UPDATE
ON `Logins`
FOR EACH ROW
BEGIN
if New.password is null then
Set New.password = SHA2(New.login, 512);
elseif New.password = '' then
Set New.password = SHA2(New.login, 512);
else
Set New.password = SHA2(New.Password, 512);
end if;
Set New.edituser = User();
END//
DELIMITER ;

Логика:
  • id_domain - Идентификатор домена. Внешний ключ.
  • login - Логин пользователя. Вкупе с идентификатором домена составляют уникальный ключ.
  • password - Пароль для входа. По логичным причинам, я буду использовать SHA2-512 метод шифрования (просто он необратим). Солить не вижу смысла. Если задан пустой, то берётся логин - за этим следит триггер.
  • name - Описание пользователя. Очень рекомендую ответственно заполнять! Есть ненулевой шанс, что вы просто не вспомните, что за пользователь такой "dom_priem_old".
  • home - Домашний каталог. По умолчанию, Null. Смысл раскроется в представлениях.
  • quota - Квота пользователя. Заранее создам и положу здесь.

А теперь насторожитесь! Список оснований и активности. Смысл в том, что у нас будет основание для "включения" записи, и её "выключения". То есть у нас будет история активности учётной записи.
CREATE TABLE IF NOT EXISTS `Reasons` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`reason` varchar(50) COLLATE utf8_general_ci NOT NULL,
`active` bit(1) NOT NULL,
`editdate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`edituser` varchar(128) COLLATE utf8_general_ci NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
UNIQUE KEY `UK_Reasons_Reason` (`reason`)
) ENGINE=InnoDB;

DELIMITER //
CREATE TRIGGER `tr_Reasons_bi`
BEFORE INSERT
ON `Reasons`
FOR EACH ROW BEGIN
Set New.edituser = User();
END//
DELIMITER ;

DELIMITER //
CREATE TRIGGER `tr_Reasons_bu`
BEFORE UPDATE
ON `Reasons`
FOR EACH ROW BEGIN
Set New.edituser = User();
END//
DELIMITER ;

Логика:
  • reason - Буковочками написать, что это за основание. Например, принят на работу. Уникальный ключ.
  • active - Непосредственно активность.

Теперь таблица статуса активности.
CREATE TABLE IF NOT EXISTS `Status` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`id_login` int(10) unsigned NOT NULL,
`id_reason` int(10) unsigned NOT NULL,
`date` datetime NOT NULL,
`editdate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`edituser` varchar(128) COLLATE utf8_general_ci NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
UNIQUE KEY `UK_Status_IdloginDate` (`id_login`,`date`),
KEY `FK_Status_Idreason` (`id_reason`),
CONSTRAINT `FK_Status_Idlogin` FOREIGN KEY (`id_login`) REFERENCES `Logins` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `FK_Status_Idreason` FOREIGN KEY (`id_reason`) REFERENCES `Reasons` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;

DELIMITER //
CREATE TRIGGER `tr_Status_bi`
BEFORE INSERT
ON `Status`
FOR EACH ROW BEGIN
Set New.edituser = User();
END//
DELIMITER ;

DELIMITER //
CREATE TRIGGER `tr_Status_bu`
BEFORE UPDATE
ON `Status`
FOR EACH ROW BEGIN
Set New.edituser = User();
END//
DELIMITER ;

Логика:
  • id_login - Идентификатор учётной записи
  • id_reason - Идентификатор основания изменения статуса
  • date - Дата-время начала действия

И последняя таблица. Алиасы.
CREATE TABLE IF NOT EXISTS `Aliases` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`id_input` int(10) unsigned DEFAULT NULL,
`address_input` varchar(256) COLLATE utf8_general_ci DEFAULT NULL,
`id_domain_input` INT(10) UNSIGNED NULL DEFAULT NULL,
`id_output` int(10) unsigned DEFAULT NULL,
`address_output` varchar(256) COLLATE utf8_general_ci DEFAULT NULL,
`id_reason` int(11) unsigned NOT NULL,
`date` datetime NOT NULL,
`editdate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`edituser` varchar(128) COLLATE utf8_general_ci NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `FK_Aliases_idinput` (`id_input`),
KEY `FK_Aliases_idoutput` (`id_output`),
KEY `FK_Aliases_idreason` (`id_reason`),
CONSTRAINT `FK_Aliases_idinput` FOREIGN KEY (`id_input`) REFERENCES `Logins` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `FK_Aliases_idoutput` FOREIGN KEY (`id_output`) REFERENCES `Logins` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `FK_Aliases_idreason` FOREIGN KEY (`id_reason`) REFERENCES `Reasons` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;

DELIMITER //
CREATE TRIGGER `tr_Aliases_bi`
BEFORE INSERT
ON `Aliases`
FOR EACH ROW BEGIN
Set New.edituser = User();
END//
DELIMITER ;

DELIMITER //
CREATE TRIGGER `tr_Aliases_bu`
BEFORE UPDATE
ON `Aliases`
FOR EACH ROW BEGIN
Set New.edituser = User();
END//
DELIMITER ;

Логика:
  • id_input - Идентификатор (нашего) пользователя. Забиваем, если нужно переправить письма с существующего аккаунта.
  • address_input - Имя (нашего виртуального) пользователя. Пишем абстрактный почтовый ящик. Например, имеем отдел сбыта. Чтобы не создавать непонятный ящик "sbyt" можно создать мнимый ящик "sbyt" и перенаправлять корреспонденцию с него всем заинтересованным необходимым пользователям
  • id_domain_input - Идентификатор домена. Изменять только при системе "мнимого" ящика.
  • id_output - Идентификатор (нашего) пользователя. Забиваем, если нужно перенаправлять на существующий аккаунт.
  • address_output - Имя (любого не нашего) пользователя. То есть с почты "sbyt" почту можно предать другому серверу. Например, mail.ru
  • id_reason - Основание изменения статуса. Снова =)
  • date - Дата-время начала действия

Так. С таблицами разобрались. Это только полдела. Для получения корректной информации прибегнем к представлениям.
От себя. В своё время завёл себе правило по возможности не брать данные напрямую из таблиц, и не давать на таблицы прямой доступ. Для доступа чаще всего использую работу с сохранёнными процедурами. Этот вариант подразумевает под собой клиент-серверное решение с самописным клиентом. Зато стабильность over 9000.
В данном случае лепить клиента не имеет смысла. А значит, отпадает смысл в сохранёнках - вся нагрузка легла на триггеры (в отличие от сохранёнок, по-моему, не попадают в статистику, а значит не влияют на производительность). А вот доступ к актуальным данным будет через представления

Домены:
CREATE VIEW `V_Domains` 
AS
select
`Domains`.`id` AS `id`,
`Domains`.`domain` AS `domain`
from `Domains`;

Create View `V_Reasons`
As
Select
`id` as `id`,
`active` as `active`
From `Reasons`

Тут ничего сложного. Просто немодифицированный вывод данных. Интересное будет дальше.
CREATE  VIEW `V_Status_Id` 
AS
select
`s`.`id_login` AS `id_login`,
max(`s`.`date`) AS `date`
from `Status` `s`
where `s`.`date` <= now()
group by `s`.`id_login`;

CREATE VIEW `V_Status`
AS
select
`s`.`id` AS `id`,
`s`.`id_login` AS `id_login`,
`s`.`id_reason` AS `id_reason`,
`s`.`date` AS `date`,
`r`.`active` AS `active`
from `Status` `s`
join `V_Status_Id` `i` on
(`s`.`id_login` = `i`.`id_login`) and
(`s`.`date` = `i`.`date`)
join `V_Reasons` `r` on
`s`.`id_reason` = `r`.`id`;

Логика:
  • В представлении "V_Status_ID". Так как в MySQL в представлении невозможно запихать вложенный запрос, ваяем свой, возвращающий последние актуальные записи статусов.
  • В представлении "V_Status" собираем эти данные воедино. В итоге получаем представление актуальных статусов.

Далее. Общая таблица логинов с вариациями:
CREATE VIEW `V_Logins` 
AS
select
`l`.`id` AS `id`,
lcase(`l`.`login`) AS `username`,
lcase(`d`.`domain`) AS `domain`,
concat(lcase(`l`.`login`),'@',lcase(`d`.`domain`)) AS `user`,
`l`.`password` AS `password`,
concat('maildir:',ifnull(`l`.`home`,concat('/mail/',lcase(`d`.`domain`),'/',lcase(`l`.`login`)))) AS `mail`,
ifnull(`l`.`home`,concat('/mail/',lcase(`d`.`domain`),'/',lcase(`l`.`login`))) AS `home`,
concat('*:storage=',`l`.`quota`) AS `quota`,
`s`.`active` AS `active`
from `V_Status` `s`
join `Logins` `l` on
`s`.`id_login` = `l`.`id`
join `V_Domains` `d` on
`l`.`id_domain` = `d`.`id`;

Логика:
  • Готовим поля для авторизации ("username", "domain", "user", "password") в Dovecot2.
  • Готовим поле, отображающее путь до папки пользователя ("mail", "home"). Если явно не задан путь (поле Logins.home), то берём из логина. Нужно, если сменится логин. Да, я знаю, что задавать поле можно явно. Мне просто так удобнее...
  • Готовим поле с квотами ("quota"). Если не указано явно, делаю 512 Мб (для меня это вполне приемлемо). Смотрите по ситуации. Имейте в виду, что при настроенном плагине quota и наличием 100 пользователей в системе все почтовые ящики будут "весить" 50 гигабайт.
  • Отметка активности. Мне удобнее, чтобы были отображены все данные. Потом в других запросах буду их фильтровать.

Хорошо. Также надо создать представления для авторизации:
CREATE VIEW `V_Users` 
AS
select
`V_Logins`.`id` AS `id`,
`V_Logins`.`user` AS `user`,
`V_Logins`.`domain` AS `domain`,
`V_Logins`.`username` AS `username`,
`V_Logins`.`mail` AS `mail`,
`V_Logins`.`home` AS `home`,
`V_Logins`.`quota` AS `quota_rule`,
5000 AS `uid`,
5000 AS `gid`
from `V_Logins`
where `V_Logins`.`active` = 1;

CREATE VIEW `V_Passwords`
AS
select
`V_Logins`.`id` AS `id`,
`V_Logins`.`user` AS `user`,
`V_Logins`.`domain` AS `domain`,
`V_Logins`.`username` AS `username`,
`V_Logins`.`password` AS `password`
from `V_Logins`
where `V_Logins`.`active` = 1;

Тут ничего сложного. Просто отфильтрованные по активности результаты представления "V_Logins". Почему я просто не делаю выборку из основного представления в конфигах Dovecot (зачем нагородил дополнительные представления)? Если честно, для масштабирования. На данный момент схема настроена так. Вдруг, завтра мне придётся изменять структуру базы? К тому же мне удобнее, когда всё по полочкам.
Остались алиасы.
CREATE VIEW `V_Aliases_all` 
AS
select
`a`.`id` AS `id`,
case when `a`.`id_input` is NULL then
concat(`a`.`address_input`,'@',`d`.`domain`)
else `l_i`.`username`
end AS `mail_input`,
case when `a`.`id_output` is NULL then
`a`.`address_output`
else
`l_i`.`username`
end AS `mail_output`,
`a`.`date` AS `date`,
`r`.`active` AS `active`
from `Aliases` `a`
left join `V_Logins` `l_i` on
`a`.`id_input` = `l_i`.`id`
left join `V_Domains` `d` on
`a`.`id_domain_input` = `d`.`id`
left join `V_Logins` `l_o` on
`a`.`id_output` = `l_o`.`id`
join `V_Reasons` `r` on
`a`.`id_reason` = `r`.`id`
where
((a.id_input is not null) or
((a.address_input is not null) and (a.id_domain_input is not null))) and
((a.id_output is not null) or
(a.address_output is not null));

CREATE VIEW `V_Aliases`
AS
select
`V_Aliases_all`.`id` AS `id`,
`V_Aliases_all`.`mail_input` AS `mail_input`,
`V_Aliases_all`.`mail_output` AS `mail_output`
from `V_Aliases_all`
where
(`V_Aliases_all`.`active` = 1) and
(`V_Aliases_all`.`date` <= now())
union
select distinct
NULL AS `id`,
`l`.`username` AS `mail_input`,
`l`.`username` AS `mail_output`
from `V_Logins` `l`
join `Aliases` `a` on
`l`.`id` = `a`.`id_input`
join `V_Reasons` `r` on
`a`.`id_reason` = `r`.`id`
where
(`r`.`active` = 1) and
(`a`.`date` <= now()) and
(`l`.`active` = 1);

Тут всё интереснее. В общем представлении (V_Aliases_all) формируем адресата и аресанта (конечно, правильно заполненные). В результирующем (V_Aliases) - только активные данные. Плюс алиас на самого себя, если адресант активен. Дело в том, что если вы настроите перенаправление с "Васи Пупкина" на "Ивана Иванова", то "Вася Пупкин" прекратит получать почту. Во избежание этого недоразумения и написана вторая часть представления.
Схема может меняться. Все изменения постараюсь оперативно вносить и сюда.
Отлично. Пора ставить MDA.
MDA. Dovecot 2

Не тянем кота за хвостЪ.
# cd /usr/ports/mail/dovecot2/
# make config-recursive

http://it.icmp.ru/postimages/8426/7632/full/dovecot2.jpeg

Отмечаем поддержку LDAP и MySQL
# make config-recursive

Нужно для конфигурирования LDAP, так как его поддержку только что отметили, и сборщик только что обнаружил данную зависимость.
# make install clean
# rehash

Копируем конфиги по умолчанию
cp -R /usr/local/etc/dovecot/example-config/* /usr/local/etc/dovecot

Начинаем править конфиги. Привожу основные, без которых не заведётся.
/usr/local/etc/dovecot/dovecot.conf
  1. Заменяем
    #!include_try local.conf
    на
    #!include_try local.conf
    Мы не будем заниматься доставкой локальным пользователям.

/usr/local/etc/dovecot/conf.d/10-auth.conf
  1. disable_plaintext_auth = no
    Вынужденная мера. В интранете нет смысла шифроваться плюс авторизация из-под LDAP может кривить. Пришлось включить =(
  2. auth_realms - я записал свои обслуживаемые домены
  3. auth_default_realm - я записал свой основной домен. Как написано в описании, именно этот домен будет иметься в виду, если доменная часть отсутствует.
  4. auth_username_chars - раскомментировать.
  5. auth_username_format - раскомментировать. Приводит все логины к нижнему регистру. Исключает накопления дубликатов папок, различных лишь регистром (Admin, admin, aDmin и ADMIN разные папки!)
  6. auth_mechanisms = plain login - это магия. Нужна для корректной авторизации некоторых клиентов.
  7. !include auth-system.conf.ext - комментируем.
  8. Снизу дописываем
    !include auth-ldap.conf.ext
    !include auth-sql.conf.ext

    То есть сначала смотрим в контроллер, а затем в базу

/usr/local/etc/dovecot/conf.d/10-logging.conf Как я погляжу, ведение полных логов иногда оправданно - следить кто, что, куда и зачем...
  1. log_path = /var/log/dovecot/dovecot.log
  2. info_log_path = /var/log/dovecot/info.log
  3. debug_log_path = /var/log/dovecot/debug.log
  4. auth_verbose = yes
  5. auth_verbose_passwords = sha1
  6. auth debug = yes
  7. auth_debug_passwords = yes
  8. mail_debug = yes
  9. Раскомментировать
    log_timestamp
    login_log_format_elements
    login_log_format
    login_log_prefix
  10. deliver_lo_format = msgid=%m subject=%s from=%f: %$

/usr/local/etc/dovecot/conf.d/10-mail.conf
  1. mail_location = maildir:/mail/%Ld/%Ln
  2. mail_uid = 5000
  3. mail_gid = 5000
  4. mail_temp_dir = /var/tmp
  5. first_valid_uid = 5000
  6. first_valid_gid = 5000

/usr/local/etc/dovecot/conf.d/10-ssl.conf
  1. ssl=no
  2. Комментируем
    ssl_cert
    ssl_key

/usr/local/etc/dovecot/dovecot-sql.conf.ext
  1. driver = mysql
  2. connect = host=localhost dbname=mail user=mail password=mail
  3. default_pass_scheme = SHA512
  4. password_query = Select `user`, `domain`, `username`, `password` From `V_Passwords` Where `user` = '%u'
  5. user_query = Select `user`, `domain`, `username`, `mail`, `home`, `uid`, `gid` From `V_Users` Where `user` = '%u'

/usr/local/etc/dovecot/dovecot-ldap.conf.ext
  1. hosts = <host>:3268 - указать либо DNS имя, либо ip адрес вашего контроллера домена.
  2. dn = <User>@<Domain> - указать пользователя, от чьего имени выполнять поиск а также домен. Например, Administrator@SOME.DOMAIN
  3. dnpass = <password>] - пароль от учётной записи. см. пункт "dn"
  4. tls=no
  5. debug_level = 2 - потом можно выключить (поставить 0)
  6. auth_bind = yes
  7. base = dc=some,dc=domain - указать, где именно искать. Пример для some.domain
  8. deref = searching
  9. scope = subtree
  10. user_filter = (&(mail=%u)(userAccountControl=512))
  11. pass_filter = (&(mail=%u)(userAccountControl=512))
  12. default_pass_scheme = CRYPT

Вроде, всё...
Надо создать пользователя и папку.
# pw groupadd vmail -g 5000
# pw useradd vmail -u 5000 -g vmail -s /usr/sbin/nologin
# mkdir /mail
# chown vmail:vmail /mail/
# mkdir /var/log/dovecot

И создать пользователя в MySQL
mysql> Grant Select, Show View on `mail`.* to mail@'localhost' identified by 'mail';
mysql> flush privileges;
mysql> quit;

Теперь самое главное: Заносим первоначальные данные в базу, а потом запускаем dovecot:
# sysrc dovecot_enable=YES
# service dovecot start

Проверка Dovecot.

Проверить работоспособность сервера можно достаточно просто. Есть отличный инструмент для работы по сети - telnet. Желательно проверять на другом компьютере. Если telnet не установлен, то вот инструкция по установке на windows и вот инструкция для линукса. Проверяем:
# telnet <mailserver> pop3
Trying <mailserver>...
Connected to <mailserver>.
Escape character is '^]'.
+OK <greetings>
user <login-mysql>
+OK
pass <pass-mysql>
+OK Logged in.
user <login-ldap>
+OK
pass <pass-ldap>
+OK Logged in.
user blablalba
+OK
pass 123123
-ERR [SYS/TEMP] Temporary authentication failure. [<mailserver>:<currenttimestamp>]
quit
+OK Logging out

где
  • <mailserver> - DNS имя или ip адрес почтового сервера сервера
  • <greetings> - приветствие, заданное в /usr/local/etc/dovecot/dovecot.conf параметр login_greeting
  • <login-mysql> и <pass-mysql> - это учётная записать, заданная в базе данных MySQL (таблица Logins). Не забудьте добавить в таблицу статусов основание включения записи!
  • <login-mysql> и <pass-mysql> - это учётная записать, заданная в поле <почта> учётной записи контроллера домена. Убедитесь, что учётная запись активная!
  • <currenttimestamp> - текущая дата-время

Если появятся ошибки, то внимательно изучайте логи (/vat/log/dovecot/*.log).
Настройка Postfix

Если ошибок не было, то приступаем к установке postfix
# cd /usr/ports/mail/postfix
# make config-recursive

http://it.icmp.ru/postimages/8426/7632/full/postfix.jpeg

Выбираем поддержку LDAP и MySQL. Также Включаем поддержку авторизации через Dovecot 2.
# make install clean
# Would you like to activate Postfix in /etc/mail/mailer.conf [n]? y
# rehash

Следуя рекомендациям установщика, отключаем sendmail
# sysrc sendmail_enable=NO
# sysrc sendmail_submit_enable=NO
# sysrc sendmail_outbound_enable=NO
# sysrc sendmail_msp_queue_enable=NO

Также отключаем ежедневные задачи sendmail. Тут есть нюанс. Стандартный установщик указывает на файл /etc/periodic.conf. На самом деле, фай находится в другом месте: /etc/defaults/periodic.conf. Правим его:
# sysrc -f /etc/defaults/periodic.conf daily_clean_hoststat_enable=NO
# sysrc -f /etc/defaults/periodic.conf daily_status_mail_rejects_enable=NO
# sysrc -f /etc/defaults/periodic.conf daily_status_include_submit_mailq=NO
# sysrc -f /etc/defaults/periodic.conf daily_submit_queuerun=NO

Вот моя конфигурация по умолчанию, разбавленная комментариями. Скачать, убрать расширение файла, открыть, курить =)). Нужно заменить 2 параметра (к сожаления, не помню какие) со значением $ERROR.
Начинаем править:
# fetch http://it.icmp.ru/postimages/8426/7632/full/main.cf.new.orig.jpg -o /tmp/main.cf
# mv /usr/local/etc/postfix/main.cf /usr/local/etc/postfix/main.cf.orig
# mv /tmp/main.cf /usr/local/etc/postfix/main.cf
# postconf -e alias_maps=hash:/etc/mail/aliases
# postconf -e mail_release_date= Подставить значение из /usr/local/etc/postfix/main.cf.default | grep mail_release_date
# postconf -e mail_version= Подставить значение из /usr/local/etc/postfix/main.cf.default | grep mail_version
# postconf -e mydestination=$myhostname,localhost.$mydomain,localhost,$mydomain
# postconf -e mydomain= Написать имя сервера
# postconf -e myhostname= Написать имя почтового домена, заявленного в mx записи DNS сервера. Если не знаете, что это, то, возможно, не стоило браться за почтовый сервер?
# postconf -e myorigin=$mydomain
# postconf -e virtual_alias_maps=ldap:/usr/local/postfix/ldap/virtual_alias_maps.conf,mysql:/usr/local/postfix/sql/virtual_alias_maps.conf,$virtual_maps
# postconf -e virtual_mailbox_domains=mysql:/usr/local/etc/postfix/sql/virtual_mailbox_domains.conf
# postconf -e virtual_mailbox_maps=mysql:/usr/local/etc/postfix/sql/virtual_mailbox_maps.conf,ldap:/usr/local/etc/postfix/ldap/virtual_mailbox_maps.conf
# postconf -e virtual_transport=dovecot
# postconf -e smtpd_sasl_auth_enable=yes
# postconf -e smtpd_sasl_path=/var/spool/postfix/private/auth
# postconf -e mynetworks_style=host
# postconf -e smtpd_relay_restrictions=permit_mynetworks,permit_sasl_authenticated,reject_unauth_destination

Настроим перенаправление почты, предназначенную руту определённому пользователю (может и не существовать!)
# echo "root: <usermail>": >> /etc/mail/aliases
# postalias /etc/mail/aliases

Дальше подсовываем конфигурации к MySQL и LDAP:
  • mysql. virtual alias maps
    # mkdir /usr/local/etc/postfix/sql
    #cat << DELIMITER > /usr/local/etc/postfix/sql/virtual_alias_maps.conf
    user = mail
    password = mail
    hosts = localhost
    dbname = mail
    table = V_Aliases
    select_field = mail_output
    where_field = mail_input
    DELIMITER
  • mysql. virtual mailbox domains
    # cat << DELIMITER > /usr/local/etc/postfix/sql/virtual_maibox_domains.conf
    user = mail
    password = mail
    hosts = localhost
    dbname = mail
    table = V_Domains
    select_field = domain
    where_field = domain
    DELIMITER
  • mysql. virtual mailbox maps
    # cat << DELIMITER > /usr/local/etc/postfix/sql/virtual_mailbox_maps.conf
    user = mail
    password = mail
    hosts = localhost
    dbname = mail
    table = V_Users
    select_field = username
    where_field = user
    DELIMITER
  • ldap. virtual alias maps
    # mkdir /usr/local/etc/postfix/ldap
    # cat << DELIMITER > /usr/local/etc/postfix/ldap/virtual_alias_maps.conf
    server_host = <Domain>
    server_port = 3268
    bind_dn = <user ldap>
    bind_pw = <pass ldap>
    search_base = dc=some,dc=domain
    query_filter = (&(mail=%s)(userAccountControl=512))
    result_attribute = mail
    #scope = subtree
    debuglevel = 0
    cache = no
  • ldap. virtual mailbox maps
    # cat << DELIMITER > /usr/local/etc/postfix/ldap/virtual_mailbox_maps.conf
    server_host = <Domain>
    server_port = 3268
    bind_dn = <user ldap>
    bind_pw = <pass ldap>
    search_base = dc=some,dc=domain
    query_filter = (&(mail=%s)(userAccountControl=512))
    result_format = /mail/%d/%u
    result_attribute = mail
    #scope = subtree
    debuglevel = 0
    cache = no

Добавим строчку в /usr/local/etc/postfix/master.cf:
dovecot   unix  -       n       n       -       -       pipe
flags=DRhu user=vmail:vmail argv=/usr/local/libexec/dovecot/dovecot-lda -f${sender} -d ${recipient}

Далее поправим настройки Dovecot.
Измените секцию service auth в файле /usr/local/etc/dovecot/conf.d/10-master.conf на следующее:
service auth {
# auth_socket_path points to this userdb socket by default. It's typically
# used by dovecot-lda, doveadm, possibly imap process, etc. Users that have
# full permissions to this socket are able to get a list of all usernames and
# get the results of everyone's userdb lookups.
#
# The default 0666 mode allows anyone to connect to the socket, but the
# userdb lookups will succeed only if the userdb returns an "uid" field that
# matches the caller process's UID. Also if caller's uid or gid matches the
# socket's uid or gid the lookup succeeds. Anything else causes a failure.
#
# To give the caller full permissions to lookup all users, set the mode to
# something else than 0666 and Dovecot lets the kernel enforce the
# permissions (e.g. 0777 allows everyone full permissions).
unix_listener auth-userdb {
mode = 0666
user = vmail
group = vmail
}

unix_listener auth-master {
mode = 0666
user = vmail
group = vmail
}

# Postfix smtp-auth
unix_listener /var/spool/postfix/private/auth {
mode = 0666
user = postfix
group = postfix
}

# Auth process is run as this user.
#user = $default_internal_user
}

И, наконец стартуем Postfix
# sysrc postfix_enable=YES
# service dovecot restart
# service sendmail stop
# service postfix start


Здесь представлены основные настройки. Дальнейшая тонкая настройка будет рассмотрена в следующей статье.

UPD#1
Чтобы postfix правильно работал с dovecot поправим чутка:
postconf dovecot_destination_recipient_limit=1
postconf mailbox_command=/usr/local/libexec/dovecot/deliver
chown -R vmail:dovecot /var/log/dovecot/*

Без этого у меня были проблемы =(
  • просмотров: ~1506
  • рейтинг: ?

Комментарии (0)

Вы - anonymous, войти ?

можно использовать bbcode-теги
[b]жирный текст[/b]
[i]курсив[/i]
[u]underline[/u]
[s]зачеркнутый текст[/s]
[size=20px]размер шрифта[/size]
всякие изменения текста
[left][/left]
[right][/right]
[center][/center]
позиционирование элементов: картинки, текст и т.д
[url][/url]
[email][/email]
внутри тега [url] помещайте ссылки, а внутри [email] адрес электронной почты;
так же [url] можно использовать в виде:
[url=http://example.com]пример[/url],
[url=http://test.ru][img]http://flickr.com/givemeimg.png[/img][/url]
[code][/code]
[quote][/quote]
внутри тега [code] можно помещать программный код (подстветка попытается включиться автоматически); для выделения цитат используйте [quote]
также можно напрямую указать язык [code=cpp]int i;[/code]
[list][/list]
создаем списки, каждый элемент пишется после [*].

Можно указывать маркер - [list=marker].
возможные маркеры 1(decimal), i(lower-roman), I(upper-roman), a(lower-alpha), A(upper-alpha). Примеры:

[list][*]1 элемент[*]2 элемент[*]3 элемент[/list]
[list=1][*]1 элемент[*]2 элемент[*]3 элемент[/list]
[list=A][*]1 элемент[*]2 элемент[*]3 элемент[/list]
[table][/table]
оформляем таблицу, используя внутренние теги [tr] и [td].
[tr] - строка, [td] - поле в строке,
[table=100%] - можно задавать ширину в процентах, по-умолчанию ширина 100%
[td=2] - можно задавать сколько столбцов входит в это поле. Пример:

[table=50%][tr][td]столбец 1[/td][td]столбец 2[/td][/tr][tr][td]значение 1[/td][td]значение 2[/td][/tr][tr][td=2]сразу 2 столбца[/td][/tr][/table]
[img][/img]
тег для вставки фото или картинок, мы любим картинки. Примеры использования:

[img]http://ya.ru/logo.png[/img],
[img=100x100px]http://ya.ru/logo.png[/img]
[img=fullimg.url]thumbimg.url[/img],
Пожалуйста загружайте картинки на наш сайт, либо вставляйте с бекбоновских ресурсов.
[video][/video]
Проигрывает видео, внутрь вставляем ссылки на видео, поддерживается Play.Ykt.Ru(нужно вставить ссылку на страницу с видео) и tv.ykt.ru(нужно вставить ссылку на адрес файла)