howtoНемного интеграции адресной книги в ldap(для thunderbird) с postfixadmin

Здесь показан пример частичной совместной интеграции phpldapadmin(веб-админка для ldap) c postfixadmin(веб-админка для postfix).
Ниже не будет инструкций «от и до», как поднять адресную книгу в ldap, подразумевется, что у вас уже есть почтовый сервер на postfix с postfixadmin и адресная книга в ldap.

Все приведенные ниже срикпты подразумевают то, что на вашем почтовом сервере в качестве username используется email адрес полностью(ЭТО ВАЖНО). У меня postfixamdin настроен для хранения почты в каталогах domain/user (без домена в последней папке), по моим прикидкам на используемых полях mysql базы в скриптах эти настройки сказываться не должны, но проверьте сами если у вас по другому. Все скрипты и конфиги прикреплены в низу статьи одним архивом.
Не буду подробно описывать настройку ldap сервера для хранения адресной книги в нем, статей на эту тему полно, скажу лишь, что для него помимо основных схем(core.schema, cosine.schema, inetorgperson.schema)должна быть подчключена схема для thunderbird.
Корень каталога:
dn: dc=mail,dc=local
objectClass: dcObject
objectClass: organization
objectClass: top
dc: mail
o: mail
в котором будет лежать OU соответствующие почтовым доменам, в которых будут храниться записи адресной книги. Связующим звеном между mysql базой и ldap каталогом будет email, это единственный параметр который должен быть везде одинаковым, но для удобство в ldap в качестве rdn будет использоваться cn(«Отображаемое имя»), по этому в ldap каталоге атрибут mail нельзя будет редактировать.

В качестве веб-админки для адресной книги будет использоваться phpldapadmin, не лучшая админка для ldap, но у нее хорошие возможности для кастомизации. Конфиг хорошо откоментирован, не буду на нем останавливтаься, моя настроенная версия лежит в архиве в конце статьи в месте с модифицированным шаблоном, который надо кинуть в phpldapadmin-1.x.x.x/templates/modification. Нужно сказать, что из «админки» в итоге можно будет только отредактировать поля адресной книги определнной записи, при этом нельзя будет удалять сами записи, все удаление-добавляение будет осуществялться из postfixadmin.
Обратить внимание стоит на возможность выполнять свои функции после выполнения каких либо действий ( пример лежат в phpldapadmin-1.x.x.x/hooks/functions/examples.php)- этим мы и воспльзуемся. Положим в «phpldapadmin-1.x.x.x/hooks/functions/» файл любое_имя.php следующего содержания:
<?php
function my_post_entry_rename() {
        $args = func_get_args();
        exec('/usr/local/bin/mysql_rename '.$args[3]."\;".$args[4]);
        return true;
}
add_hook('post_entry_rename','my_post_entry_rename');
?>

В результате чего после переименования rdn(«Отображаемое имя») в ldap будет выполняться наш bash скрипт с параметрами Old DN и New RDN, с ";" в качестве разделителя между ними (для удообство «парсинга»)
mysql_rename:
#!/bin/bash
#$1- old dn $2- new rdn
mysql_user=""
mysql_pass=''
ldap_dn=""
ldap_pass=''
all_param="$@"
name="`echo $all_param|sed -n '/cn/p'|cut -d';' -f1|sed 's/cn=//'`"
ou="`echo $all_param|sed -n '/ou/p'|cut -d';' -f2|cut -d',' -f1`"
mail=`ldapsearch -x -D $ldap_dn -w $ldap_pass -b "$ou,dc=mail,dc=local" "(cn=$name)" mail -LLL|grep mail|sed -n '/mail:/p'|sed 's/mail: //'`
mysql -u $mysql_user --password=$mysql_pass postfix -sse "set names utf8 ; update mailbox set name=\"$name\" where username=\"$mail\""

в начале прописываеться учетная запись для доступа к mysql и ldap, правилом хорошего тона для mysql будет хранение логина и пароля в ~/.my.cnf, но у меня не получилось хранить эти данные для ldap в ~/.ldaprc, по этому было решено все хранить в скрипте.
Скрипт ищет email адрес в ldap в атрибуте mail «old dn», а затем меняет в mysql поле name в записи с username соответсвующим нашему email. Для учетной записи mysql проставлены права только на select username и update name в таблице mailbox(пример привести не могу- делал через графический клиент), для учетной записи в ldap достаточно только прав на чтение соответствующего подразделения. Важная особенность при работе с phpldapadmin: отображаемая информация обновляется не сразу(или я что-то перешаманил в настройках), чтобы видеть актуальную информацию нужно нажать Purge-caches->Home->Refresh. На этом мы больше не будем трогать phpldapadmin.

У postfixamdin тоже есть замечательная возможность- выполнение своих скриптов после ряда действий, этим-то мы и воспользуемся.
Нужно в конфиг добавить следующие строчки:
$CONF['domain_postcreation_script']='/usr/local/bin/ldap_add_domain';
$CONF['domain_postdeletion_script']='/usr/local/bin/ldap_del_domain';
$CONF['mailbox_postcreation_script']='/usr/local/bin/ldap_add_user';
$CONF['mailbox_postdeletion_script']='/usr/local/bin/ldap_del_user';
$CONF['mailbox_postedit_script']='/usr/local/bin/ldap_change_user';

