ПРАВИЛЬНАЯ инциализация OpenLDAP-сервера с динамической конфигурацией

LDAP
Положим, перед нами стоит задача «с нуля» настроить новый сервер каталогов, при этом использование безнадёжно устаревшего метода конфигурации, — редактирование slapd.conf, — полностью исключено, поскольку завещано нашими предками: «Делай хорошо, а плохо само получится!»
Исходные данные: есть установленный любым путём OpenLDAP-сервер и стандартные утилиты к нему (во многих дистрибутивах по каким-то странным соображениям клиентская и серверная части OL разнесены по разным пакетам, так что в этом случае вам необходимо установить и то, и другое)
Требуется: создать рабочую конфигурацию LDAP-каталога и добавить суффикс dc=example,dc=com, при этом использовать slapd.conf на любом этапе нельзя (мы же твёрдо решили отказаться от этого анахронизма).

Здесь перед вами встаёт дилема: раз нельзя использовать slapd.conf, значит нужно написать конфигурацию каким-либо иным образом. А поскольку известно, что физически на файловой системе дерево конфигурации представляет собой структуру каталогов со вложенными ldif-файлами, то первое, что приходит на ум — это изучение 5-й главы «Руководства администратора OpenLDAP» на предмет того, как бы написать все необходимые LDIF'ы самому (и при этом не сойти с ума, узнав, что operationalAttributes для cn=config никто не отменял). Когда я первый раз решил настроить свой сервер каталогов через cn=config и попытался создать всё дерево настроек вручную, скурпулёзно следуя этой самой 5-й главе, я потерпел фиаско, при этом с пользой потратив время на изучение внутреннего устройства конфигурации. Оказалось, что такой подход — неправильный. Дальнейшее изучение документации, в том числе и man-страниц (для проекта OpenLDAP вообще характерно, что в man'ах можно найти значительно более обстоятельную информацию, нежели в «Руководстве администратора»;) привело меня к тому, что можно создать некий простенький стартаповый файл конфигурации slapd.conf, который slapd при запуске со своеобразным «секретным» сочетанием параметров -f <путь_к_slapd.conf> -F <каталог_для_cn=config> — преобразует к формату cn=config'а (впрочем, он это в любом случае делает при каждом запуске) и сам выложит в «каталог_для_cn=config» всю нужную мне структуру. Таким образом я действительно добился результата, в заботливо созданном и подсунутом slapd в параметре -F каталоге и правда появилась структура с нужными ldif'ами, но… при этом получалось, что мне так и не удалось избавиться от slapd.conf, ведь на этапе инициализации он в минималистичном виде, но всё же понадобился. А хотелось не прибегать к помощи slapd.conf'а совсем, к тому же по всем признакам было понятно, что сами разработчики OpenLDAP'а не используют этот неудобный и явно устаревший метод. «А ларчик просто открывался» — решение, оказалось, лежало на поверхности!
Итак, всё НАМНОГО элементарнее, чем вы думали, если вы над этим уже думали, мой дорогой читатель, на что я, честно говоря, очень рассчитываю.
Перво-наперво нужно создать каталог для cn=config, он может называться как угодно и находиться по сути дела где угодно, лишь бы файловая система была доступна на запись и позволяла устанавливать права доступа для UNIX'овых пользователей. Например:
mkdir -p /etc/openldap/slapd.d/example

После создания каталога вам нужно определиться с паролем, который будет использоваться для доступа к конфигурации. Используйте утилиту slappasswd (на самом деле это тот же slapd) для получения шифрованного пароля:
slappasswd -h '{<МЕТОД_ХЕШИРОВАНИЯ>}' -s 'ПАРОЛЬ'

Например:
Команда: slappasswd -h '{MD5}' -s 'topsecret'
Вывод: {MD5}6oR5iLpZcn2/TjTudXJtww==

Если вы немного параноик (что в принципе хорошо) и не привыкли оставлять не предназначенные для чужих глаз сведения в своём history, можете не указывать пароль в открытом виде и опустить опцию -s, но тогда вам придётся два раза вводить одно и то же, как в утилите passwd.
Скопируйте вывод команды slappasswd, например, просто выделив его мышью в окне терминала. Теперь вам предстоит… да-да, написать начальную конфигурацию, которая разрешила бы классическую дилему с курицей и яйцом, возникающую из-за того, что наполнить дерево cn=config можно только после запуска сервера, а сервер не запустить, если для него вообще нет конфигурационной информации.
Но это делается на самом деле даже проще, чем со slapd.conf'ом. Итак, открываем любимый текстовый редактор и пишем:
dn: cn=config
objectClass: olcGlobal
cn: config

