Active directory
Active Directory is just an LDAP-server with predefined attributes. A
sample configuration is shown below:
Configuration examples
Since there are many possible
DIT
layouts, it will probably be easiest to understand how to configure the
module by looking at an example for a given DIT (or one resembling it).
Control parameters
These parameters control the behaviour of the module.
Jabber (xmpp) windows sso, часть 1
Под катом чисто техническая статья про настройку Jabber-сервера под Linux и клиентов к нему под Windows с одновременным прикручиванием формирования контакт-листа из AD LDAP с SSO-авторизацией посредством Kerberos.
Сперва небольшой обзор имеющихся доступных решений.
Как ни странно, из актуальных Jabber-серверов SSO / GSSAPI поддерживают все три: eJabberd, Openfire, Prosody. Первый я не осилил ввиду зубодробительности erLang-а. Точнее, в дефолтной конфигурации его запустить как раз плюнуть (спасибо дистростроителям). Но вот если надо прикрутить что-то эх-такое, чего изначально не было предусмотрено в конструкции, возникает дикая попа-боль. Второй весьма user-friendly, большинство фич работают из коробки. Но это Java со всеми вытекающими. В частности, даже банальное подсовывание SSL-сертификата сразу превращается в увлекательнейший траходром. Третья лишена большинства недостатков первых двух, но она отвратительно (точнее, почти вообще никак) не документирована и не масштабируется. В том плане, что до 100…150 пользователей работает ОК, потом начинает тупить, тормозить и валиться. Конкретно мне больше и не надо, поэтому я взял в качестве сервера Prosody. О ней и пойдет дальнейший разговор.
Касаемо клиентов. Всего хоть как-то GSSAPI под виндой сейчас поддежривают: Miranda, Psi, Spark, Pidgin.
Miranda хороша-красива, когда “обвешана”. На “голую” (без плагинов) без слёз не взглянешь. Если же надо обновить версию, то “обвес” придётся формировать заново. Она не enterprise-friendly. Она глючновата в плане интерфейса. Может, например, почему-то не показать поступившее входящее сообщение. Может обвалиться после очередного обновления винды. Не проверяет SSL-сертификат сервера на валидность. Одним словом, мы попробовали, попользовались, нам не понравилось.
Что касается Psi, то для реализации GSSAPI там надо самостоятельно собирать Qt-шную библиотеку под названием qca-wingss. Готового бинарника я ожидаемо не нашёл, а скопилировать самостоятельно ниасилил.
Spark — это опять же Java, со всеми вытекающими. Кто желает, может пользовать. Меня от неё тошнит.
Остался Pidgin. Но и тут всё не слава богу. Во-первых, последняя виндовая версия, которая ещё умеет GSSAPI — это 2.10.11. В более свежих поддержку GSSAPI/SSPI выпилили (это касается только виндовых сборок; под линуксом всё нормально). Во-вторых, на клиентские машины нужно дополнительно ставить MIT Kerberos for Windows. Берут здесь. В более-менее “стандартной” конфигурации каких-то дополнительных настроек не требуется, в не совсем типичных случаях его тоже потребуется “подкрутить”.
Окей, с выбором софта более-менее разобрались. Теперь некоторые неочевидные нюансы. Полностью процесс “от А до Я” расписывать не буду. Потому что в энторнетах есть некоторое количество хаутушек, да и админ, взявшийся за подобную задачу, наверняка уже обладает некоторыми навыками и компетенцией. Расскажу о том, во что “влетел” конкретно я.
Что касается сервера. Документация к нему не отличается точностью и полнотой. Там есть определённая путаница. Следует учитывать, что до версии 0.8 данный механизм был реализован в модуле “mod_saslauth” через прокладку в виде saslauthd. После версии 0.8 функционал был перенесён в отдельный модуль “mod_auth_cyrus”, а saslauthd больше не нужен. То есть пакет “sasl2-bin” можно и не ставить, а руководствоваться нужно вот этой статьёй.
Далее. Как всегда, требуется сформировать в системе правильный конфиг “/etc/krb5.conf” и сгенерировать keytab. И самое главное, prosody должна иметь доступ на чтение и к тому, и к другому. Если вовремя не вспомнить, что в Debian-е по умолчанию она запускается из-под отдельного непривилегированного пользователя, то могут возникнуть весьма трудно диагностируемые проблемы и странные совершенно неинформативные фатальные ошибки в логах.
Кроме того, если в том же OpenFire функционал импорта пользователей из AD реализован “из коробки”, то в случае c Prosody его придётся колхозить самостоятельно, ручками. Это не так уж и сложно, но это нужно сделать. Об этом я напишу во второй части.
Теперь что касается клиента.
Pidgin-а ставим как обычно, но не свежее версии 2.10.11. С MIT Kerberos никаких премудростей нет, обычный wizard типа “xyяк, xyяк и в продакшн“ “next, next и готово”. На последнем шаге он спросит, надо ли автоматически стартовать при логине пользователя и надо ли сразу получать Kerberos-тикеты. Ответ: нет, не надо. Впрочем, на самом деле пофиг. Будет работать и так, и так.
В самом Pidgin-е в настройках аккаунта указываем только логин, имя домена и ресурс. Поле “пароль” оставляем пустым. Примерно так.
Если по условию задачи Kerber-осовский Realm совпадает с Jabber-овым доменом, то больше никаких действий предпринимать не нужно. Всё заведётся, пользователь успешно залогинится. В противном случае придётся совершить ещё ряд небольших шаманств.
Во-первых, сделать текстовый файл с названием “krb5.ini” по аналогии с линуксовым “krb5.conf” с указанием KDC и сопоставлением сущностей “domain” и “realm”. Чтобы оно понимало, из какого Realm-а запрашивать ключи для того или иного домена и где вообще искать KDC (Key Distribution Center). Упомянутый “krb5.ini” следует положить либо в “c:windows”, либо в профиль пользователя (по желанию админа). Во вторых, после неудачной попытки залогиниться нужно назначить “Default Ticket” прямо в интерфейсе MIT Kerberos (на скриншоте обвёл синим прямоугольником). Это требуется сделать ровно один раз. Возможно, данную настройку можно выполнить и через реестр. Не знаю, настолько глубоко не ковырял.
Как-то так и настраивается SSO-аутентификация. Про формирование ростера (списка контактов и групп) читайте в следующем псто.
Ldap connection
Two connections are established to the LDAP server per vhost, one for
authentication and other for regular calls.
To configure the LDAP connection there are these top-level options:
Example:
auth_method: [ldap]
ldap_servers:
- ldap1.example.org
ldap_port: 389
ldap_rootdn: "cn=Manager,dc=domain,dc=org"
ldap_password: "**********"
Supported storages
The following LDAP servers are tested with ejabberd:
Vcard in ldap
Since LDAP may be complex to configure in mod_vcard,
this section provides more details.
ejabberd can map LDAP attributes to vCard fields. This feature is
enabled when the mod_vcard module is configured with db_type:
ldap. Notice that it does not depend on the authentication method
(see LDAP Authentication).
Usually ejabberd treats LDAP as a read-only storage: it is possible to
consult data, but not possible to create accounts or edit vCard that is
stored in LDAP. However, it is possible to change passwords if
mod_register module is enabled and LDAP server supports
RFC 3062.
This feature has its own optional parameters. The first
group of parameters has the same meaning as the top-level LDAP
parameters to set the authentication method: ldap_servers,
ldap_port, ldap_rootdn, ldap_password, ldap_base, ldap_uids,
ldap_deref_aliases and ldap_filter.
Examples:
Yate в качестве jabber сервера