Для всех сриптов ниже учетная запись ldap должна иметь права на запись в корень каталога, а учетной записи mysql достаточно прав на select полей username и name в таблице mailbox.

ldap_add_domain:
#!/bin/bash
#$1 -domain
ldap_dn=""
ldap_pass=''
ldif ()
{
echo "dn: ou=$1,dc=mail,dc=local"
echo "ou: $1"
echo "objectClass: organizationalUnit"
echo "objectClass: top"
}
ldif $1 > /tmp/domain_add.ldif
ldapadd -x -D $ldap_dn -w $ldap_pass -f /tmp/domain_add.ldif
Все просто — добавили домен — в корне ldap каталога появился одноименный OU

ldap_del_domain:
#!/bin/bash
#$1 domain 
ldap_dn=""
ldap_pass=''
ldapsearch -x -D $ldap_dn -w $ldap_pass -b "dc=mail,dc=local" "(ou=$1)" dn -LLL> /tmp/domain_del.ldif
sed -i '/^$/d' /tmp/domain_del.ldif
sed -i 's/dn: //' /tmp/domain_del.ldif
ldapdelete -r -x -D $ldap_dn -w $ldap_pass -f /tmp/domain_del.ldif
Аналогично- удалили домен- из ldap удалился соответствующий OU со всем(!) содержимым.

ldap_add_user:
#!/bin/bash
#$1 email $2 domain
ldap_dn=""
ldap_pass=''
mysql_user=""
mysql_pass=''
rm -f /tmp/create.ldif
cn=`mysql -u $mysql_user --password=$mysql_pass postfix -sse "set names utf8 ;select name from mailbox where username=\"$1\""`
#Уберем из cn левые символы
#cn=${cn//[^а-яА-Яa-zA-Z ]/} #некоретно работает с кирилицей 
#Проверка полных тесок-----------
cn_flag=`ldapsearch -x -D $ldap_dn -w $ldap_pass -b "ou=$2,dc=mail,dc=local" "(cn=$cn)" cn -LLL`
[ -n "$cn_flag" ]&& cn="$cn$RANDOM"
#--------------------------------
email=$1
domain=$2
if [ -z "$cn" ]
then
        cn="Новый Пользователь`date +%S`$RANDOM"
        gn="Новый"
        sn="Пользователь"
else
        gn="`echo $cn|awk '{print $2}'`"
        sn="`echo $cn|awk '{print $1}'`"
fi
[ -z "$gn" ] && gn="Имя"
ldif ()
{
echo "dn: cn=$1,ou=$2,dc=mail,dc=local"
echo "givenName: $3"
echo "sn: $4"
echo "cn: $1"
echo "mail: $5"
echo "objectClass: inetOrgPerson"
echo "objectClass: mozillaOrgPerson"
}
ldif "$cn" $domain "$gn" "$sn" $email > /tmp/create.ldif
ldapadd -x -D $ldap_dn -w $ldap_pass -f /tmp/create.ldif
Тут приходится из mysql брать «Имя» так, как оно не передается, далее идет проверка есть ли в текущем OU такой rdn ( возможна ситуация когда для разных email адресов в postfixadmin будут стоят одинаковые имена), если есть- то к нему добавляются случайные цифры, потом проверка заполнено ли поле «Имя» в postfixadmin, если нет- то будем «новым пользователем+ случайное число». При проверке тесок- конечно ерись написана, но насколько я понял у ldapsearch код возврата всегда «0» если запрос удался( даже если ничего не найдено)- по этому так проще.

ldap_del_user:
#!/bin/bash
#$1 email $2 domain
ldap_dn=""
ldap_pass=''
rm -f /tmp/delete.ldif
ldapsearch -x -D $ldap_dn -w $ldap_pass -b "dc=mail,dc=local" "(mail=$1)" dn -LLL> /tmp/delete.ldif
sed -i 's/^$/changetype: delete/g' /tmp/delete.ldif
ldapmodify -x -D $ldap_dn -w $ldap_pass -f /tmp/delete.ldif
Тут все просто — ищем в каталоге запись с соответствущим атрибутом mail и удаляем ее. Поиск от корня, потому, что некоторые записи могут лежать в «неродных» OU, у меня это сделано для сокрытия адресов учередителей и соответсвенно общая учетка для доступа к каталогу этот OU не видит.

ldap_change_user:
#!/bin/bash
#$1 email
ldap_dn=""
ldap_pass=''
mysql_user=""
mysql_pass=''
#------------------------
un64ldif () {
}
search () {
}
ldif () {
echo "dn: $1"
echo "replace: givenName"
echo "givenName: $2"
echo "-"
echo "replace: sn"
echo "sn: $3"
echo "-"
}
search $1|un64ldif > /tmp/search.ldif
cn=`mysql -u $mysql_user --password=$mysql_pass postfix -sse "set names utf8 ;select name from mailbox where username=\"$1\""`
dn_old="`cat /tmp/search.ldif|sed -n /^dn/p|sed 's/dn: //'`"
if [ -z "$cn" ]
then
        cn="Новый Пользователь`date +%S`$RANDOM"
        gn="Новый"
        sn="Пользователь"