dn: olcDatabase={0}config,cn=config
objectClass: olcDatabaseConfig
olcDatabase: {0}config
olcRootPW: ШИФР_ПАРОЛЬ

Собственно на место ШИФР_ПАРОЛЬ нужно вставить тот вывод slappasswd, который вы перед этим скопировали.
Вуаля, начальная конфигурация готова, её можно сохранить в файл — например, под именем startup-config.ldif. Теперь всё готово для того, чтобы в правильном формате «скормить» этот файл LDAP-серверу, при этом не запуская его. Для добавления данных в напрямую в бэкенды OpenLDAP, а не через посредство сервера, то есть в режиме оффлайн, предназначена утилита slapadd (опять же, на самом деле это просто ссылка на slapd, имя которой является своеобразным неявным параметром, определяющим, что данному бинарнику следует делать). Шаблон начальной конфигурации, в котором, заметьте, нашего пока ничего, кроме пароля, нет, загружается следующим образом:
slapadd -n 0 -F <КАТАЛОГ_ДЛЯ_cn=config> -l <ФАЙЛ_НАЧАЛЬНОЙ_КОНФИГУРАЦИИ>

Обратите внимание на то, что мы обязательно должны передать утилите slapadd параметр -n 0, что означает «добавить данные в базу номер ноль». Посмотрите на файл начальной конфигурации: в нём описание собственно самой базы cn=config находится в dn: olcDatabase={0}config,cn=config. Следует обратить внимание на число 0, указанное в фигурных скобках — это собственно тот самый индекс базы (для cn=config всегда равен нулю), который мы затем указываем в параметре -n для slapadd. Делать это явным образом нужно потому, что по умолчанию slapadd пытается добавить данные в базу, соответствующую первому инициализированному суффиксу с индексом больше единицы (обычно это уже собственно содержимое каталога, то есть пользовательские данные).
Итак, для примера считаем, что под нужды cn=config мы создали каталог /etc/openldap/slapd.d/example, тогда команда может выглядеть так:
Команда: slapadd -n 0 -F /etc/openldap/slapd.d/example -l ПУТЬ/К/ФАЙЛУ/startup-config.ldif
Вывод: _#################### 100.00% eta none elapsed none fast!
Closing DB...

Убедитесь в том, что теперь каталог cn=config'а не пуст, выполнив в нём ls -lR. Если вы видите структуру каталогов со вложенными ldif'ами, значит всё в порядке, в противном случае перечитайте всё изложенное мной выше и попытайтесь понять, что вы сделали не так :) Вроде бы всё сделали, но остаётся один маленький штришок: поскольку все предыдуще действия были произведены под суперпользователем, а OpenLDAP в целях безопасности должен запускаться с правами собственной учётной записи, если сейчас просто взять и запустить LDAP-сервер указав ему в параметре -F только что инициализированную нами базу cn=config, он не сможет получить доступ к файлам — ведь они принадлежат root и на них утилитой slapadd предусмотрительно установлена маска доступа со сброшенными битами для «группы» и «остальных». Поэтому в каталоге с базой cn=config'а нужно рекурсивно поменять владельца на того пользователя, с правами которого будет запускаться сервер. Для того, чтобы узнать имя этого самого пользователя, посмотрите вывод getent passwd | fgrep -i ldap, скорее всего его так и зовут: «ldap», — хотя в Debian'е, например, традиционно уже решили соригинальничать и назвали его openldap, что не суть важно (в особенности, если вы умеете быстро печатать десятипальцевым методом, который ваш покорный слуга так до сей поры чудесной и не освоил).
В примере после chown -R ldap.ldap /etc/openldap/slapd.d/example мы имеем готовую работоспособную конфигурацию, пусть и простейшую и теперь мы можем запустить сам сервер:
slapd -F /etc/openldap/slapd.d/example -u ldap -g ldap -h ldap:///

Итак, если вы точно следовали моим рекомендациям, уже сейчас у вас есть сервер OpenLDAP, который больше не нужно перезапускать, все оставшиеся действия по настройке (подключение модулей, расширение схемы, добавление суффиксов) вы будете делать только в режиме «онлайн», изменяя конфигурацию на работающем сервере!

