Содержание
Часто нет возможности отказаться от использования домена Windows с AD. Например из-за групповых политик. Но при этом нет никакой необходимости держать громоздкий негибкий и по слухам небозопастный Exchange для обработки почты. Что ж - выход есть, простой и элегантный. Вам всего лишь нужно организовать почтовый сервер на базе Linux, который брал бы всю информацию о пользователях и их почтовых ящиках из Active Directory.
Сервер на Postfix и Dovecot
В качестве программного обеспечения для нашего сервера мы будем использовать Postfix в связке с Dovecot. Как настроить полнофункциональную почтовую систему на базе этих двух приложений подробно описано в соответствующих статьях про Postfix и Dovecot. Здесь же я акцентирую внимание на интеграцию почтовой системы с сервером AD.
Итак. В Postfix все ваши домены будут обрабатываться через механизм виртуальных доменов. В AD информация о почтовых ящиках будет храниться в поле proxyAddresses, которое в отсутствии Exchange пустует. Postfix не будет для каждого письма опрашивать AD на предмет наличия пользователя и соответствия имени пользователя адресу электронной почты. Вместо этого раз в час Postfix будет импортировать данные обо всех пользователях из AD, что позволит избежать сразу многих неудобств и значительно упростить дальнейшее управление доменом. Dovecot же напротив будет опрашивать домен на предмет авторизации.
Логин и пароль пользователя для доступа к Postfix и к Dovecot будут совпадать с доменными логином и паролем.
Итак, все подробности по мере описания настройки.
Настройка AD
Вам потребуется пользователь в AD, через которого Postfix и Dovecot будут получать информацию из дерева каталогов. Я назвал этого пользователя ldap-user
, вы же можете выбрать любое другое имя. Кстати, в целях безопастности лучше запретите созданному пользователю вход на все компьютеры.
К сожалению в отсутствии Exchange через окно редактирования свойст пользователя нельзя изменить значение поля proxyAddresses
. Поэтому для назначение почтовых ящиков пользователям вам понадобится Редактор ADSI
, хотя конечно вы можете добавить необходимые поля редактирования в редактор свойств вручную, но делается это далеко не тривиально.
В любом случае особенности настройки AD - не тема этой статьи. Для работы системы вам потребуется пользователь для доступа к AD, а за всей другой иформацией обращайтесь в справку Microsoft или на специализированные ресурсы.
Настройка Postfix
Postfix настроим следующим образом:
- Он будет принимать почту для всех пользователей вашего Windows-домена, имеющих почтовые ящики.
- У каждого пользователя будет один ящик, на который будет приходить почта со всех его адресов.
- Кроме того Postfix будет автоматически рассылать почту для групповых1) ящиков на ящики пользователей.
- Пользователи смогут отсылать через Postfix письма только с тех ящиков, которые им принадлежат - напрямую или через группы.
- Авторизация и локальная доставка почты будет обеспечиваться Dovecot.
Итак, подробно о параметрах Postfix можно почитать в соответствующей статье, тут же я опишу лишь те настройки, которые необходимы для решения поставленной задачи.
Сразу хочу обратить внимание: вам необходимо настроить SASL авторизацию и TLS шифрование для Postfix, поскольку мы будем позволять отсылать письма только зарегистрировавшимся пользователям, а механизм авторизации у нас будет PLAIN LOGIN2).
Авторизацию будет проводить Dovecot, поэтому в файле main.cf
основных настроек Postfix у вас должны быть примерно следующие параметры:
smtpd_sasl_auth_enable = yes smtpd_sasl_type = dovecot smtpd_sasl_path = private/auth-client smtpd_sasl_security_options = noanonymous, noplaintext smtpd_sasl_tls_security_options = noanonymous # Можно так вместо предыдущих двух опций #smtpd_tls_auth_only = yes
Подробней об авторизации и о связывании Dovecot и Postfix можно почитать в соответствующих статьях. Теперь же о содержательных настройках, касающихся логики функционирования нашей почтовой системы.
Для начала вам потребуется задать основной почтовый домен для вашей организации в параметре $myorigin, без этого работать ничего не будет. Вот примерно как должны выглядеть основные настройки адресов в Postfix:
myhostname = mail.example.com mydestination = $myhostname myorigin = $mydomain mynetworks = 127.0.0.0/8
Теперь нам нужно настроить собственно домены, для которых мы будем получать почту, а так же список разрешённых получателей и отправителей. Для этого нам потребуются три параметра, начинающихся на virtual_
:
virtual_mailbox_domains = $mydomain example.ru virtual_mailbox_maps = hash:/etc/postfix/local_mailboxes/valid_users virtual_alias_maps = hash:/etc/postfix/local_mailboxes/aliases
Назначение первого, я надеюсь, понятно. В нём содержится список всех доменов, для которых будет принимать почту Postfix. В этом списке обязательно должен быть $mydomain, иначе работать ничего не будет.
Второй парамерт указывает на карту, в которой содержится список пользователей AD, имеющих почтовые ящики. Он нужен для того, чтобы сообщить Postfix для каких пользователей он должен получать почту, а для каких - нет. Сам список должен состоять из имён аккаунтов (без домена!) пользователей, но поскольку карты Postfix должны обязательно содержать два поля на запись (см. статью про Postfix), то файл мог бы выглядеть так:
user1 OK user2 OK admin OK
Вместо OK можно написать что угодно. Эта карта будет автоматически создаваться скриптом, запрашивающим данные из AD (см. ниже).
Третий параметр, $virtual_alias_maps, отвечает за установление соответствия имён почтовых ящиков пользовательским аккаунтам. Он мог бы выглядеть так:
user1@example.com user1 user1@example.com user1 user2@example.com user2 abuse@example.com user1, user2, admin
Формат файла я думаю понятен. В левой колонке адрес, а в правой - все его владельцы. Если владельцев много - значит ящик принадлежит группе пользователей в AD. Этот файл естественно тоже будет создаваться скриптом.
Ну и наконец нам нужно настроить локальную доставку. Для этого, как я уже говорил, будет использоваться Dovecot. Поэтому все параметры доставки мы будет настраивать именно в нём, а Postfix лишь должен правильно пересылать письма. Для решения этой задачи в main.cf
нужно указать транспорт на Dovecot:
dovecot_destination_recipient_limit = 1 virtual_transport = dovecot
Ну и собственно описать сам транспорт в файле master.cf
:
dovecot unix - n n - - pipe flags=DRhu user=postfix-vuser:postfix-vuser argv=/usr/lib/dovecot/deliver -f ${sender} -d ${user}
Обратите внимание, что в параматре -d
я указал ${user}
, то есть в Dovecot будет отправлено только имя пользователя без домена. Тут надо обратить внимание на одну особенность: Postfix не умеет оперировать именами пользователей, он работает только с полными адресами почты. Поэтому если указывать в описанных выше картах Postfix только имя пользователя (как мы и делали), то к нему будет автоматически добавлен домен из $myorigin. Это не доставляет никаких неудобств, потому что мы-то оперируем только именами пользователей во всех конфигах, но об этом нужно помнить, в частности, при отправке писем на Dovecot.
Итак, доставку писем пользователям мы организовали. Теперь нужно проконтролировать отправку. Тут всё проще. Поскольку наш Postfix использует SASL авторизацию, то мы просто можем ему сообщить, каким адресам отправителя соответствует какой аккаунт. Делается это с помощью аналогичной по структуре описанному выше файлу alias карты. Собственно и содержание должно быть тоже аналогично, поэтому мы будем использовать ту же карту.
Для привязки SASL логинов к адресам отправителей служит следующий парамерт в файле main.cf
:
smtpd_sender_login_maps = hash:/etc/postfix/local_mailboxes/aliases
Однако указание этого параметра не заставит Postfix отклонять почту при несоответствии логина и адреса отправителя. Нужно ещё правильно расставить запреты в параметрах smtpd_*_restrictions
. Например это можно сделать так:
smtpd_recipient_restrictions = ... permit_mynetworks permit_sasl_authenticated reject_unauth_destination ... smtpd_sender_restrictions = ... reject_authenticated_sender_login_mismatch permit_mynetworks permit_sasl_authenticated ...
Обратите внимание на ограничение reject_authenticated_sender_login_mismatch
. Оно отклоняет все письма от авторизованных пользователей, если адрес отправителя не соответствует логину в параметре $smtpd_sender_login_maps.
Собственно на этом настройка самого Postfix закончена. Теперь нужно лишь разжиться скриптом, который будет создавать файлы alias_maps и mailbox_maps по данным из AD.
Скрипт генерации карт Postfix по данным из AD
Скрипт написан на Perl и требует модуля Net::LDAP и Perl версии 5.104). Что делает скрипт:
- Он опрашивает AD и выбирает всех пользователей, у которых есть записи в поле proxyAddresses.
- Все эти пользователи добавляются в mailbox_maps, и все их ящики прописываются в alias_maps.
- Дальше скрипт опрашивает AD и выбирает все группы, для которых задан парамерт proxyAddresses или5) параметр mail.
- Для каждой полученной группы скрипт вычисляет всех пользователей, входящих в эту группу напрямую или опосредованно (через другие группы), и добавляет все необходимые псевдонимы в alias_maps. Кроме того скрипт так же добавляет пользователей, у которых нет своего ящика, но на которых ссылается ящик группы, в mailbox_maps. Таким образом все входящие в группу с прописанным почтовым ящиком пользвоатели будут получать коррекпонденцию для этой группы и смогут отправлять ответы.
- Кроме того скрипт попутно выявляет дубликаты адресов (когда один адрес почты задан для нескольких объектов) и адреса, не соответствующие домену. В первом случае обрабатывается только первый найденный объект с указанным адресом, а во втором просто выдаётся предупреждение, но адрес тем не менее обрабатывается.
Собственно сам скрипт:
#!/usr/bin/perl # Автор: Неворотин Вадим (malamut@ubuntu.ru) # Лицензия: GPLv3 use 5.010; use Net::LDAP; # ## Параметры # $AD_IP = 'ldap://192.168.0.1'; # AD сервер. # AD пользователь для коннекта. Указывать в большинстве случаев надо именно с именем Windows-домена $AD_user = 'ldap@domain.com'; $AD_passwd = 'Password'; # Пароль $AD_base = 'OU=var,DC=domain,DC=com'; # Исходная точка поиска в дереве AD # Разрешённые домены - для проверки адресов @domains = ('example.com','example.ru'); # Выходные файлы $aliases_file = '/etc/postfix/local_mailboxes/aliases'; $users_file = '/etc/postfix/local_mailboxes/valid_users'; # Хеш псевдонимов. Ключ - адрес почты, значение - список пользователей через запятую %aliases = (); # Список пользователей, имеющих почтовые ящики @valid_users = (); # Соединяемся с сервером... $ldap = Net::LDAP->new($AD_IP) or die "Couldn't connect to $AD_IP!\n"; $msg = $ldap->bind($AD_user, password => $AD_passwd); $msg->code && die "Connection problem...\n"; # Открываем файл псевдонимов open ALIASES, ">$aliases_file" or die "Can't open $alaises_file\n"; # ## Берём всех пользователей и все группы # # Процедура проверяет свой аргумент (адрес почты) на соответствие разрешённым доменам. sub check_domain { @_ == 1 or die "check_domain - OOPS!\n"; my $flag = 0; foreach $domain (@domains) { if ($_[0] =~ /@($domain)/) { $flag = 1; } } if (!$flag) { say "WARNING: incorrect domain in $_[0]"; } } # Процедура проверяет вхождение первого элемента (адрес) в качестве ключа для хеша (третий аргумент) # Второй аргумент - это предполагаемое значение ключа (имя пользователя или группы) sub check_mail { @_ > 1 or die "check_mail - OOPS!\n"; $mail = shift @_; $obj = shift @_; %hash = @_; if (exists($hash{$mail})) { say "WARNING: address $mail is used more than for one object:"; say "\t",$hash{$mail}; say "\t",$obj; say "I'll add it only for '$hash{$mail}'"; return 1; } return 0; } # ## Пользователи # # emails - из proxyAddresses, login - из sAMAccountName # Параметры поиска в AD для пользователей $filter = '(&(objectClass=user)(proxyAddresses=*))'; $attrs = ['CN','sAMAccountName','proxyAddresses']; # LDAP запрос $result = $ldap->search( base => $AD_base, filter => $filter, attrs => $attrs); # Выделяем необходимую инфу из результатов foreach $entry ($result->entries) { @addrs = $entry->get_value('proxyAddresses'); $obj = $entry->get_value('sAMAccountName'); foreach $mail (@addrs) { check_domain($mail); unless (check_mail($mail,$obj,%aliases)) { $aliases{$mail} = $obj; say ALIASES "$mail\t$obj"; } } } @valid_users = values %aliases; # ## Группы # # Проверяет вхождение элемента в массив. Короче - оператор in))) sub check_element { @_ > 1 or die "check_element - OOPS!\n"; $element = shift @_; @arr = @_; foreach (@arr) { if ($_ eq $element) { return 1 } } return 0; } # Удаляет из массива повторяющиеся элементы sub delete_duplicates { my $el = undef; my @result; foreach (sort @_) { if ($_ ne $el) { push @result, $_ } $el = $_; } return @result; } # Рекурсивно выбиряет по предоставленному DN всех пользователей. # Нужно для выборки по DN группы всех входящих в неё пользвоателей sub get_users { @_ == 1 or die "get_users - OOPS!\n"; my $base = $_[0]; my $result = $ldap->search( base => $base, scope => "base", filter => "objectClass=*" ); my $ent = $result->entry(0); my @users = (); if ( check_element("user", $ent->get_value("objectClass")) ) { push @users, $ent->get_value("sAMAccountName"); } elsif ( check_element("group", $ent->get_value("objectClass")) ) { foreach my $member ($ent->get_value("member")) { push @users, get_users($member); } } return @users; } # Параметры поиска для групп $filter = '(&(objectClass=group)(|(proxyAddresses=*)(mail=*)))'; $attrs = ['member','proxyAddresses','mail','sAMAccountName']; # LDAP запрос $result = $ldap->search( base => $AD_base, filter => $filter, attrs => $attrs); foreach $entry ($result->entries) { @mails = (); push @mails, $entry->get_value("mail"), $entry->get_value("proxyAddresses"); $obj = $entry->get_value('sAMAccountName'); @users = (); foreach $member ($entry->get_value("member")) { push @users, get_users($member); } @users = delete_duplicates(@users); push @valid_users, @users; if (! @users) { say "WARNING: empty group ",$entry->dn; next; } foreach $mail (@mails) { check_domain($mail); unless (check_mail($mail,$obj,%aliases)) { $aliases{$mail} = $obj; say ALIASES "$mail\t", join(", ",@users); } } } # Открываем файл пользователей и пишем туда всех пользователей с почтовыми ящиками open USERS, ">$users_file" or die "Can't open $users_file\n"; @valid_users = delete_duplicates(@valid_users); foreach (@valid_users) { say USERS "$_\tdovecot"; } close USERS; # Close aliases file close ALIASES; # Close connection to LDAP $msg = $ldap->unbind; `postmap hash:$aliases_file`; `postmap hash:$users_file`;
Поменяйте в скрипте необходимые параметры (все они собраны в начале) и поставьте его выполняться через cron
. В принципе, Postfix трогать больше не потребуется.
Настройка Dovecot
При настройке Dovecot в первую очередь нужно не забыть выставить сокет авторизации для Postfix, как это делать описано в статье про Dovecot.
Теперь нужно указать механизмы авторизации. Мы будем использовать только PLAIN LOGIN, то есть механизмы с открытой передачей пароля. Правда при этом потребуем обязательного TLS шифрования, что обезопасит пользователей от кражи их аккаунтов. Некоторые говорят, что использование PLAIN небезопастно, однако тут надо учесть один немаловажный факт: при использовании PLAIN авторизации вы можете хранить пароли в виде хешей, а при использовании «безопасных» методов вам придётся хранить все данные о пользователях открытым текстом. При этом PLAIN авторизацию можно обернуть в TLS шифрование, что обезопасит этот механизм, а гарантированно защитить базу паролей на сервере вы не сможете. В общем и целом - всегда используйте PLAIN с TLS шифрованием и храните все пароли в виде хешей.
Тем более нам надо соединяться с AD и предоставлять ему определять правильность пароля, так что нам в любом случае нужен PLAIN механизм авторизации.
Итак, в Dovecot авторизация должна быть настроена примерно следующим образом:
# Эта опция обязательна! Она требует TLS шифрования для PLAIN авторизации, без неё безопастность будет на 0 disable_plaintext_auth = yes # Включаем шифрование ssl_disable = no # Собственно описание механизма авторизации auth default { # Поддерживаемые типы. Тут не должно быть ничего кроме plain и login mechanisms = plain login # База паролей passdb ldap { args = /etc/dovecot/dovecot-ldap-passdb.conf } # База пользователей userdb ldap { args = /etc/dovecot/dovecot-ldap-userdb.conf } # Пользователь, имеющий доступ к ящикам и сокету авторизации user = postfix-vuser # Сокеты socket listen { master { path = /var/run/dovecot/auth-master mode = 0600 user = postfix-vuser group = postfix-vuser } client { path = /var/spool/postfix/private/auth-client mode = 0660 user = postfix group = postfix } } }
Подробнее можно почитать с статье про Dovecot. Нас тут интересует использование LDAP в качестве базы пользователей и паролей. Кстати, сразу хочу сказать, файл dovecot-ldap-userdb.conf должен быть симлинком на dovecot-ldap-passdb.conf. То есть фактически это один и тот же файл. Такое разделение связано с механизмами обращения к LDAP в Dovecot, подробней см. в документации.
Теперь содержимое dovecot-ldap-userdb.conf:
uris = ldap://192.168.0.1 # LDAP account dn = ldap@domain.com dnpass = Password # LDAP settings ldap_version = 3 base = ou=var, dc=domain, dc=com scope = subtree # Password check auth_bind = yes auth_bind_userdn = %n@domain.com pass_filter = (&(objectCategory=Person)(sAMAccountName=%n)) # Users check user_filter = (&(objectCategory=Person)(sAMAccountName=%n)) user_global_uid = 900 user_global_gid = 900
Дополнительные настройки
У приведенной выше конфигурации есть один недостаток. Дело в том, что Postfix предоставляет возможность SASL авторизации всем клиентам. Что открывает доступ к простому перебору паролей методом брутфорса. А так как пароли у пользвоателей такие же, как и от доменных аккаунтов, то всё это может быть весьма нехорошо.
Поэтому возможно неплохим решением было бы ограничить возможность SASL авторизации только в рамках внутренней сети. Но сразу скажу - Postfix так делать не умеет. То есть вне зависимости от своих настроек он будет предоставлять авторизацию всем. Но не надо отчаиваться, можно сделать так, чтобы в авторизации клиентам не из разрешённой сети отказывал Dovecot. Хотя тут есть некоторые трудности. Технически это осуществялется с помощью параметра allow_nets, который является необязательным полем passdb. И для IMAP и POP этот параметр прекрасно работает. Но Postfix до версии 2.7 не посылает в сокет авторизации Dovecot информацию о клиенте, поэтому Dovecot с установленным параметром allow_nets благополучно отказывает всем.
Мораль сего такая: если вы хотите ограничить возможность авторизации на сервере только в рамках вашей сети, то разумнее всего раздобыть Postfix 2.7, если в вашей системе более старая версия, вам придётся пересобрать Postfix из исходников, доступных на сайте.
say
, если вы понимаете что это такое.