The OpenNET Project / Index page

[ новости /+++ | форум | теги | ]

Миграция с postfix + Active directory + squirrelmail на zimbra (postfix activedirectory ldap zimbra mail groupware)


<< Предыдущая ИНДЕКС Исправить src / Печать Следующая >>
Ключевые слова: postfix, activedirectory, ldap, zimbra, mail, groupware,  (найти похожие документы)
From: Буклов Борис <buklov@mail.ru.> Newsgroups: email Date: Mon, 15 Feb 2010 17:02:14 +0000 (UTC) Subject: Миграция с postfix + Active directory + squirrelmail на zimbra ------------------- Есть такой замечательный продукт Zimbra Collaboration Suite 6.0 - Open Source Edition, который имеет замечательный веб интерфейс и один в один брат близнец Zimbra Desktop для тех кто не любит веб интерфейс. Пример интерфейса можно посмотреть тут http://www.zimbra.com/products/hosted_demo.php У меня был настроен postfix и dovecot по статье http://www.linuxmail.info/postfix-dovecot-ldap-centos-5/ и группы рассылки по примеру http://www.linuxmail.info/postfix-active-directory-ldap-lookup-howto/ ну и squirrelmail в качестве веб интерфейса , что бы не устанавливать пользователям ни каких лишних программ. В виду того что Zimbra внешне выглядит намного лучше и позволяет делать почти все вещи что и MS Outlook и к тому же бесплано, было принято решение перейти на неё. Для этих целей была использована программа zmprov(идёт в комплекте) через которую можно делать очень много операций. Так как zimbra может использовать Ad только как источник внешней аутентификации , и пользователей создавать сама не может был написан скрипт который добавляет всех пользователей у которых в AD заполнено поле mail и оно имеет формат name@domain, где домен имеется в виду почтовый а не АД, а так же отключает пользователей которые есть в zimbra но нету в домене. Это сделано что бы если уволили юзера в ад его убили или отключили хотелось что бы в почте оно происходило тоже автоматом #!/usr/bin/perl use Net::LDAP; use utf8; use Encode; my $domain="mail_domain"; my $zmprov="/opt/zimbra/bin/zmprov -l"; my $AD_server="10.0.0.150"; my $AD_user="jabberd\@domain.ru"; my $AD_pass="passwd"; my $AD_base="dc=domain,dc=ru"; my $LDAP_server="10.0.0.242"; my $LDAP_user="uid=zimbra,cn=admins,cn=zimbra"; my $LDAP_pass="ldap"; my $LDAP_base="dc=mail_domain,dc=ru"; my %LDAP=(),%AD=(); #----------AD $ldap = Net::LDAP->new($AD_server); $ldap->bind($AD_user, password=>$AD_pass); $mesg = $ldap->search(filter=>"(&(objectclass=user)(mail=*\@$domain)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))", base=>$AD_base, attrs=> ['sAMAccountName','displayName','sn', 'givenname', 'initials','department','description','userAccountControl','mail','company','telephoneNumber']); @entries = $mesg->entries; foreach $entry (@entries) { my $name,$sn,$mail,$department,$company,$sAMAccountName; $id=$entry->get_value(sAMAccountName); $id=lc($id); $displayName=$entry->get_value(displayName); $sn=$entry->get_value(sn); $givenname=$entry->get_value(givenname); $initials=$entry->get_value(initials); $department=$entry->get_value(department); $description=$entry->get_value(description); $userAccountControl=$entry->get_value(userAccountControl); $mail=$entry->get_value(mail); $mail=lc($mail); $telephoneNumber=$entry->get_value(telephoneNumber); $company=$entry->get_value(company); $disabled=false; if (length($displayName)>0) { $AD{$id}{displayName} = $displayName; } if (length($sn)>0) { $AD{$id}{sn} = $sn; } if (length($givenname)>0) { $AD{$id}{givenname} = $givenname; } if (length($initials)>0) { $AD{$id}{initials} = $initials; } if (length($description)>0) { $AD{$id}{description} = $description; } if (length($disabled)>0) { $AD{$id}{disabled} = $disabled; } if (length($mail)>0) { $AD{$id}{mail} = $mail; } if (length($telephoneNumber)>0) { $AD{$id}{telephoneNumber} = $telephoneNumber; } if (length($company)>0) { $AD{$id}{company} = $company; } } #---------- zimbra ldap print "\nZIMBRA\n"; $ldap2 = Net::LDAP->new($LDAP_server); $ldap2->bind($LDAP_user, password=>$LDAP_pass); $mesg = $ldap2->search(filter=>"(&(objectClass=zimbraAccount)(!(userPassword=*))(!(zimbraCalResType=*)))", base=>$LDAP_base, attrs=> ['uid', 'displayName','sn','givenname','sn','mail','initials', 'description','zimbraPrefFromAddress','zimbraMailDeliveryAddress','zimbraAccountStatus','telephoneNumber','company'] ); my $id='',$displayName='',$sn='',$givenname='',$initials='',$department='',$description='',$disabled='',$mail=''; @entries = $mesg->entries; foreach $entry (@entries) { $id=$entry->get_value(uid); # if (($id ne "admin")&&($id ne "admin")&&($id ne "wiki")&&($id ne "spam.2bv4rvsjs")&&($id ne "ham.xdnsnpsmd3")) { $displayName=$entry->get_value(displayName); $sn=$entry->get_value(sn); $givenname=$entry->get_value(givenname); $sn=$entry->get_value(sn); $initials=$entry->get_value(initials); $description=$entry->get_value(description); $telephoneNumber=$entry->get_value(telephoneNumber); $company=$entry->get_value(company); $status=$entry->get_value(zimbraAccountStatus); if (defined ($AD{$id})) { if ("$status" ne "active") { print "$id status=active\n"; $status="active"; } else{ $status=""; } } else { if ("$status" eq "active") { print "$id status=locked\n"; $status="locked"; } else{ $status=""; } } $mail=$entry->get_value(zimbraPrefFromAddress); if (length($mail)==0) { $mail=$entry->get_value('zimbraMailDeliveryAddress'); } if (length($displayName)>0) { $LDAP{$id}{displayName} = $displayName; } if (length($sn)) { $LDAP{$id}{sn} = $sn; } if (length($givenname)>0) { $LDAP{$id}{givenname} = $givenname; } if (length($initials)>0) { $LDAP{$id}{initials} = $initials; } if (length($description)>0) { $LDAP{$id}{description} = $description; } if (length($telephoneNumber)>0) { $LDAP{$id}{telephoneNumber} = $telephoneNumber; } if (length($company)>0) { $LDAP{$id}{company} = $company; } if (length($mail)>0) { $LDAP{$id}{mail} = $mail; } if (length($status)>0) { $AD{$id}{status} = $status; } # } } print ("\n===AD==\n"); my $id='',$displayName='',$sn='',$givenname='',$initials='',$department='',$description='',$disabled='',$mail=''; print "удаляем одинаковые и несовпадающие с ад поля\n"; my $k2,$v2; while(my ($k,$v)=each(%AD)) { #Юзер уже есть if (defined ($LDAP{$k})) { while(($k2,$v2)=each(%{$AD{$k}})) { # print "$k2 # $v2\n"; if ($LDAP{$k}{$k2} ne $AD{$k}{$k2}) { delete $LDAP{$k}{$k2}; # print "удалено значение не соответсвующее АД значение\n"; } else { # print "Удалены идентичные значения\n"; delete $AD{$k}{$k2}; delete $LDAP{$k}{$k2}; } } } } open(PIPE,'|/opt/zimbra/bin/zmprov -l'); my $create; my $attr_st; my $alias_st; open (FILE, ">commands.list"); while(my ($k,$v)=each(%AD)) { $create=0; $attr_st=""; $alias_st=""; $from_st=""; if (!(defined ($LDAP{$k}))) { $create=1; } #обновляем данные юзера while(my ($k2,$v2)=each(%{$AD{$k}})) { # print "$k --> $k2:$v2\n"; if ($k2 eq "mail") { if ("$k\@$domain" ne "$v2" ) { $alias_st=$v2; } } if ($k2 eq "displayName") { $attr_st="$attr_st displayName \"$v2\""; } if ($k2 eq "sn") { $attr_st="$attr_st sn \"$v2\""; } if ($k2 eq "givenname") { $attr_st="$attr_st givenname \"$v2\""; } if ($k2 eq "initials") { $attr_st="$attr_st initials \"$v2\""; } if ($k2 eq "description") { $attr_st="$attr_st description \"$v2\""; } if ($k2 eq "sn") { $attr_st="$attr_st sn \"$v2\""; } if ($k2 eq "status") { print "$k zimbraAccountStatus \"$v2\"\n"; $attr_st="$attr_st zimbraAccountStatus \"$v2\""; } if ($k2 eq "telephoneNumber") { print "$k telephoneNumber \"$v2\"\n"; $attr_st="$attr_st telephoneNumber \"$v2\""; } if ($k2 eq "company") { print "$k company \"$v2\"\n"; $attr_st="$attr_st company \"$v2\""; } } if ($create==1) { print PIPE "ca $k\@$domain '' $attr_st\n"; print FILE "ca $k\@$domain '' $attr_st\n"; } else { if (length($attr_st)>0) { print PIPE "ma $k\@$domain $attr_st\n"; print FILE "ma $k\@$domain $attr_st\n"; } } if (length($alias_st)>0) { print PIPE "aaa $k\@$domain $alias_st\n"; print FILE "aaa $k\@$domain $alias_st\n"; } if (length($alias_st)>0) { print PIPE "ma $k\@$domain zimbraPrefFromAddress $alias_st\n"; print FILE "ma $k\@$domain zimbraPrefFromAddress $alias_st\n"; } } close(FILE); close(PIPE); Так же примерно переносятся группы рассылки из АД так как в АД могут быть группы которые не имеют email а они могут быть включены в группу рассылки , то просто собираем всех юзеров с почтовым адресом из этой группы и всех групп которых она содержит и добавляем в группу рассылки zimbra #!/usr/bin/perl use Net::LDAP; use Net::LDAP::Control::Sort; use Net::LDAP::Constant qw(LDAP_CONTROL_SORTRESULT); my $domain="mail_domain"; my $AD_server="10.0.0.150"; my $AD_user="jabberd\@domain.ru"; my $AD_pass="passwd"; my $AD_base="dc=domain,dc=ru"; my $LDAP_server="10.0.0.242"; my $LDAP_user="uid=zimbra,cn=admins,cn=zimbra"; my $LDAP_pass="passwd"; my $LDAP_base="dc=mail_domain,dc=ru"; #use utf8; my %LDAP=(),%AD=(); open(PIPE,'|/opt/zimbra/bin/zmprov -l'); sub PrintMembers { my $dn = @_[0]; my $group_mail = @_[1]; # print "dn=$dn group_mail=$group_mail\n"; my $type="user"; my $ldap = Net::LDAP->new($AD_server); $ldap->bind($AD_user, password=>$AD_pass); my $sortc = Net::LDAP::Control->new( LDAP_CONTROL_SORTREQUEST, order => 'name'); my $mesg = $ldap->search(filter=>"(distinguishedName=$dn)", base=>$AD_base,control => ['name'] ,control => [ $sortc ], attrs=> ['objectClass','name','distinguishedName','member','mail'] ); @entries = $mesg->entries; foreach $entry (@entries) { my $member=$entry->get_value(member); my $objectClass=$entry->get_value(objectClass); my $name=$entry->get_value(name); my $mail=$entry->get_value(mail); # print "name=$name\n\n"; foreach my $vals ($entry->get_value(objectClass)) { if ("$vals" eq "group") { $type="group"; } } if ("$type" eq "group") # если группа распечатать всех { foreach my $vals ($entry->get_value(member)) { PrintMembers($vals,"$group_mail"); } } else { #иначе вывести только имена if (length($mail)>0) { # print "$group_mail member -- $mail \n"; $AD{$group_mail}{$mail} = $mail; } } } $ldap->unbind(); return $i; } my $ldap = Net::LDAP->new($AD_server); $ldap->bind($AD_user, password=>$AD_pass); my $mesg = $ldap->search(filter=>"(&(objectClass=group)(mail=*\@$domain))", base=>$AD_base, attrs=> ['member','name','distinguishedName','mail'] ); my @entries = $mesg->entries; foreach $entry (@entries) { my $group_mail=$entry->get_value(mail); my $dn= $entry->get_value(distinguishedName); my $name=$entry->get_value(name); # print "--------GROUP $group_mail-------\n"; PrintMembers($dn,"$group_mail"); } #это просто файл куда пишутся комманды которые выполнились в zmpro для отладки open (FILE, ">commands.list"); print "\n--------ZIMBRA groups--------\n"; $ldap = Net::LDAP->new($LDAP_server); $ldap->bind($LDAP_user, password=>$LDAP_pass); $mesg = $ldap->search(filter=>"(objectClass=zimbraDistributionList)", base=>$LDAP_base, attrs=> ['mail', 'cn','zimbraMailForwardingAddress'] ); my $name,$sn,$mail,$department,$company,$sAMAccountName; @entries = $mesg->entries; foreach $entry (@entries) { $cn=$entry->get_value(cn); $group_mail=$entry->get_value(mail); @members=$entry->get_value(zimbraMailForwardingAddress); #delete empty groups if ($#members==-1) { print FILE "ddl $group_mail\n"; print PIPE "ddl $group_mail\n"; } else { foreach $mail (@members) { $LDAP{$group_mail}{$mail} = $mail; # print FILE "LDAP___$group_mail $mail\n"; } } } #print "-------Удаляем одинаковые значения\n"; while(my ($k,$v)=each(%AD)) { while(my ($k2,$v2)=each(%{$AD{$k}})) { #print "$k $k2\n"; if (defined ($LDAP{$k})) { if (defined ($LDAP{$k}{$k2})) { delete $LDAP{$k}{$k2}; delete $AD{$k}{$k2}; } } } } #print "------ Удаляем группы которых нету в АД"; while(my ($k,$v)=each(%LDAP)) { # print "\n$k $k2\n"; if (!(defined ($AD{$k}))) { print FILE "ddl $k\n"; print PIPE "ddl $k\n"; } else { while(my ($k2,$v2)=each(%{$LDAP{$k}})) { print FILE "rdlm $k $k2\n"; print PIPE "rdlm $k $k2\n"; } } } while(my ($k,$v)=each(%AD)) { print FILE "cdl $k\n"; print PIPE "cdl $k\n"; while(my ($k2,$v2)=each(%{$AD{$k}})) { print FILE "adlm $k $k2\n"; print PIPE "adlm $k $k2\n"; } } close(FILE); close(PIPE); Эти 2 скрипта у меня запускаются каждый час для того что бы в zimbra всегде был актуальные юзеры и списки. Теперь приступим к переносу адресных кних пользователей. В squirrelmail они хранятся в файлах username.abook этот скрипт загружает адресную книгу одного пользователя cat convert.pl #!/usr/bin/perl use utf8; open (FILE, "$ARGV[0]"); open(PIPE,'|/opt/zimbra/bin/zmprov'); $username=$ARGV[0]; $username=~s/.*[^\/]+\///; $username=~s/.abook//; print PIPE "sm $username\n"; while ($line = <FILE>) { $line=~s/\n//; @contact = split(/\|/,$line); print PIPE "createContact company \"\" email \"@contact[3]\" firstName \"@contact[1]\" lastName \"@contact[2]\" notes \"@contact[4]\"\n"; } print PIPE "quit\n"; print PIPE "quit\n"; close (FILE); close (PIPE); а этот перебирает все файлы и запускает вышестоящий скрипт cat convert.sh #!/bin/bash for file in /zimbra/address_book/books/*.abook do ./convert.pl $file echo "Converting $file" done ну и напоследок загружаем все почтовые сообщения в zimbra #!/usr/bin/perl $path="/zimbra/messages"; $hostname="hostname";# здесь пишем имя сервера, так как сообщения имеют вид например # у меня 1232529672.V806I98065M202440.mx.domain_name.ru:2,S open(PIPE,'|/opt/zimbra/bin/zmprov'); sub load_user { $username = $_[0]; print PIPE "sm $username\n"; #load inbox @ms=`find $username -maxdepth 2 -regex '.*$hostname.*'`; my $mess=""; foreach $message (@ms) { print PIPE "am inbox $path/$message" } @dirs=`find $username/.* -maxdepth 0 -type d`; foreach $dir (@dirs) { $dir=~s/($username\/)//; $dir=~s/(\n)+//; if (("$dir" eq "..")||("$dir" eq ".")) { next; } $dir=substr($dir,1,length($dir)); print "$dir\n"; print PIPE "createFolder \"/$dir\"\n"; @ms=`find '$username/.$dir' -regex '.*$hostname.*'`; print "find \'$username/.$dir\' -regex \'.*$hostname.*\'"; foreach $message (@ms) { $message=~s/(\n)+//; print PIPE "am \"$dir\" \"$path/$message\"\n"; } } print PIPE "quit"; } $username=$ARGV[0]; load_user($username); close (PIPE); этот скрипт загружает одну папку имя которой передаётся в качестве аргумента.

<< Предыдущая ИНДЕКС Исправить src / Печать Следующая >>

Обсуждение [ Линейный режим | Показать все | RSS ]
  • 1.1, eth1 (?), 19:51, 27/02/2010 [ответить] [﹢﹢﹢] [ · · · ]  
  • +/
    автор, почему ты такой безграмотный? Читать невозможно :(
     
  • 1.2, ACCA (ok), 03:40, 08/03/2010 [ответить] [﹢﹢﹢] [ · · · ]  
  • +/
    ладно придираться - это ещё неплохо. С запятыми беда, но остальное терпимо. Вообще очень хорошее дело - падонки научили людей ценить грамотное письмо.

    Автору, если читает -

    вместо длиннющих индусско-китайских повторений

    $givenname=$entry->get_value(givenname);
    $sn=$entry->get_value(sn);
    [...]
    if (length($displayName)>0) {
        $LDAP{$id}{displayName} = $displayName;
    }
    if (length($sn)) {
        $LDAP{$id}{sn} = $sn;
    }
    [...]

    Следовало использовать подобную конструкцию:

    my @attrs = qw'givenname sn displayName...';

    foreach my $attr (@attrs) {
        $value{$attr} = $entry->get_value($attr);
    }

    [...]

    foreach my $attr (@attrs) {
        $LDAP{$id}{$attr} = $value{$attr} if (length($value{$attr}));
    }

     
  • 1.3, capitan__nemo (?), 17:02, 17/03/2010 [ответить] [﹢﹢﹢] [ · · · ]  
  • +/
    У кого нить данный скрипт запустился?
     
  • 1.4, grishick (?), 22:47, 10/06/2010 [ответить] [﹢﹢﹢] [ · · · ]  
  • +/
    Вот тут пишут, что работает: http://community.livejournal.com/ru_zimbra/34298.html
     
  • 1.5, thunder (??), 10:26, 06/01/2011 [ответить] [﹢﹢﹢] [ · · · ]  
  • +/
    Научил Зимбру взаимодействовать с procmail и использовать внутренний антивирь, дабы не иметь 2-х одинаковых процессов. Если интересно - могу выложить конфиги.
     
  • 1.6, unpropable (?), 18:22, 21/02/2011 [ответить] [﹢﹢﹢] [ · · · ]  
  • +/
    Полезная  статья. Использовал при интеграции ZCS 7.0 с AD, но русские имена пользователей переносятся с крякозяблами. Если модифицировать так чтобы запускались команды
    zmprov ca acc@domain attr "русские буквы"
    zmprov ca acc@domain attr "русские буквы"
    то все ок, но очень медленно инициализируется zmprov на каждую команду, хз что делать даже.
     
  • 1.7, Justbox (?), 22:02, 12/03/2011 [ответить] [﹢﹢﹢] [ · · · ]  
  • +/
    Ктонить поборол Русские буквы в именах пользователей7
     
  • 1.8, Boris (??), 11:43, 27/03/2011 [ответить] [﹢﹢﹢] [ · · · ]  
  • +/
    только что опять занялся зимброй вот обновил немного скрипт, тут можно несколько... большой текст свёрнут, показать
     
     
  • 2.9, Илья (??), 11:22, 29/03/2011 [^] [^^] [^^^] [ответить]  
  • +/
    День добрый Boris!
    Попробовал использовать Ваш скрипт последней версии(т.к. возникла проблема со старым из-за отображения русских имен) -
    Получаю соответственно ошибки, где используются символы ╜
    Например 26 строка - my %LDAP=(),╜=();
    44, 217, 246
     
  • 2.10, Илья (??), 12:20, 29/03/2011 [^] [^^] [^^^] [ответить]  
  • +/
    Пришлось по старинке как и в первом скрипте использовать структуру
    my %LDAP=(),%AD=(); вместо > my %LDAP=(),╜=();  и других повторений ╜
     
     
  • 3.11, boris (??), 00:56, 04/04/2011 [^] [^^] [^^^] [ответить]  
  • +/
    да, как-то коряво вставился из буфера
     

  • 1.12, Илья (??), 15:00, 29/04/2011 [ответить] [﹢﹢﹢] [ · · · ]  
  • +/
    Просьба есть:
    как дописать скрипт, чтобы обновления из АД заливались на Зимбру?
    Чтоб не ручками описание править и там и там..
    Спасибо!
     
  • 1.13, Костик (??), 22:22, 30/08/2011 [ответить] [﹢﹢﹢] [ · · · ]  
  • +/
    Зачем в 7ке скрипты, когда она с ад работает тз каробки? А если есть екченж то с него есть очень удобный импорт всего что касается почтовых ящиков.
     
  • 1.14, Denis (??), 08:28, 19/07/2012 [ответить] [﹢﹢﹢] [ · · · ]  
  • +/
    при выполненнии скрипта ошибка
    ZIMBRA
    Can't call method "bind" on an undefined value at sync2.sh line 114, <DATA> line 558.

    в чем может быть проблема?

     
  • 1.15, Denis (??), 12:05, 20/07/2012 [ответить] [﹢﹢﹢] [ · · · ]  
  • +/
    ругается на $ldap2->bind($LDAP_user, password=>$LDAP_pass);
     
  • 1.16, Denis (??), 14:44, 23/07/2012 [ответить] [﹢﹢﹢] [ · · · ]  
  • +/
    проблема была в файле hosts маил домен нызвался mail.local, а в файле было MAIL.LOCAL
     
  • 1.17, В (?), 19:05, 07/12/2013 [ответить] [﹢﹢﹢] [ · · · ]  
  • +/
    Используйте Zentyal !
     

     Добавить комментарий
    Имя:
    E-Mail:
    Заголовок:
    Текст:




    Партнёры:
    PostgresPro
    Inferno Solutions
    Hosting by Hoster.ru
    Хостинг:

    Закладки на сайте
    Проследить за страницей
    Created 1996-2024 by Maxim Chirkov
    Добавить, Поддержать, Вебмастеру