Сделав наш сервер максимально гибким и конфигурируемым благодаря динамическому дереву настроек cn=config, мы конечно же не можем не вопользоваться теперь предоставленными нам широчайшими возможностями для того, чтобы создать суффикс dc=example,dc=net (ведь именно это нам в конечном счёте и нужно, не правда ли?).
Но дабы вы не наступали всё на те же чудные грабли, на которые наступал я и не раз, даже не надейтесь на то, что хотя бы минимально необходимые атрибуты и классы объектов сейчас есть в составе автоматически созданного сервером дерева настроек. Да оно в общем и не мудрено — ведь прописывали же вы все так называемые файлы схем в древнем, как сама история файлике slapd.conf? И ведь там даже core.schema нужно было явным образом указывать, иначе вообще ничего у вас не заработало бы. Ну так я могу вас обрадовать тем, что в случае с cn=config определённый прогресс уже есть и то, что лежит в core по умолчанию будет лежать у вас в cn=schema,cn=config. Всё остальное же нужно добавлять самим. При этом очевидно, что поскольку сама по себе схема каталога интегрирована в дерево cn=config, то и добавлять её составляющие нужно будет как обычные LDIF-файлы: ведь именно то, что мы видим в дереве конфигурации — это и есть представление данных именно в том виде, в котором их читает сервер, эта конфигурация уже не интерпретируется, как раньше происходило с файлом slapd.conf, где include'ы считывались в процессе парсинга. Но где же взять файлы нужных схем в формате LDIF, спросите вы? Вопрос не праздный, потому что никаких утилит преобразования файлов schema в LDIF'ы в поставке OpenLDAP'а нет (что, вообще говоря, довольно странно и вообще вряд ли сей факт имеет какое-о разумное объяснение с учётом того, что до сих пор большинство кустарных схем к огромному количеству самого разного софта так и поставляется в старом формате). Если вы внимательно рассмотрите содержимое каталога /etc/openldap/schema (соответственно, это может быть другой каталог, в зависимости от того, что у вас за система и/или какое значение prefix вы задали на этапе конфигурирования), то найдёте там несколько важных (если не сказать ключевых) и наиболее часто используемых файлов схем в формате LDIF. Любые другие схемы можно добавить, переконвертировав их из schema в ldif довольно простым скриптом на bash, который я приведу чуть позже. А пока давайте просто добавим схемы в том порядке, в котором их взаимные зависимости будут удовлетворены. Поскольку в поставке OpenLDAP'а таких схем маловато, пока что можно особо не заморачиваться взаимозависимостями и сделать это так, как делал я:
while read s; do ldapadd -x -D 'cn=config' -w 'ПАРОЛЬ_ДЛЯ_cn=config' -f $s; done < <( find /etc/openldap/schema/ -type f -name '*.ldif' -a ! -name 'core.ldif' ) 

Собственно, вся «хитрость» данного подхода заключается в том, что схемы в алфавитном порядке имён файлов грузятся правильно, а если их попытаться «скормить» серверу том порядке, в котором их находит find, возникают конфликты неразрешённых зависимостей. В общем, это всё не суть важно, можно и руками загружать при желании.

Что ж, теперь у нас есть базовый набор схем и можно приступать к настройке суффикса. Впрочем, постойте, а поверх чего этот суффикс будет работать? Ведь наверняка же поверх бэкендов BDB или HDB? А модули для этих бэкендов загружены или, возможно, вполне возможно, всё необходимое статически скомпилировано со slapd? Для того, чтобы узнать, был ли slapd собран полностью статично, попробуйте следующий запрос:
ldapsearch -xLLL -b 'cn=subschema' -s base '(objectClass=*)' '+' | fgrep olcModuleList

Если в ответ на этот запрос вы получите строку, содержащую olcModuleList — значит у вас модульная конфигурация, если же вы не увидите ничего, значит slapd скомпилирован полностью статически и без поддержки загружаемых модулей.

Для того, чтобы точно узнать, требуются от вас какие-либо дополнительные действия или нет, действуйте по принципу доказательства от противного: попробуйте загрузить LDIF для нового пространства имён (суффикса), например:

dn: olcDatabase=bdb,cn=config
objectClass: top
objectClass: olcDatabaseConfig
objectClass: olcBdbConfig
olcDatabase: bdb
olcSuffix: dc=example,dc=com
olcRootDN: cn=manager,dc=example,dc=com
olcRootPW: ШИФР_ПАРОЛЬ
olcDbDirectory: /var/lib/ldap

Надеюсь, для тех, кто хоть раз настраивал сервер OpenLDAP, всё достаточно очевидно. ШИФР_ПАРОЛЬ сформируйте командой slappasswd по аналогии с тем, как вы это делали для cn=config. В olcDbDirectory прописывается путь к каталогу, в котором будут размещаться файлы базы данных-бэкенда (например, Berkeley DB), то есть это аналог директивы directory в slapd.conf. Если при попытке добавить данный LDIF в дерево конфигурации вы получаете ошибку, свидетельстующую об отсутствии objectClass'а olcBdbConfig, значит, либо не загружен соответствующий модуль (но для этого предыдущая проверка должна была пройти успешно и objectClass olcModuleList присутствует в текущей схеме), либо кто-то (возможно, вы сами) умудрился статически скомпилировать slapd без поддержки Berkeley DB. Аналогично и в случае с HDB.
Если выяснилось, что вам нужно подгрузить модуль, в соответствии с п.п. 5.2.2 «Руководства администратора», добавьте следующий LDIF:
dn: cn=module,cn=config
objectClass: olcModuleList
cn: module
olcModuleLoad: ПУТЬ/К/МОДУЛЮ/back_bdb.la