YATE является во многих отношениях уникальным телефонным сервером. Он понимает SIP-T, считается лучшим H323-SIP конвертером, а также поддерживает большую часть семейства протоколов ОКС-7 (SS7). И все это доступно под GPL. С другой стороны, проблемой является недостаточная документированность проекта.
Но я хочу рассказать не о телефонии. Кролики — это не только ценный мех, но и Yate может служить также jabber сервером. Забавно, что Yate не указан в списке xmpp.org/xmpp-software/servers, хотя поддержка jabber сервера появилась в нем еще в 2022 году.
Трудно сказать, зачем может понадобиться использовать Yate для джаббера, если есть ejabberd, Openfire, Prosody и Tigase. Этот вопрос — за рамками статьи. Я хочу лишь познакомить вас с еще одним вариантом.
Итак, добро пожаловать под cut. (Осторожно, много букв!) Заодно расскажу, как прикрутить авторизацию в Active Directory.
Будем исходить из того, что с установкой читатель справится самостоятельно. Займемся настройкой.
Указываем основные параметры jabber сервера: домен, слушающие сокеты, отменяем digest авторизацию (необходимо для передачи пароля в исходном виде для авторизации в AD).
Внимание! Опция c2s_plainauthonly доступна только в svn, в версии 4.3 ее нет.
jabberserver.conf
[general]
domains=mydomain.org
c2s_plainauthonly=yes; force text password for LDAP auth
[listener s2s]
enable=yes
type=s2s
port=5269
[listener c2s]
enable=yes
type=c2s
port=5222
Чтобы не передавать наш пароль в открытом виде, нужно включить SSL шифрование. Для этого мы укажем в настройках, для каких доменов следует использовать ssl сертификат. Следует заметить, что не следует включать опцию sslcontext=, которую вы можете увидеть в дефолтном конфиге, так как в этом случае Yate будет ожидать от клиента изначально шифрованное соединение, тогда как jabber клиенты обычно используют двух-этапную процедуру StartTLS.
openssl.conf
[yate]
certificate=yate.pem
domains=mydomain.org
Как генерировать сертификат, рассказывать не буду.
Настраиваем модуль авторизации и регистрации к базе данных.
register.conf
[general]
user.auth=yes
user.register=yes
user.unregister=yes
engine.timer=yes
[default]
account=yate
[user.auth]
query=SELECT password FROM users WHERE username='${username}' AND password IS NOT NULL AND password<>''
result=password
[user.register]
query=UPDATE users SET location='${data}', expires=CURRENT_TIMESTAMP INTERVAL ${expires} 300 second WHERE username='${username}'
[user.unregister]
query=UPDATE users SET location=NULL,expires=NULL WHERE expires IS NOT NULL AND username='${username}'
[engine.timer]
query=UPDATE users SET location=NULL,expires=NULL WHERE expires IS NOT NULL AND expires<=CURRENT_TIMESTAMP
Структура базы данных для MySQL. Можно использовать и Postgres.
CREATE TABLE `offlinechat` (
`username` varchar(100) DEFAULT NULL,
`xml` text,
`time` int(11) NOT NULL,
KEY `username` (`username`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `roster` (
`username` varchar(100) DEFAULT NULL,
`contact` varchar(100) DEFAULT NULL,
`name` varchar(100) DEFAULT NULL,
`groups` varchar(100) DEFAULT NULL,
`subscription` varchar(100) DEFAULT NULL,
UNIQUE KEY `uc` (`username`,`contact`),
KEY `username` (`username`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `users` (
`username` varchar(100) NOT NULL DEFAULT '',
`password` varchar(100) DEFAULT NULL,
`vcard` text,
`location` varchar(100) DEFAULT NULL,
`expires` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`username`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
Настраиваем коннектор к базе данных
mysqldb.conf
[yate]
database=yate
user=yate
password=yatepass
Настраиваем функцию vcard и офлайн сообщений.
jbfeatures.conf
[general]
account=yate
[vcard]
get=SELECT vcard FROM users WHERE username='${username}'
set=UPDATE users SET vcard='${vcard}' WHERE username='${username}'
[offline_chat]
get=SELECT * FROM offlinechat WHERE username='${username}' ORDER BY time
add=INSERT INTO offlinechat (username,xml,time) VALUES ('${username}', '${xml}', ${time})
clear_user=DELETE FROM offlinechat WHERE username='${username}'
Настраиваем ростер.
subscription.conf
[general]
account=yate
user_roster_load=SELECT users.username, roster.* FROM users LEFT OUTER JOIN roster ON users.username=roster.username WHERE users.username='${username}'
user_roster_delete=DELETE FROM roster WHERE username='${username}'
contact_load=SELECT * FROM roster WHERE username='${username}' AND contact='${contact}'
contact_subscription_set=INSERT roster (username,contact,subscription) VALUES ('${username}','${contact}','${subscription}') ON DUPLICATE KEY UPDATE subscription='${subscription}'
contact_set=INSERT roster (username,contact,name,groups) VALUES ('${username}','${contact}','${name}','${groups}') ON DUPLICATE KEY UPDATE name='${name}',groups='${groups}'
contact_set_full=INSERT roster (username,contact,name,groups,subscription) VALUES ('${username}','${contact}','${name}','${groups}','${subscription}') ON DUPLICATE KEY UPDATE name='${name}',groups='${groups}',subscription='${subscription}'
contact_delete=DELETE FROM roster WHERE username='${username}' AND contact='${contact}'
Как видите, большая часть настройки сводится к прописыванию SQL запросов. Может показаться неудобным, но зато дает гибкость.
Теперь об авторизации через Active Directory.
Во-первых, подправим register.conf, чтобы пользователи автоматически добавлялись в базу данных после успешной авторизации (а вот и гибкость пригодилась!).
[user.register]
query=INSERT users (username,location,expires) VALUES ('${username}','${data}',CURRENT_TIMESTAMP INTERVAL ${expires} 300 second) ON DUPLICATE KEY UPDATE location='${data}', expires=CURRENT_TIMESTAMP INTERVAL ${expires} 300 second
Во-вторых, добавим немного Yate магии. Ядро сервера представляет из себя диспетчер системных сообщений, которыми Engine обменивается со стандартными и внешними модулями (нет принципиальных различий). Чтобы запустить внешний модуль на PHP, достаточно прописать его в конфиге. Наш скрипт jabber.php будет перехватывать системное сообщение user.auth (запрос на авторизацию), и обрабатывать его, обращаясь к AD. Сообщение перехватывается за счет приоритета обработчика = 40, тогда как остальные модули имеют приоритет 50 и выше, что является менее приоритетным.
extmodule.conf
[general]
scripts_dir=/etc/yate/
[scripts]
jabber.php=
И, наконец, сам скрипт (модуль) авторизации. Обратите внимание, он использует библиотеку «libyate.php», входящую в дистрибутив Yate. Она должна быть доступна для скрипта, поэтому лучше всего скопировать ее в тот же каталог. Если контроллер домена использует само-подписанный сертификат, то следует добавить в /etc/openldap/ldap.conf строку
TLS_REQCERT never
jabber.php
#!/usr/bin/php -q
<?php
require_once("libyate.php");
$ad_host = 'ldaps://dc.mydomain.org';
$ad_domain = 'mydomain.org';
/* Always the first action to do */
Yate::Init();
/* Install a handler for the call routing message */
Yate::Install("user.auth",40);
function ad_auth($user, $password) {
global $ad_host, $ad_domain;
$con = ldap_connect($ad_host);
return ldap_bind($con, "$user@$ad_domain", $password) & true;
}
/* The main loop. We pick events and handle them */
for (;;) {
$ev=Yate::GetEvent();
/* If Yate disconnected us then exit cleanly */
if ($ev === false)
break;
/* Empty events are normal in non-blocking operation.
This is an opportunity to do idle tasks and check timers */
if ($ev === true) {
// Yate::Output("PHP event: empty");
continue;
}
/* If we reached here we should have a valid object */
switch ($ev->type) {
case "incoming":
switch ($ev->name) {
case "user.auth":
if (!isset($ev->params["digest-uri"])) {
$username = $ev->params["username"];
$username = substr($username,0,strpos($username,'@'));
$password = isset($ev->params["response"]) ? $ev->params["response"] : $ev->params["password"];
$auth = ad_auth($username, $password);
if ($auth) {
$ev->retval = $password;
$ev->handled = true;
}
}
break;
}
$ev->Acknowledge();
break;
case "installed":
Yate::Output("PHP Installed: " . $ev->name);
break;
case "uninstalled":
Yate::Output("PHP Uninstalled: " . $ev->name);
break;
default:
Yate::Output("PHP Event: " . $ev->type);
}
}
Yate::Output("PHP: bye!");
/* vi: set ts=8 sw=4 sts=4 noet: */
?>
Как говорит Paul Chitescu, главный разработчик Yate, — must be ready.
И напоследок у меня две новости, хорошая и плохая:
Если что-то непонятно, или не работает, задавайте вопросы, постараюсь ответить.
Подключаем пользователей active directory к ejabberd [rtzra’s hive]
Выбираем данные о пользователях. Пользователь должен быть:
{mod_shared_roster_ldap, [{ldap_groupattr,"department"}, {ldap_groupdesc,"department"}, {ldap_base, "dc=YOU-DOMAIN"}, {ldap_rfilter, "(&(memberOf=CN=JabberUsers,CN=Users,DC=YOU-DOMAIN)(|(userAccountControl=66050)(userAccountControl=66048)))"}, {ldap_memberattr,"sAMAccountName"}, {ldap_userdesc,"cn"} ] }
Выбираем из LDAP информацию в нужном формате:
{mod_vcard_ldap, [{ldap_vcard_map, [{"NICKNAME", "%u", []}, {"GIVEN", "%s", ["givenName"]}, {"MIDDLE", "%s", ["initials"]}, {"FAMILY", "%s", ["sn"]}, {"FN", "%s", ["displayName"]}, {"EMAIL", "%s", ["mail"]}, {"ORGNAME", "%s", ["company"]}, {"ORGUNIT", "%s", ["department"]}, {"CTRY", "%s", ["c"]}, {"LOCALITY", "%s", ["l"]}, {"STREET", "%s", ["streetAddress"]}, {"REGION", "%s", ["st"]}, {"PCODE", "%s", ["postalCode"]}, {"TITLE", "%s", ["title"]}, {"URL", "%s", ["wWWHomePage"]}, {"DESC", "%s", ["description"]}, {"TEL", "%s", ["telephoneNumber"]}]}, {ldap_search_fields, [{"User", "%u"}, {"Name", "givenName"}, {"Family Name", "sn"}, {"Email", "mail"}, {"Company", "company"}, {"Department", "department"}, {"Role", "title"}, {"Description", "description"}, {"Phone", "telephoneNumber"}]}, {ldap_search_reported, [{"Full Name", "FN"}, {"Nickname", "NICKNAME"}, {"Email", "EMAIL"}]} ]}
Памятка: внутренний сервер "аси" с прозрачной авторизацией.
Настраиваем связку Внутренний сервер “аси” OpenFire с прозрачной авторизацией в windows домене.
1. Качаем сам OpenFire с сайта разработчика — (на данный момент последняя версия – 3.6.4
… пропускаем автоматические нажатия мышки
Выбор БД. Есть два варианта установки OpenFire:
так как пользователей не много я выбрал – встроенную базу данных(Embedded DataBase).
Останавливаемся подробнее на странице Profile Settings
Далее начинается самое интересное, а именно – настройка того кто и как будет подключаться. Мы хотим связать наш OpenFire с AD, следовательно на странице Profile Settings выбираем пункт Directory Server (LDAP)
Далее проводим настройки подключения к серверу:
1. Server Type – Active Directiry;
2. Host – имя вашего контроллера домена, например serverdc;
3. DN – имя домена в виде dc=«mydomain»,dc=«local» соответственно для домена mydomain.local”
4. Далее логин и пароль учетки, из под которой будут читаться данные из AD
Давим на кнопку Test Settings и если видим Status: Success! – значит все ОК, если нет, то проверяем выше введенные данные, особенно обращаем внимание на строчку Base DN в плане наличия пробелов, их там быть не должно!
В следующем окне оставил поля: {cn} EMAIL displayName
и нажимаем кнопку Test Settings, если видим табличку с данными какой-нить доменной учетки (выбирается рандомно), то все хорошо, если нет, то возвращаемся и проверяем настройки подключения к AD. На данном этапе лично я ещё установил фильтр выбора данных из АД, для этого в Advanced Settings вместо (objectClass=organizationalPerson) вписываем следующее значение:
(objectClass=user)(objectCategory=person)(!(userAccountControl:1.2.840.113556.1.4.803:=2))
Эта строчка поможет нам избавиться от отключенных учетных записей, которые наверняка у всех есть и очень будут мешаться.
… пропускаем автоматические нажатия мышки
…
Осталось только сделать группы и включить в них пользователей, чтобы у всех они были в ник-листе по умолчанию. Это совсем не сложно – идем в закладку Groups
Group Summary в левой части страницы, а потом выбираем из списка нужную группу.
На появившейся странице выбираем Enable contact list group sharing.
Далее вводим нужную группу которую создали и ниже нее ставить галочку на Share group with additional users и переключатель на All users.
Все, теперь эта группа будет по-умолчанию у всех пользователей.
Работа над ошибками сервера
ПАТЧ v.7.2(сервера 3.6.4 ) Для прозрачной NTLM авторизации клиентов
2. Далее необходимо подготовить программу клиент
2.1 Pandion — XMPP-клиент для Microsoft Windows. Для работы использует Internet Explorer. Имеется возможность создавать расширения с использованием HTML и JScript.
Самое большое преимущество данного клиента – прозрачная аутентификация Windows.
То есть достаточно подготовить преднастроенный дистрибутив, предоставить пользователю с помощью групповых политик или вручную и вуаля пользователь без дополнительных телодвижений оказывается подключенный к серверу и видит все группы и всех подключенных. настройки берутся путем редактирования файла brand.xml и сохранением под именем override.xml, можно положить рядом с дистрибутивом и программа подхватит настройки.
Работа над ошибками клиента Pandion
Набор заплаток для Pandion (в том числе и недоставка сообщений)