Ejabberd на FreeBSD с авторизацией в Active Directory

Установка ничем особенным не отличается, как обычно, собрал из портов.
А вот конфигурация весьма интересная и отличается крайней невменяемостью, которую ejabberd унаследовал от языка программирования, на котором он написан — erlang. Многие админы сломали голову в попытках настроить данный софт, и часто совсем отказываются от его использования в пользу jabberd2 или openfire. Я считаю что напрасно, но это только мое ИМХО.
Итак, для настройки достаточно создать пару конфигурационных файлов и прописать автостарт в rc.conf. Также необходимо создать учетку в AD которая имеет права на чтение LDAP базы домена my.domain.local, в моем случае это — CN=for_jabber,OU=it,DC=my,DC=domain,DC=local
Также нужен pem файл который содержит сертификат и ключ для шифрования сетевой деятельности между клиентами и сервером. Ничего особенного в нем нету, стандартное содержимое формата:

-----BEGIN CERTIFICATE-----
....cert here....
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
....key here....
-----END RSA PRIVATE KEY-----

Листинг конфига /usr/local/etc/ejabberd/inetrc

{lookup,["file","native"]}.
{host,{127,0,0,1}, ["localhost","hostalias"]}.
{file, resolv, "/etc/resolv.conf"}.

Листинг главного конфига ejabberd — /usr/local/etc/ejabberd/ejabberd.cfg

override_global.
override_local.
override_acls.

{loglevel, 2}.

% Ports and services
{listen, [

        % Client to server
        {5222, ejabberd_c2s, [
                starttls_required, {certfile, "/usr/local/etc/ejabberd/jabber.pem"},
                zlib
        ]},

        % HTTP service
        {5280, ejabberd_http, [
                web_admin
        ]}

]}.

% Webadmin access
{acl, admins, {user, "my_ad_login", "my.domain.local"}}.
{access, configure, [{allow, admins}]}.

% Host config
{hosts, ["my.domain.local"]}.

%
% HOST: my.domain.local
%

{host_config, "my.domain.local", [

        {auth_method, ldap},
        {ldap_servers, ["my.domain.local"]},
        {ldap_rootdn, "CN=for_jabber,OU=it,DC=my,DC=domain,DC=local"},
        {ldap_password, "12345609876"},
        {ldap_base, "DC=my,DC=domain,DC=local"},
        {ldap_uids, [{"sAMAccountName", "%u"}]},
        {ldap_filter, "(&(objectCategory=person)(objectClass=user))"}

]}.

{language, "ru"}.

{access, muc_admin,  [{allow, admins}]}.
{access, muc_access, [{allow, all}]}.
{access, muc_create, [{allow, admins}]}.
{access, muc_log,    [{allow, admins}]}.

%%%.   =======
%%%'   MODULES