Заметьте, что индексы в фигурных скобках в начале RDN (как тот же «cn={0}module» в Руководстве) на самом деле генерируются сервером автоматически, их нужно указывать явно _только_ в особых случаях (например, когда нужно «раздвинуть» существующие индексы, вставив новый элемент).
Стоит особо отметить, что при истинно модульной конфигурации OpenLDAP, подобной модульной сборке ядра операционной системы, все компоненты по возможности находятся в отдельных подключаемых во время исполнения бинарных файлах с расширением «la». В этих случаях иногда требуется подключить не один-два модуля, а, например, десяток или даже больше. Понятное дело, что каждый раз указывать полный путь к файлам модулей в директиве olcModuleLoad в этом случае несколько нерационально, в особенности если у вас все la-шные файлы лежат в одном и том же каталоге. В этом случае можно добавить директиву olcModulePath, которая выполняет ту же функцию, что и системная переменная PATH — определяет пути поиска файлов по умолчанию. В этом случае LDIF для добавления нужных модулей может выглядеть так:
dn: cn=module,cn=config
objectClass: olcModuleList
cn: module
olcModulePath: /usr/lib/ldap/bin:/usr/share/samba/ldap/modules:<и т.д.>
olcModuleLoad: back_bdb.la
olcModuleLoad: back_hdb.la
olcModuleLoad: back_sock.la
olcModuleLoad: <ещё_один_модуль>.la

2 комментария

avatar
Спасибо за статью. Да и за блог вцелом.

После начальной конфигурации slapd не стартанул: ругнулся что нет pid-файла.
Изменил вот так:
avatar
По ctrl+v ушел комент (
dn: cn=config
objectClass: olcGlobal
cn: config
olcPidFile: /var/run/slapd/slapd.pid
dn: olcDatabase={0}config,cn=config
objectClass: olcDatabaseConfig
olcDatabase: {0}config
olcRootPW: ШИФР_ПАРОЛЬ
запустился! Может пометите в статье пару слов о pid-файле?

fgrep olcModuleList выдал строчку, но ldif на создание нового суффикса все равно ругается:
adding new entry «olcDatabase=bdb,cn=config»
ldap_add: Invalid syntax (21)
additional info: objectClass: value #2 invalid per syntax

Debian 6.0 2.6.32 Когда сделал apt-get install из оф репа, была такая структура:
slapd.d/:
total 8
drwxr-x--- 3 openldap openldap 4096 Oct 24 15:24 cn=config
-rw------- 1 openldap openldap 407 Oct 24 15:24 cn=config.ldif

slapd.d/cn=config:
total 28
-rw------- 1 openldap openldap 383 Oct 24 15:24 cn=module{0}.ldif
drwxr-x--- 2 openldap openldap 4096 Oct 24 15:24 cn=schema
-rw------- 1 openldap openldap 325 Oct 24 15:24 cn=schema.ldif
-rw------- 1 openldap openldap 343 Oct 24 15:24 olcBackend={0}bdb.ldif
-rw------- 1 openldap openldap 472 Oct 24 15:24 olcDatabase={0}config.ldif
-rw------- 1 openldap openldap 1012 Oct 24 15:24 olcDatabase={1}bdb.ldif
-rw------- 1 openldap openldap 586 Oct 24 15:24 olcDatabase={-1}frontend.ldif

slapd.d/cn=config/cn=schema:
total 40
-rw------- 1 openldap openldap 15474 Oct 24 15:24 cn={0}core.ldif
-rw------- 1 openldap openldap 11308 Oct 24 15:24 cn={1}cosine.ldif
-rw------- 1 openldap openldap 6438 Oct 24 15:24 cn={2}nis.ldif
-rw------- 1 openldap openldap 2802 Oct 24 15:24 cn={3}inetorgperson.ldif
Там вроде есть Database bdb… Вот и не знаю почему ошибка :(
avatar
Подключение модуля решило проблему.
Создаем файл module.ldif:
dn: cn=module,cn=config
objectClass:olcModuleList
cn:module
olcModuleLoad:/usr/lib/ldap/back_bdb.la


В /usr/lib/ldap/ есть и другие модули, если необходимо.
Добавляем его в конфигурацию:
ldapadd -x -D 'cn=config' -w '1q2w3e' -f module.ldif

Теперь можно загрузить LDIF для нового пространства имён.
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.