Дружим между собой Active Directory, сервер IP телефонии Asterisk и Jabber-сервер OpenFire.
Не буду касаться установки всего по отдельности – все неплохо описано и работает в индивидуальном порядке весьма замечательно. Напишу, как я объединял все это вместе, на что наткнулся и что у меня получилось.
Диспозиция:
Есть контроллер домена dc.steepler.local (10.10.8.200). Домен, соответственно — steepler.local. На сервере заведены пользователи, побиты на отделы и т.д. Что важно – у каждого пользователя в графе «телефон» прописан его номер на сервере Asterisk.
Есть установленный VoIP сервер Asterisk (10.10.8.98). Все пользователи подключены по SIP, соответственно устройства, в понимании Asterisk, у них вида SIP/XXX (где XXX – добавочный номер). На момент написания сервер был давно установлен и уже подвергнут серьезным настройкам. Именно поэтому работа не была доведена до логического конца – закономерным финалом была бы автоматическая генерация SIP конфига оконечных устройств и плана набора. К сожалению, я побоялся, что придется серьезно переписывать существующую конфигурацию, да и в любом случае – автоматическая генерация того плана набора, который существует сейчас в нашей организации и имеет множество интерактивных меню и прочих кастомизаций, не будет интересна читателю, перед которым стоит задача, в первую очередь, связать между собой сервера. Куда двигаться дальше — будет понятно. Написанные скрипты не сложны и имеют хороший задел для дальнейшей работы.
Итак, есть Asterisk с установленным фронтендом FreePBX, что сильно усложнило задачу. Ибо FreePBX имеет обыкновение и необходимость переписывать поверх все конфигурационные файлы после внесения любых изменений в web интерфейсе. То есть, поменять конфиги из командной строки мы можем, но, как только поменяем что-то потом через веб-морду, конфиги будут затерты FreePBX’ом, как порядочным фронтендом. Конечно, создатели оболочки не полагались на свою гениальность и оставили возможность для тонкой настройки. Возможность эта реализуется через подгрузку дополнительных контекстов с суффиксом «-custom» (чего, в итоге, оказалось достаточно), либо при помощи “override” конфигов, которые жестко фиксируют необходимые вам изменения. Но, надо понимать, что то, что прописано в custom или override конфиге будет работать по-вашему, не взирая на веб-интерфейс FreePBX. Либо шашечки, либо ехать. То есть, что бы вы ни крутили там во фронтенде, что бы ни настраивали – если оно коснется кастомизированных настроек – работать будут настройки из файлов, а не фронтенда. Именно поэтому было потрачено много времени на трассировку плана набора, сгенерированного FreePBX – хотелось найти точку входа в такой процедуре, жесткая привязка которой не затронула бы дальнейшую работу.
Есть, вернее, на момент написания статьи – не было Jabber сервера. Выбор, по необъяснимым причинам пал на OpenFire. На самом деле, причины просты – OpenFire позволяет организовать сквозную (Kerberos/GSSAPI/SASL) авторизацию пользователей. То бишь – пользователю не надо вводить ни логина ни пароля. Если он прошел доменную авторизацию при входе в windows – он наш клиент. При запуске клиент сам подставит пользователя, пошлет запрос на jabber-сервер, а тот, используя Kerberos, подтвердит или опровергнет подлинность запроса клиента. Не буду вдаваться в детали, для нас важно, что авторизация проходит прозрачно для клиента даже в том случае, когда в домене существует политика периодической смены пароля. Не стоит генерировать истории про «тупых юзеров», которые пишут жалобы начальству о том, что у них что-то перестало работать по тому, что они забыли поменять пароль. Надо просто делать так, чтобы им было негде тупить.
На OpenFire можно установить штатный плагин Asterisk-IM для связи с Asterisk. Он позволяет динамично отслеживать статусы пользователей, звонить на IP телефоны, отправлять уведомления. К сожалению, из коробки автоматизация оставляет желать лучшего — не смотря на то, что возможна сквозная аутентификация и авторизация пользователей через AD и то, что в AD изначально предоставлена информация о рабочем телефоне пользователя, приходится вручную сопоставлять пользователей AD/Asterisk.
Итак. Jabber серверу быть OpenFire, зваться jbrgseveren01.steepler.local и работать по адресу 10.10.8.226.
Дальше я исхожу из того, что Linux у меня в виде CentOS5, Asterisk 1.8.2, а домен-контроллер Win2008. Хотя, это совершенно не принципиально, заработает и при других раскладах. Критична только версия Asterisk – поддержка jabber появилась только с ветки 1.6, да и скомпилирована PBX должна быть с его поддержкой.
Задачи:
Нужно установить Jabber сервер, настроить его так, чтобы необходимых пользователей он брал из доменной группы IM. Нужно тем или иным способом автоматизировать сопоставление информации о пользователях домена, пользователях jabber, абонентах Asterisk.
Как все работает из коробки? (Или подводные камни)
OpenFire обращается по ldap протоколу к домен-контроллеру и получает от него информацию о пользователях, которым разрешено пользоваться jabber.
Дальше нам нужно поставить плагин Asterisk-IM (два клика мышкой в веб-морде OpenFire). В плагине нам нужно прописать сервер Asterisk. И, oh-shit, руками, заново прописать всех пользователей в плане – логин — номер телефона — его абонентское устройство в понимании Asterisk. После этого заработает функционал плагина – при помощи родного клиента OpenFire под названием Spark пользователи смогут звонить друг — другу используя существующие телефоны просто кликая по списку контактов. То есть – нахожу контакт, щелкаю правой мышкой «позвонить», у меня на столе начинает звонить телефон, я снимаю трубку и, тотчас же, телефон начинает звонить у контакта. Более того, когда кто-то говорит по телефону, его статус в контакт-листе меняется на соответствующий. Удобно. Но, работу по прописыванию пользователей надо автоматизировать.
Едем дальше – есть желание присылать в jabber уведомления о пропущенных звонках. Одно дело, когда на телефоне моргает лампа и надо лезть в меню, смотреть, кто звонил и — совсем другое дело, когда тебя дожидается сообщение с точным временем и координатами звонивших. Из коробки не реализуется никак. То есть Asterisk, конечно легко цепляется к OpenFire серверу в режиме клиента или компонента, но вся дальнейшая задача по обработке и отправке сообщений ложится на ваши плечи. Конечно не сама работа по отправке, а объяснительная работа с Asterisk ;-) Самым тупым решением тут является обработка каждого номера по отдельности. Но, если номеров больше пяти, то оно нам не подходит. Плюс существует вероятность миграции пользователей между телефонами, добавление новых, удаление старых. К тому же, не забываем про FreePBX. Если мы жестко определим правила набора, то лишимся возможности пользоваться замечательным веб-интерфейсом. В общем, отказать в грубой форме. Не катит. Нам нужно найти точку входа в плане набора, написать свою процедуру, которая будет по номеру адресата искать соответствующего доменного пользователя и, в случае «недозвона» отправлять тому jabber сообщение – мол, звонил вам такого-то числа во столько-то такой-то абонент.
Приступаем
Первое, что нам понадобится – пользователи. Создаем в домене двух пользователей. Один нам будет нужен для ldap аутентификации, другой для Kerberos. Первого я назвал openfire, второго xmpp-openfire. Дальше – сразу создаем группу для пользователей jabber (у меня она называется IM) и добавляем в нее необходимых пользователей. Проверяем, чтобы у всех пользователей, имеющих внутренний телефон и входящих в группу IM в поле «номер телефона», стоял именно внутренний номер абонента.
Второе – прописываем в DNS наш будущий jabber сервер. Нам нужна и прямая и обратная зона. На самом jabber сервере настраиваем имя хоста – прописываем в /etc/hosts:
Проверяем со всем сторон nslookup’ом – все должно правильно ресолвиться. Да, имя хоста в маленьком регистре. Это важно.
Третье — ставим OpenFire по инструкции — http://www.igniterealtime.org/builds/openfire/docs/latest/documentation/ldap-guide.html
Там все просто, подводных камней нет. Максимум сложностей – формирование грамотных фильтров в ldap запросе. Подключаемся через первого пользователя. На выходе получите функционирующий сервер со сквозной авторизацией через AD. Можно подключать клиентов и работать. Но, наша задача – SSO: Single Sign On. Нам надо, чтобы пользователю не надо было думать о своем логине и пароле для клиента.
Приступаем к настройке Kerberos. Тут используем второго созданного нами юзера. Все описано тут —http://community.igniterealtime.org/docs/DOC-1060
Все чуть сложнее, есть подводные камни. Главное – пОмНиТе пРО РегиСтры – все имеет значение. Пишите как в мануале – где заглавными, там заглавными, где строчными, там строчными. Не забудьте ввести сервер в домен и проверить факт введения. Это важно!!!
Да, keytab я создавал на контроллере домена – у меня все заработало. Средствами java я не пользовался.
Ставьте Spark – родной клиент OpenFire, проверяйте – если SSO работает – хорошо. Если нет, надо разбираться – ищите, пишите, посмотрим.
Идем на сервер Asterisk (не забывайте – у меня стоит FreePbx, поэтому даю названия файлов относительно его схемы; в случае голого Asterisk все будет чуть проще) и прописываем в manager_custom.conf пользователя OpenFire:
Теперь ставим плагин Asterisk-IM. Он есть в веб-интерфейсе OpenFire, в закладке с доступными плагинами. Прописываем на появившейся вкладке Asterisk-IM наш VoIP сервер:
Настала очередь прописывать пользователей руками… Надо идти на вкладку Phone Mappings и писать, писать, писать. Ограничимся парой пользователей, проверим работу. В контакт листе Spark, при щелчке правой кнопкой по имени пользователя, должна появиться опция call. Выбираем – должен зазвонить наш аппарат, при подъеме трубки – аппарат абонента.
Если все работает – хорошо. Если нет, надо разбираться – ищите, пишите, посмотрим.
Теперь начинается то, на что было потрачено основное время.
Надо объяснить Asterisk-IM, что бывают доменные пользователи и что в Active Directory есть вся необходимая информация.
Напрямую – никак. Плагин старый, поддержка его прекращена – жрите, что есть. А есть у нас MySQL база данных, в которой плагин хранит свою информацию. Самым простым способом было бы вынимать из базы информацию по пользователям OpenFire и подсовывать ее Asterisk-IM. Но, так как аутентификация у нас сквозная, то в своей базе OpenFire ничего не хранит – тащит напрямую с домен-контроллера.
Хорошо. Пишем скрипт, который будет цепляться к AD по ldap протоколу (пользователь у нас уже есть), тащить информацию по пользователям домена, входящим в группу IM и вынимать поля, содержащие полное имя, логин и номер телефона. Потом формируем SQL инъекцию и запихиваем в прямо базу Asterisk-IM. Тупой костыль, но работает.
Скриптов вышло два – один я нашел готовый тут. Написан на perl – он тянет инфу из домена и, кстати, в состоянии выводить готовый sip.conf после минимальной правки. Второй, на баше – вызывает первый, препарирует его вывод (да я знаю, что я извращенец, но раз задача кем-то уже решена, не надо городить), формирует SQL инъекции и пихает все в БД.
Несного измененный users-from-AD.pl
А вот второй на баше:
phone-bindings-update-from-AD.sh:
Как можно заметить, второй скрипт использует шаблоны для заголовка и футера инъекции. Шаблоны получены путем выполнения mysqldump к существующим таблицам и последующей обрезки результата. Собственно, вот шаблоны:
phoneUser.tplhead:
phoneUser.tplfoot:
phoneDevice.tplhead:
phoneDevice.tplfoot:
Скрипт запихиваем в крон, выполняем и обнаруживаем появившуюся привязку пользователей к телефонам в закладке Phone Bindings плагина Asterisk-IM. Полдела сделано.
Следующая задача — отправка сообщений о пропущенных вызовах. Если у вас стоит TrixBox или FreePBX – делайте, как я – все скорее всего заработает. Если голый Asterisk – все в ваших руках, импровизируйте, вам доступно многое. Я, даже, отчасти завидую )))
Для начала необходимо выполнить авторизацию через ssh по ключам – мы будем использовать scp и дистанционное выполнение процедуры. ssh-keygen вам поможет, мануалов в сети достаточно, повторяться не буду. Скрипт на jabber сервере будет лезть в базу данных, которую мы правили предыдущим скриптом (да, их можно объединить в один, но я решал задачи не одновременно, да и в целом – unix way говорит о правильности разбиения задач на составляющие), вынимать логин пользователя, номер телефона. Формировать jID. Дальше мы готовим исполняемый скрипт, который будет вносить информацию во встроенную базу Asterisk, переносим его на сервер Asterisk и запускаем.
Вот что получилось:
phone-mapping-request.sh:
Дело за малым – объяснить Asterisk, что с этим делать. Тут было потрачено огромное количество времени на нахождение точки входа. Если заработает как у меня – прекрасно. Если нет – даю наводку. Астериск, в случае включения разных инструкций на одинаковое условие (то есть в диалплане написано одно действие на условие, во включении из подгружаемого контекста — другое) берет за инструкцию то, которое было получено первым. Последующие тупо игнорируются. Я к тому, что если вы написали какую-то функцию, вставили ее, а результата ноль – делайте dialplan show и смотрите, где это условие в этой ветке контекста со всеми include встречается раньше вашего.
В моем случае оказалось достаточным добавить в extensions_custom.conf:
И, прописать Asterisk как компонент OpenFire:
На стороне Asterisk:
Jabber.conf:
На стороне OpenFire идем в Server -> Server Settings -> External Components Settings
Включаем Service Enabled. При желании добавляем asterisk в whitelist.
Проверяем работу сервиса…
Надеюсь, чем-то помог. Если есть вопросы – пишите.