%%
%% Modules enabled in all ejabberd virtual hosts.
%%
{modules,
 [
  {mod_adhoc,    []},
  {mod_announce, [{access, announce}]}, % recommends mod_adhoc
  {mod_blocking,[]}, % requires mod_privacy
  {mod_caps,     []},
  {mod_configure,[]}, % requires mod_adhoc
  {mod_disco,    []},
  %%{mod_echo,   [{host, "echo.localhost"}]},
  {mod_irc,      []},
  {mod_http_bind, []},
  %%{mod_http_fileserver, [
  %%                       {docroot, "/var/www"},
  %%                       {accesslog, "/var/log/ejabberd/access.log"}
  %%                      ]},
  {mod_last,     []},
  {mod_muc,      [
                  {host, "conference.@HOST@"},
                  {access, muc_access},
                  {access_create, muc_create},
                  {access_persistent, muc_create},
                  {access_admin, muc_admin},
                  {default_room_options,
                        [
                         {logging, true}
                        ]}
                 ]},
  {mod_muc_log,  [{access_log, muc_log}, {cssfile, false}, {dirname, room_jid}, {outdir, "/usr/local/www/ejabberd/logs_muc"}]},
  {mod_log_chat, [{path, "/usr/local/www/ejabberd/logs_chat"}, {format, html}]},
  {mod_offline,  [{access_max_user_messages, max_user_offline_messages}]},
  {mod_ping,     []},
  %%{mod_pres_counter,[{count, 5}, {interval, 60}]},
  {mod_privacy,  []},
  {mod_private,  []},
%% Proxy Server for file transfer over NAT %%  
  {mod_proxy65,  [
                {host, "proxy.my.domain.local"}, %% defines the Jabber ID of service.
                {ip, {0,0,0,0}}, %% 127.0.0.1 by default, make it 0.0.0.0 to listen on all interfaces, or the ip of specific interface
                {hostname, "my.domain.local"}, % useful service run behind a NAT. default is ip option. Ex: "proxy.mydomain.org", "200.150.100.50"
                {port, 7777}, %% Default, we don't need to add
                {access, all} %% Default, we don't need to add
                 ]},
  {mod_pubsub,   [
                  {access_createnode, pubsub_createnode},
                  {ignore_pep_from_offline, true}, % reduces resource comsumption, but XEP incompliant
                  %%{ignore_pep_from_offline, false},  % XEP compliant, but increases resource comsumption
                  {last_item_cache, false},
                  {plugins, ["flat", "hometree", "pep"]}  % pep requires mod_caps
                 ]},
  {mod_register, [
                  %%
                  %% Protect In-Band account registrations with CAPTCHA.
                  %%
                  %%{captcha_protected, true},

                  %%
                  %% Set the minimum informational entropy for passwords.
                  %%
                  %%{password_strength, 32},

                  %%
                  %% After successful registration, the user receives
                  %% a message with this subject and body.
                  %%
                  {welcome_message, {"Welcome!",
                                     "Hi.\nWelcome to this XMPP server."}},

                  %%
                  %% When a user registers, send a notification to
                  %% these XMPP accounts.
                  %%
                  %%{registration_watchers, ["admin1@example.org"]},

                  %%
                  %% Only clients in the server machine can register accounts
                  %%
                  {ip_access, [{allow, "127.0.0.0/8"},
                               {deny, "0.0.0.0/0"}]},

                  %%
                  %% Local c2s or remote s2s users cannot register accounts
                  %%
                  %%{access_from, deny},

                  {access, register}
                 ]},
  %%{mod_register_web, [
                  %%
                  %% When a user registers, send a notification to
                  %% these XMPP accounts.
                  %%
                  %%{registration_watchers, ["admin1@example.org"]}
  %%             ]},
  {mod_roster,   []},
  %%{mod_shared_roster,[]},
  {mod_shared_roster_ldap, [
          {ldap_filter, "(&(objectCategory=person)(objectClass=user))"},
          {ldap_base, "OU=Organisation,DC=my,DC=domain,DC=local"},
   %%     {ldap_rfilter, "(&(sAMAccountType=805306368)(!(memberOf=CN=Some_not_real_users,OU=Users_All,OU=Organisation,DC=my,DC=domain,DC=local)))"},
          {ldap_rfilter, "(&(objectClass=person)(objectClass=user))"},
          {ldap_groupattr, "department"},
          {ldap_groupdesc, "department"},
          {ldap_memberattr, "sAMAccountName"},
          {ldap_userdesc, "cn"}
        ]},
  %%{mod_service_log,[]},
  {mod_shared_roster,[]},
  {mod_stats,    []},
  {mod_time,     []},
  %%{mod_vcard,    []},

{mod_vcard_ldap,
   [
    %% We use the same server and port, but want to bind anonymously because
    %% our LDAP server accepts anonymous requests to
    %% "ou=AddressBook,dc=example,dc=org" subtree.
    {ldap_rootdn, "CN=for_jabber,OU=it,DC=my,DC=domain,DC=local"},
    {ldap_password, "12345609876"},
    %% define the addressbook's base
    {ldap_base, "OU=Organisation,DC=my,DC=domain,DC=local"},
    %% uidattr: user's part of JID is located in the "mail" attribute
    %% uidattr_format: common format for our emails
    %%%%%%{ldap_uids, [{"mail","%u@myco.ua"}]},
    %% We have to define empty filter here, because entries in addressbook does not
    %% belong to shadowAccount object class
    %%{ldap_filter, "(&(sAMAccountType=805306368)(!(memberOf=CN=Some_not_real_users,OU=Users_All,OU=Organisation,DC=my,DC=domain,DC=local)))"},
    {ldap_filter, "(&(objectClass=person)(objectClass=user))"},
    %% Now we want to define vCard pattern
    {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", ["streetaaadress"]},
       {"REGION", "%s", ["st"]},
       {"PCODE", "%s", ["postalCode"]},
       {"TITLE", "%s", ["title"]},
       {"URL", "%s", ["wWWHomePage"]},
       {"DESC", "%s", ["description"]},
       {"TEL", "%s, office: %s", ["mobile", "telephoneNumber"]}]},
    {ldap_search_fields,
      [{"User", "%u"},
       {"Name", "givenName"},
       {"Family Name", "sn"},
       {"Email", "mail"},
       {"Company", "company"},
       {"Department", "department"},
       {"Role", "title"},
       {"Description", "description"},
       {"Phone", "mobile"}]},
    {ldap_search_reported,
      [{"Full Name", "FN"},
       {"Nickname", "NICKNAME"},
       {"Email", "EMAIL"}]}
  ]},

  {mod_version,  [
        {show_os, true}
  ]}
 ]}.

Со стороны клиента настройки должны быть такие:

    server — server FQDN
    domain — my.domain.local
    username — AD sAMAccountName
    password — AD password
    Шифрование — обязательно!

Также в данном конфиге используется модуль которого нету в стандартной поставке сервера — mod_log_chat, его необходимо собирать самому и подключать отдельно. Как это делается я писал ранее.
Не побрезгуйте ознакомиться с официальной документацией, она, на удивление, очень подробная с множеством примеров и разъяснений.
Enjoy!

  1. Комментов пока нет

  1. Трэкбэков пока нет.

Why ask?