else
        cn_old="`cat /tmp/search.ldif|sed -n /^cn/p|sed 's/cn: //'`"
        #Проверка на то что имя не изменилось
        [ "$cn" = "$cn_old" ]&& exit 0
        #Проверка полных тесок-----------
        cn_flag=`ldapsearch -x -D $ldap_dn -w $ldap_pass -b "dc=mail,dc=local" "(cn=$cn)" cn -LLL`
        [ -n "$cn_flag" ]&& cn="$cn$RANDOM"
        #--------------------------------
        gn="`echo $cn|awk '{print $2}'`"
        sn="`echo $cn|awk '{print $1}'`"
        [ -z "$gn" ] && gn="Имя"
fi
ldif "$dn_old" "$gn" "$sn"> /tmp/change.ldif
ldapmodify -x -D $ldap_dn -w $ldap_pass -f /tmp/change.ldif
echo "$dn_old">/tmp/change_rdn.ldif
echo "cn=$cn">>/tmp/change_rdn.ldif
ldapmodrdn -x -D $ldap_dn -w $ldap_pass -r -f /tmp/change_rdn.ldif
Самый хитрожопый скрипт, функция un64ldif () не моя (akkerman ничего не ответил по поводу ее публикации здесь), по этому приведена сылка на оригинал, я только добавил к команде base64 параметр -i, тоже самое с функцией search() (это решение N3 оформленоне в виде функции), но в архиве со скриптами фунции есть.
Тут мы ищем сначла в ldap(cn-«Отображаемое имя»), потом в mysql базе имя пользователя, если имя пустое, то будем «новым пользователем», если имя не изменилось- ничего не делаем, в противном случае- переименовываем.
У меня учетки для доступа к ldap хранятся в самом каталоге в отдельном ou, с закрытым доступом для всех (читаем только свое), по этому в ldapsearch используется параметр -x, также если у вас корень каталога не dc=mail,dc=local замените в скриптах на свой корень. Временные файлы используються для удобства отладки и по ним сразу видно «последнее действие»(и на каком то этапе у меня не получилось редактировать каталог без временного файла- по этому решил использовать везде)).

Обращаю внимание, что в скриптах не фильтруются входные данные, из посторонних лиц доступ в phpldapadmin можно дать «секретарше Свете» для забивание информации по пользователям, но cn в нем хорошо фильтруеться и ничего натворить не получится( по крайней мере у меня не получилось, но я в этом деле не профи), а в postfixamdin им делать нечего).
По сути для заполнения доступны почти все поля используемые в адресной книге thunderbird, но в шаблоне (custom_mozillaOrgPerson.xml, есть в архиве) лишние, на мой взгляд, поля скрыты, вы можете сделать их доступными для редактирования просто убрав параметр
<hidden>1</hidden>
у нужного поля(атрибута).
В итоге мы имеем: При добавлении в postfixadmin домена(пользователя) в каталог ldap добавляется соответствующий OU(запись адресной книги), при удалении в postfixadmin домена(пользователя) происходит соответствующее удаление и в ldap, При редактировании имени в postfixadmin в ldap изменяются «cn-Отображаемое имя», «givenname- Имя» и «sn- Фамилия», но изменения в mysql «name- Имя» происходит только при редактировани «cn-Отображаемое имя» в phpldapadmin. Мелочь- а приятно).
PS: вообще называть cn- «Отображаемое имя» неправильно, в схеме inetorgperson есть отдельный параметр displayName, но в качестве «Отображаемое имя» по-умолчанию thunderbird использует именно cn, во избежании путаницы и для полного соответствия полям thunderbird в шаблоне для phpldapadmin cn называется «Отображаемое имя».

немного скриншотов:


Сылки по теме:
Все нужные скрипты и конфиги
Настройка адресной книги в ldap и установка phpldapadmin
еще
Оф. сайт phpldapadmin
Параметры шаблонов для phpldapadmin
Оф. сайт postfixadmin
OpenLDAP Software 2.4 Administrator's Guide
еще
Roundcube и адресная книга в ldap
еще

PS2: Если вы нашли лексические, орфографические ошибки- просьба писать о них в личку и я их обязательно исправлю.
  • +7
  • CraDem
  • 25 мая 2011, 13:05

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

  • avatar
  • LRN
  • 25 мая 2011, 18:20
  • #
  • 0
Читать всё не стал (многабукафниасилил), спрошу сразу о наболевшем:
Как в thunderbird аутентифицировать IMAP через NTLM, если логин у пользователя на кириллице?
не создавать логинов на кирилице…
Понятие не имею, не сталкивался и по пьяни мне такое в голову не придет(извините уж).
Эм и статья то как бы немножечко совсем о другом)
Ну, я посмотрел, увидел, что ты в теме разбираешься… решил спросить. Жаль, если не знаешь.
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.