Установка Prosody v0.8 (Jabber-сервер) с аутентификацией из LDAP

    Доброго всем времени суток!
    Все началось с того, что используемый мной OpenFire с нагрузкой из 80 клиентов начал потреблять около 2х Гб оперативной памяти. Ресурсы сервера это конечно позволяли, но я думаю, что все согласятся — это не хорошо. И поскольку в течении продолжительного времени разработчики OpenFire так и не устранили утечки памяти, решено было перейти на другой, более легковесный сервер.
    Следоватьно поставленная задача: поднять непотребный к ресурсам jabber-сервер, с авторизацией по ldap-записям, и со списком контактов из ldap-групп пользователей.
    Из всех вариантов я выбрал Prosody. Но столкнулся с ограниченностью литературы и некоторыми проблемами при установке и настройке. Под катом о том, как это все было преодолено.

    Начал я устанавливать по инструкции пользователя AcidumIrae
    Возникла куча проблем. То saslauthd не хотел подключаться к LDAP, потом оказалось что saslauthd ищет пользователя по ключу userPrincipalName, который в моих записях LDAP просто отсутствует, и создавать его для каждой записи при большом количестве пользователей неудобно. Были еще какие-то мелочи, сейчас уже не помню, но провозился безрезультатно дня два.
    В итоге было решено делать с нуля, без инструкций, используя последние версии Prosody и плагинов.

    Приступим.
    Ставить буду на Debian 6.0, поэтому все операции буду производить под рутом.

    На данный момент в репозитории представлен пакет prosody, но версии 0.7, в которой взаимодействие с LDAP крайне неудобно, собственно из-за чего у меня и возникали проблемы. Потому ставлю пакет версии 0.8 для 64bit-ной системы отсюда. Там же есть инструкции по установке на другие системы. Заодно нам понадобится модуль LuaSec оттуда же.

    # Поскольку дебиан ругнулся на недостающие зависимости, предварительно установлю несколько пакетов
    apt-get install lua5.1 liblua5.1-0 liblua5.1-expat0 liblua5.1-socket2 liblua5.1-filesystem0
    # А теперь, собственно, установка Prosody
    wget http://prosody.im/downloads/debian/prosody_0.8.2-1_amd64.deb
    wget http://prosody.im/downloads/debian/liblua5.1-sec0_0.3.2-2prosody1_amd64.deb
    dpkg -i liblua5.1-sec0_0.3.2-2prosody1_amd64.deb
    dpkg -i prosody_0.8.2-1_amd64.deb
    


    Для взаимодействия с LDAP нам понядобятся несколько модулей Prosody, которые не ставятся из коробки: mod_auth_ldap, mod_storage_ldap, а также библитеки ldap.lib.lua(mod_lib_ldap) и vcard.lib.lua
    Добыл я их здесь — 0-8.prosody-modules.googlecode.com/hg
    Кроме модуля mod_auth_ldap, его скачал с scm.stefant.org/svn/tools/stuff/trunk/patches/prosody, так как другие версии у меня работать корректно не хотели. На всякий случай (вдруг разработчик решит его удалить, однажды он сменил размещение файла, или вдруг случится конец света) суну в спойлер текст модуля
    mod_auth_ldap.lua
    local new_sasl = require "util.sasl".new;
    local nodeprep = require "util.encodings".stringprep.nodeprep;
    local log = require "util.logger".init("auth_ldap");
    
    local ldap_server = module:get_option("ldap_server") or "localhost";
    local ldap_rootdn = module:get_option("ldap_rootdn") or "";
    local ldap_password = module:get_option("ldap_password") or "";
    local ldap_tls = module:get_option("ldap_tls");
    local ldap_base = assert(module:get_option("ldap_base"), "ldap_base is a required option for ldap");
    local ldap_scope = module:get_option("ldap_scope") or "onelevel";
    local ldap_filter = module:get_option("ldap_filter") or "";
    
    local lualdap = require "lualdap";
    local ld = assert(lualdap.open_simple(ldap_server, ldap_rootdn, ldap_password, ldap_tls));
    module.unload = function() ld:close(); end
    
    local function ldap_filter_escape(s) return (s:gsub("[\\*\\(\\)\\\\%z]", function(c) return ("\\%02x"):format(c:byte()) end)); end
    
    local function find_userdn(username)
    	local iter, err = ld:search {
    		base = ldap_base;
    		-- we need to set scope here, else ldap-search may fail (silently!!)
    		scope = ldap_scope;
    		filter = "(&(uid="..ldap_filter_escape(username)..")"..ldap_filter..")";
    	}
    	if not iter then
    	    module:log("error", "LDAP usersearch failed (%s): %s", username, err);
    	    return false;
    	end
    	for dn, attribs in iter do
    	   return dn;
    	end
    	return false;
    end
    
    local provider = { name = "ldap" };
    
    function provider.test_password(username, password)
    	local userdn = find_userdn(username);
    	if not userdn then return false; end
    	
    	local ldu = lualdap.open_simple(ldap_server, userdn, password, ldap_tls);
    	if not ldu then return false; end
    
    	ldu:close();
    	return true;
    end
    
    function provider.user_exists(username)
    	return find_userdn(username);
    end
    
    function provider.get_password(username) return nil, "Passwords unavailable for LDAP."; end
    function provider.set_password(username, password) return nil, "Passwords unavailable for LDAP."; end
    function provider.create_user(username, password) return nil, "Account creation/modification not available with LDAP."; end
    
    function provider.get_sasl_handler()
    	local realm = module:get_option("sasl_realm") or module.host;
    	local testpass_authentication_profile = {
    		plain_test = function(sasl, username, password, realm)
    			local prepped_username = nodeprep(username);
    			if not prepped_username then
    				log("debug", "NODEprep failed on username: %s", username);
    				return "", nil;
    			end
    			return provider.test_password(prepped_username, password), true;
    		end
    	};
    	return new_sasl(realm, testpass_authentication_profile);
    end
    
    module:add_item("auth-provider", provider);
    

    # Перейдем в каталог где лежат модули Prosody и скачаем новые модули туда
    # У меня это /usr/lib/prosody/modules
    cd /usr/lib/prosody/modules
    wget http://scm.stefant.org/svn/tools/stuff/trunk/patches/prosody/mod_auth_ldap.lua
    wget http://0-8.prosody-modules.googlecode.com/hg/mod_storage_ldap/mod_storage_ldap.lua
    wget http://0-8.prosody-modules.googlecode.com/hg/mod_lib_ldap/ldap.lib.lua
    # Библитека vcard.lib.lua должна лежать в каталоге ldap
    mkdir ldap
    cd ldap
    wget http://0-8.prosody-modules.googlecode.com/hg/mod_storage_ldap/ldap/vcard.lib.lua
    

    Теперь настроим конфиги.

    /etc/prosody/prosody.cfg.lua
    1. Включим шифрование для клиента:
    находим опцию "--c2s_require_encryption = false", расскомменчиваем и меняем false на true
    2. Настроим аутентификацию по LDAP:
    находим authentication = «internal_plain» и меняем
    authentication = "ldap";
    ldap_server = "localhost";
    ldap_base = "ou=users,dc=example,dc=com";
    ldap_rootdn = "cn=manager,dc=example,dc=com";
    ldap_password = "password";
    
    Инструкции по данным параметрам — code.google.com/p/prosody-modules/wiki/mod_auth_ldap
    3. Инклюдим конфиг для модуля mod_storage_ldap
    Include '/etc/prosody/prosody-posix-ldap.cfg.lua'
    
    4. Настроим наш виртуальный хост, для этого добавим в секцию Virtual hosts
    VirtualHost "example.com"
            ssl = {
                    key = "/etc/prosody/certs/localhost.key";
                    certificate = "/etc/prosody/certs/localhost.cert";
            }
    

    В итоге у меня получился следующий конфиг:
    /etc/prosody/prosody.cfg.lua
    -- Prosody XMPP Server Configuration
    --
    -- Information on configuring Prosody can be found on our
    -- website at http://prosody.im/doc/configure
    --
    -- Tip: You can check that the syntax of this file is correct
    -- when you have finished by running: luac -p prosody.cfg.lua
    -- If there are any errors, it will let you know what and where
    -- they are, otherwise it will keep quiet.
    --
    
    ---------- Server-wide settings ----------
    -- Settings in this section apply to the whole server and are the default settings
    -- for any virtual hosts
    
    -- This is a (by default, empty) list of accounts that are admins
    -- for the server. Note that you must create the accounts separately
    -- (see http://prosody.im/doc/creating_accounts for info)
    -- Example: admins = { "user1@example.com", "user2@example.net" }
    admins = { }
    
    -- Enable use of libevent for better performance under high load
    -- For more information see: http://prosody.im/doc/libevent
    use_libevent = false;
    
    -- This is the list of modules Prosody will load on startup.
    -- It looks for mod_modulename.lua in the plugins folder, so make sure that exists too.
    -- Documentation on modules can be found at: http://prosody.im/doc/modules
    modules_enabled = {
    
    	-- Generally required
    		"roster"; -- Allow users to have a roster. Recommended ;)
    		"saslauth"; -- Authentication for clients and servers. Recommended if you want to log in.
    		"tls"; -- Add support for secure TLS on c2s/s2s connections
    		"dialback"; -- s2s dialback support
    		"disco"; -- Service discovery
    
    	-- Not essential, but recommended
    		"private"; -- Private XML storage (for room bookmarks, etc.)
    		"vcard"; -- Allow users to set vCards
    		--"privacy"; -- Support privacy lists
    		--"compression"; -- Stream compression
    
    	-- Nice to have
    		"legacyauth"; -- Legacy authentication. Only used by some old clients and bots.
    		"version"; -- Replies to server version requests
    		"uptime"; -- Report how long server has been running
    		"time"; -- Let others know the time here on this server
    		"ping"; -- Replies to XMPP pings with pongs
    		"pep"; -- Enables users to publish their mood, activity, playing music and more
    		"register"; -- Allow users to register on this server using a client and change passwords
    		"adhoc"; -- Support for "ad-hoc commands" that can be executed with an XMPP client
    
    	-- Admin interfaces
    		"admin_adhoc"; -- Allows administration via an XMPP client that supports ad-hoc commands
    		--"admin_telnet"; -- Opens telnet console interface on localhost port 5582
    
    	-- Other specific functionality
    		"posix"; -- POSIX functionality, sends server to background, enables syslog, etc.
    		--"bosh"; -- Enable BOSH clients, aka "Jabber over HTTP"
    		--"httpserver"; -- Serve static files from a directory over HTTP
    		--"groups"; -- Shared roster support
    		--"announce"; -- Send announcement to all online users
    		--"welcome"; -- Welcome users who register accounts
    		--"watchregistrations"; -- Alert admins of registrations
    		--"motd"; -- Send a message to users when they log in
    };
    
    -- These modules are auto-loaded, should you
    -- (for some mad reason) want to disable
    -- them then uncomment them below
    modules_disabled = {
    	-- "presence"; -- Route user/contact status information
    	-- "message"; -- Route messages
    	-- "iq"; -- Route info queries
    	-- "offline"; -- Store offline messages
    };
    
    -- Disable account creation by default, for security
    -- For more information see http://prosody.im/doc/creating_accounts
    allow_registration = false;
    
    -- These are the SSL/TLS-related settings. If you don't want
    -- to use SSL/TLS, you may comment or remove this
    ssl = {
    	key = "/etc/prosody/certs/localhost.key";
    	certificate = "/etc/prosody/certs/localhost.cert";
    }
    
    -- Only allow encrypted streams? Encryption is already used when
    -- available. These options will cause Prosody to deny connections that
    -- are not encrypted. Note that some servers do not support s2s
    -- encryption or have it disabled, including gmail.com and Google Apps
    -- domains.
    
    c2s_require_encryption = true
    --s2s_require_encryption = false
    
    -- Select the authentication backend to use. The 'internal' providers
    -- use Prosody's configured data storage to store the authentication data.
    -- To allow Prosody to offer secure authentication mechanisms to clients, the
    -- default provider stores passwords in plaintext. If you do not trust your
    -- server please see http://prosody.im/doc/modules/mod_auth_internal_hashed
    -- for information about using the hashed backend.
    
    --authentication = "internal_plain"
    authentication = "ldap";
    ldap_server = "localhost";
    ldap_base = "ou=users,dc=example,dc=com";
    ldap_rootdn = "cn=manager,dc=example,dc=com";
    ldap_password = "password";
    --ldap_filter = "(authorizedService=jabber)"; -- optional
    
    Include '/etc/prosody/prosody-posix-ldap.cfg.lua'
    
    -- Select the storage backend to use. By default Prosody uses flat files
    -- in its configured data directory, but it also supports more backends
    -- through modules. An "sql" backend is included by default, but requires
    -- additional dependencies. See http://prosody.im/doc/storage for more info.
    
    --storage = "sql" -- Default is "internal"
    
    -- For the "sql" backend, you can uncomment *one* of the below to configure:
    --sql = { driver = "SQLite3", database = "prosody.sqlite" } -- Default. 'database' is the filename.
    --sql = { driver = "MySQL", database = "prosody", username = "prosody", password = "secret", host = "localhost" }
    --sql = { driver = "PostgreSQL", database = "prosody", username = "prosody", password = "secret", host = "localhost" }
    
    -- Logging configuration
    -- For advanced logging see http://prosody.im/doc/logging
    -- Hint: If you create a new log file or rename them, don't forget 
    -- to update the logrotate config at /etc/logrotate.d/prosody
    log = {
            -- Log all error messages to prosody.err
    	error = "/var/log/prosody/prosody.err";
            -- Log everything of level "info" and higher (that is, all except "debug" messages)
            -- to prosody.log
            info = "/var/log/prosody/prosody.log"; -- Change 'info' to 'debug' for more verbose logging
            --"*syslog"; -- Uncomment this for logging to syslog
    }
    
    -- Pidfile, used by prosodyctl and the init.d script
    pidfile = "/var/run/prosody/prosody.pid";
    
    ----------- Virtual hosts -----------
    -- You need to add a VirtualHost entry for each domain you wish Prosody to serve.
    -- Settings under each VirtualHost entry apply *only* to that host.
    
    VirtualHost "localhost"
    
    VirtualHost "example.com"
    	-- enabled = false -- Remove this line to enable this host
    
    	-- Assign this host a certificate for TLS, otherwise it would use the one
    	-- set in the global section (if any).
    	-- Note that old-style SSL on port 5223 only supports one certificate, and will always
    	-- use the global one.
            ssl = {
                    key = "/etc/prosody/certs/localhost.key";
                    certificate = "/etc/prosody/certs/localhost.cert";
            }
    
    ------ Components ------
    -- You can specify components to add hosts that provide special services,
    -- like multi-user conferences, and transports.
    -- For more information on components, see http://prosody.im/doc/components
    
    ---Set up a MUC (multi-user chat) room server on conference.example.com:
    --Component "conference.example.com" "muc"
    
    -- Set up a SOCKS5 bytestream proxy for server-proxied file transfers:
    --Component "proxy.example.com" "proxy65"
    
    ---Set up an external component (default component port is 5347)
    --
    -- External components allow adding various services, such as gateways/
    -- transports to other networks like ICQ, MSN and Yahoo. For more info
    -- see: http://prosody.im/doc/components#adding_an_external_component
    --
    --Component "gateway.example.com"
    --	component_secret = "password"
    

    Нам нужно создать еще один конфиг для mod_lib_ldap, я скачаю образец и изменю его под свои нужды
    cd /etc/prosody
    wget http://0-8.prosody-modules.googlecode.com/hg/mod_lib_ldap/dev/prosody-posix-ldap.cfg.lua
    chmod 755 prosody-posix-ldap.cfg.lua
    

    Документация и образцы лежат здесь — 0-8.prosody-modules.googlecode.com/hg/mod_lib_ldap/dev
    Приведу сразу образец готового конфига:
    /etc/prosody/prosody-posix-ldap.cfg.lua
    -- Use Include 'prosody-posix-ldap.cfg.lua' from prosody.cfg.lua to include this file
    authentication = 'ldap' -- Indicate that we want to use LDAP for authentication
    --storage        = 'ldap' -- Indicate that we want to use LDAP for roster/vcard storage
    storage = {
            roster = "ldap";
            vcard = "ldap";
    }
    
    ldap = {
       hostname      = 'localhost',                    -- LDAP server location
        bind_dn       = 'cn=manager,dc=example,dc=com', -- Bind DN for LDAP authentication (optional if anonymous bind is supported)
        bind_password = 'password',                      -- Bind password (optional if anonymous bind is supported)
    
        user = {
          basedn        = 'ou=users,dc=example,dc=com', -- The base DN where user records can be found
          filter        = 'objectClass=posixAccount',   -- Filter expression to find user records under basedn
          usernamefield = 'uid',                        -- The field that contains the user's ID (this will be the username portion of the JID)
          namefield     = 'cn',                         -- The field that contains the user's full name (this will be the alias found in the roster)
        },
    
        groups = {
          basedn      = 'ou=groups,dc=example,dc=com', -- The base DN where group records can be found
          memberfield = 'memberUid',                   -- The field that contains user ID records for this group (each member must have a corresponding entry under the user basedn with the same value in usernamefield)
          namefield   = 'cn',                          -- The field that contains the group's name (used for matching groups in LDAP to group definitions below)
    
          {
            name  = 'First Group', -- The group name that will be seen in users' rosters
            cn    = 'first_group', -- This field's key *must* match ldap.groups.namefield! It's the name of the LDAP group this definition represents
            admin = false,      -- (Optional) A boolean flag that indicates whether members of this group should be considered administrators.
          },
          {
            name  = 'Second Group',
            cn    = 'second_group',
            admin = true,
          },
        },
    
        vcard_format = {
          displayname = 'cn', -- Consult the vCard configuration section in the README
          nickname    = 'uid',
        },
    }
    

    В этом конфиге в массиве groups я перечислил группы из LDAP. Теперь пользователь будет видеть всех пользователей относящихся к его группе.
    Но мне надо чтобы пользователь видел всех пользователей, включая тех кто не относится к его группе. Для этого придется внести коррективы в /usr/lib/prosody/modules/mod_storage_ldap.lua. Если у Вас нет такой потребности пропустите этот пункт
    Находим в /usr/lib/prosody/modules/mod_storage_ldap.lua строку
        local filter      = memberfield .. '=' .. tostring(username);
    
    Закомментим и просто объявим переменную, не присваивая значения:
        --local filter      = memberfield .. '=' .. tostring(username);
        local filter;
    

    Проверьте логи (/var/log/prosody) на ошибки.
    Не исключено, что понадобится установить lualdap, и скомпилировать его придется из исходников.
    Для начала надо скачать сырцы lualdap и compat
    # установим пакеты необходимые для компиляции
    apt-get install libldap-dev liblua5.1-0-dev
    # создадим временную директорию и перейдем в нее
    mkdir /tmp/lualdap
    cd /tmp/lualdap
    # скачаем сырцы
    wget http://files.luaforge.net/releases/lualdap/lualdap/LuaLDAP1.1.0/lualdap-1.1.0.tar.gz
    wget http://files.luaforge.net/releases/compat/Compat-5.1/Compat-5.1release5/compat-5.1r5.tar.gz
    # распакуем 
    tar xvfz compat-5.1r5.tar.gz
    tar xvfz lualdap-1.1.0.tar.gz
    

    Теперь надо подрехтовать конфиги, указать правильные пути. Мои конфиги выглядят так:
    lualdap-1.1.0/config
    # Installation directories
    # System's libraries directory (where binary libraries are installed)
    LUA_LIBDIR= /usr/lib/lua/5.1
    # Lua includes directory
    LUA_INC= /usr/include/lua5.1
    # OpenLDAP includes directory
    OPENLDAP_INC= /usr/include
    # OpenLDAP library (an optional directory can be specified with -L<dir>)
    OPENLDAP_LIB= -lldap
    
    # OS dependent
    LIB_OPTION= -shared #for Linux
    #LIB_OPTION= -bundle -undefined dynamic_lookup #for MacOS X
    
    # Lua version number (first and second digits of target version)
    LUA_VERSION_NUM= 500
    LIBNAME= $T.so.$V
    COMPAT_DIR= ../compat-5.1r5
    
    # Compilation parameters
    WARN= -O2 -Wall -fPIC -W -Waggregate-return -Wcast-align -Wmissing-prototypes -Wnested-externs -Wshadow -Wwrite-strings -ansi
    INCS= -I$(LUA_INC) -I$(OPENLDAP_INC) -I$(COMPAT_DIR)
    CFLAGS= $(WARN) $(INCS)
    CC= gcc
    
    # $Id: config,v 1.5 2006/07/24 01:42:06 tomas Exp $
    lualdap-1.1.0/Makefile
    # $Id: Makefile,v 1.30 2007/03/13 22:07:33 godinho Exp $
    
    T= lualdap
    V= 1.1.0
    CONFIG= ./config
    
    include $(CONFIG)
    
    ifeq "$(LUA_VERSION_NUM)" "500"
    COMPAT_O= $(COMPAT_DIR)/compat-5.1.o
    endif
    
    OBJS= src/lualdap.o $(COMPAT_O)
    
    
    src/$(LIBNAME): $(OBJS)
            export MACOSX_DEPLOYMENT_TARGET="10.3"; $(CC) $(CFLAGS) $(LIB_OPTION) -o src/$(LIBNAME) $(OBJS) $(OPENLDAP_LIB)
    
    $(COMPAT_DIR)/compat-5.1.o: $(COMPAT_DIR)/compat-5.1.c
            $(CC) -c $(CFLAGS) -o $@ $(COMPAT_DIR)/compat-5.1.c
    
    install: src/$(LIBNAME)
            mkdir -p $(LUA_LIBDIR)
            cp src/$(LIBNAME) $(LUA_LIBDIR)
            cd $(LUA_LIBDIR); ln -f -s $(LIBNAME) $T.so
    
    clean:
            rm -f $(OBJS) src/$(LIBNAME)
    

    Компилируем и устанавливаем
    make
    make install
    

    На этом все, спасибо за внимание!

    Полезные ссылки
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 4

      +2
      Просто любопытно, если не устраивает скорость OF (что в общем-то не удивительно, он какой угодно, но только не быстрый), почему нельзя поставить прекрасный ejabberd, который работает из коробки (да, LDAP тоже) и забыть об этой проблеме?
        –1
        ejabberd не очень удобен, плохо развивается и erlang напрягает слегка
          +3
          Насчет неудобства — ИМХО спорно, все равно рулить им надо через XMPP, а он везде одинаковый. Насчет плохого развития ничего не могу сказать, так как все функции, которые мне когда-либо были нужны от XMPP server — работали без каких-либо проблем и я помню только один инцедент с безопасностью за всю историю его использования. А если все работает — что с ним делать? Насчет erlang — ИМХО эрланг как раз идеально подходит для написания подобных сервисов. ОК, эрланг напрягает, а луа — нет?
            0
            я скажу больше,ejabberd можно сделать в кластере, в отличие от Prosody.
            По крайне мере на тот момент, когда смотрел я.

      Only users with full accounts can post comments. Log in, please.