Связка Jabber сервера и почты

Cерьёзные фирмы предпочитают поддерживать все необходимые сервисы на собственных серверах. Этот путь, конечно, даёт массу преимуществ. Системный администратор компании сможет настроить что угодно и как угодно. Однако есть и один существенный недостаток: если у Google всё уже настроено и связано воедино, то вам придётся настраивать всё вручную. Плюс вы вряд ли сможете обеспечить вашу систему таким же красивым и удобным веб-интерфейсом. Однако, как показывает практика, развернуть гибкую и мощную инфраструктуру для компании можно легко и не прибегая к помощи Google. Здесь рассказывается, как интегрировать XMPP сервер с почтовой системой, чтобы получилось в итоге значительно лучше, чем у Google.

Почтовый сервер

Для начала вам потребуется почтовый сервер. Разумным выбором MTA является Postfix, как один из самых модульных, гибких и при этом надёжных. Для доставки почты конечным получателям без вариантов необходим IMAP сервер, в качестве которого я использую Dovecot.

Dovecot позволяет легко использовать в качестве базы пользователей практически любое стандартное хранилище. Я, например, использую AD. Кроме того, Dovecot предоставляет сторонним приложениям возможность авторизовываться через себя, в частности, это умеет делать Postfix. Таким образом получается весьма удобная схема: за общение с БД пользователей отвечает Dovecot, а все остальные компоненты системы просто обращаются к нему за проверкой авторизации. Преимущество очевидно: захотелось вам поменять БД (например, отказаться от AD в пользу OpenLDAP) — ничего перенастраивать не надо, достаточно просто направить Dovecot на новое хранилище.

Как настраивать связку Dovecot+Postfix написано в документации к этим программам. В результате настройки я лично обзавёлся SMTP и IMAP серверами, авторизовываются на которых пользователи по доменному логину/паролю. Кстати, у Dovecot и Postfix очень хорошая документация — это тоже немаловажное преимущество этих продуктов. Поэтому уверен, что с настройкой этой связки проблем возникнуть не должно.

Jabber сервер

Осталось дело за малым: надо прикрутить к уже имеющейся почтовой системе Jabber сервер. Сделать это до смешного элементарно. Для начала потребуется установить ejabberd. Почему его? Хотя бы потому, что он самый гибкий. О богатых возможностях этого продукта написана масса статей, поэтому перейду сразу к организации авторизации ejabberd через Dovecot. К всеобщему счастью ejabberd поддерживает внешнюю авторизацию<, и всё, что нужно сделать — это написать скрипт проверки пользовательских данных через Dovecot.

Небольшое отступление: внешние механизмы авторизации через Dovecot работают через два UNIX сокета, создаваемых Dovecot. Параметры этих сокетов указываются в конфиге Dovecot, подробней про них можно почитать на официальной вики<.

Порывшись немного в интернете я нашёл такой вот фрукт:

http://search.cpan.org/~sasha/Authen-SASL-Authd-0.04/lib/Authen/SASL/Authd.pm<

Нагло потырив из него кода и добавив необходимое для ejabberd поведение получил конечный вариант:

#!/usr/bin/perl
use 5.010;
use IO::Socket::UNIX;
use IO::Select;
use MIME::Base64 qw(encode_base64);

# Сокеты Dovecot
$dovecot_auth_master = '/var/run/dovecot/auth-master';
$dovecot_auth_client = '/var/spool/postfix/private/auth-client';

sub read_until {
        my ($sock, $re, $timeout) = @_;
        my $sel = new IO::Select($sock);
        my $result = '';
        while ($result !~ /$re/m) {
                $sel->can_read($timeout) or die "Timed out while waiting for response";
                defined recv($sock, my $buf, 256, 0) or die 'Error while reading response';
                $result .= $buf;
        }
        return $result;
}

sub dovecot_auth {

        my ($login, $passwd) = @_;

        utf8::encode($login);
        utf8::encode($passwd);
       
        my $base64 = encode_base64("\0$login\0$passwd",'');

        my $service = "ejabberd";
        my $timeout = 3;

        my $sock = new IO::Socket::UNIX(Type => SOCK_STREAM, Peer => $dovecot_auth_client) or die;

        my $resp = read_until($sock,'DONE',$timeout);
       
        die unless $resp =~ /^VERSION\t1\t\d+$/m;
        die unless $resp =~ /^MECH\tPLAIN/m;
               
        $sock->send("VERSION\t1\t0\nCPID\t$$\nAUTH\t1\tPLAIN\tservice=$service\tsecured\tresp=$base64\n") or die;

        $resp = read_until($sock,'\n',$timeout);

        $sock->close;
       
        return $resp =~ /OK/;
}

sub dovecot_user {
       
        my $login = shift @_;
       
        utf8::encode($login);
       
        my $service = "ejabberd";
        my $timeout = 3;
       
        my $sock = new IO::Socket::UNIX(Type => SOCK_STREAM, Peer => $dovecot_auth_master) or die;
       
        my $resp = read_until($sock,'VERSION',$timeout);
       
        die unless $resp =~ /^VERSION\t1\t\d+$/m;
       
        $sock->send("VERSION\t1\t0\nUSER\t1\t$login\tservice=$service\tsecured\n") or die;
       
        $resp = read_until($sock,'\n',$timeout);

        $sock->close;
       
        return $resp =~ /USER/;
}

# Reading information from ejabberd
while(1) {
        my $nread = sysread STDIN, my $buf, 2;
        unless ($nread == 2) { exit }
        my $len = unpack "n", $buf;
        $nread = sysread STDIN, $buf, $len;
       
        my ($op,$user,$domain,$passwd) = split /:/, $buf;

        # Filter dangerous characters
        $user =~ s/[."\n\r'\$`]//g;
        $passwd =~ s/[."\n\r'\$`]//g;
        $domain =~ s/[."\n\r'\$`]//g;
       
        my $result = 0;
        if ($op =~ /auth/i) {
                $result = dovecot_auth($user, $passwd) ? 1 : 0;
        } elsif ($op =~ /isuser/i) {
                $result = dovecot_user($user) ? 1 : 0;
        }
       
        my $out = pack "nn", 2, $result;
        syswrite STDOUT, $out;
}
<


Небольшое замечание: исходный модуль из CPAN в данный момент вряд ли заработает. Для корректного взаимодействия с Dovecot при вызове функции encode_base64 необходимо указывать в качестве второго аргумента пустую строку. В представленном выше скрипте это конечно учтено.

Тут стоит обратить внимание, что для авторизации через Dovecot процесс ejabberd должен иметь права на чтение и запись в сокеты авторизации Dovecot. Работает ejabberd от имени пользователя ejabberd, который по странному стечению обстоятельств состоит в группе ejabberd. Таким образом не забудьте дать права rw для этого пользователя (или группы) на оба сокета Dovecot и на каталоги, в которых они находятся (про последний пункт почему-то часто забывают).

Осталось добавить в конфиг ejabberd наш скрипт:

{auth_method, external}.
{extauth_program, "/etc/ejabberd/auth.pl"}.
<

Собственно, на этом содержательная часть заканчивается. Теперь кроме почты пользователи смогут заходить под своими доменными логинами ещё и в Jabber. А если при настройке почтовой системы вы не сильно извращались, то JID и email у каждого пользователя будут совпадать и выглядеть как [email protected]

Однако осталось ещё догнать и перегнать Google. Чтож, это сделать очень просто. Достаточно совершенно стандартным способом включить нужные функции в ejabberd. Вот самые интересные с моей точки зрения возможности:

  • Конференции, они же MUC. За их поддержку отвечает стандартный модуль mod_muc, настраивать нужно разве что желаемые параметры по умолчанию.
  • Логгирование конференций. За это отвечает модуль mod_muc_log. Он кладёт все логи конференций в одну папочку в виде HTML файликов, остаётся только натравить на эту папочку Apache и разрешить доступ к полученному ресурсу только из локальной сети.
  • Логгирование личной переписки. Для включения этой возможности вам потребуется доустановить модуль mod_log_chat, который будет складывать все логи в одну папку опять-таки, в виде HTML файликов. Дальше можно написать простейший скрипт, который периодически будет сканировать эту папку и рассылать нужные файлы по нужным адресам электронной почты. Таким образом получим сходный с Google функционал.
  • Общие ростеры. Крайне полезная возможность в рамках компании. За неё отвечает mod_shared_rosters. Позволяет гибко управлять ростерами пользователей, добавляя в них целые группы.
  • Поддержка оффлайновых сообщений. Просто не отключайте её (mod_offline если что)
  • Транспорты. Я думаю, этот пункт в комментариях не нуждается. Вы можете предоставить своим сотрудникам лёгкий способ общаться через любую существующую сеть IM.

На всякий случай обращу ваше внимание на то, что т.к. мы настраивали совмещённую с почтой (и в моём случае даже с AD) систему авторизации, то всё, связанное с регистрацией, на Jabber сервере стоит отключить. Например, тот же mod_register. Просто чтоб глаза не мозолил.

Резюме

Вот так простым скриптом можно связать воедино почту и Jabber, при этом оставив себе практически неограниченную свободу настройки всего и вся в рамках используемых продуктов. А поскольку были использованы одни из самых функциональных, гибких и надёжных инструментов в своих областях, то фактически получается чуть ли не самая мощная система из всех возможных в рамках поставленной задачи (почта+IM для корпоративного общения). Никакие интегрированные решение вроде Exchange и уж тем более Google Apps конечно и близко не могут обеспечить подобного функционала. При этом с точки зрения пользователя полученная система абсолютно прозрачна и максимально проста в использовании, да и с точки зрения администратора тоже. Разве что администратору надо разобраться во всём при настройке (и правильно всё настроить!), а дальнейшая поддержка чуть менее чем элементарна.

P.S. Если будет кому-нибудь интересно — могу рассказать ещё как выжать много чего интересного из связки Dovecot+Postfix (IMAP ACL, админский прямой доступ к любому ящику из любого клиента, рассылки, общие папки, подписки, делегирование прав самими пользователями etc) и как организовать гибкую настройку любых параметров почтового клиента Thunderbird через сервер, избавив пользователей от необходимости (а при желании и возможности) вообще что-либо изменять в своём клиенте.

Story URL: