4. Всё сразу: пример множественной настройки

Содержание

Постановка задачи

Предпосылки: В организации, сведения о которой ведутся в нашем каталоге, два отдела: Developers и Designers. Информация о принадлежности пользователя отделу фиксируется как значение атрибута ou в учётной записи каждого пользователя. На почтовом сервере организации обслуживаются почтовые ящики пользователей, адреса электронной почты фиксируются как значение атрибута mail в учётной записи каждого пользователя.

Задача: Требуется создать списки рассылки, которые будут использоваться почтовым сервером для организации рассылки писем всем сотрудникам организации и отдельно сотрудникам каждого подразделения. Кроме того, для разграничения доступа различных приложений требуется организовать объекты POSIX-групп и классических групп для имеющихся отделов. Списки рассылки и групповые объекты должны постоянно отражать актуальную структуру организации.

Решение

В этом примере мы объединили три предыдущие задачи, чтобы продемонстрировать возможность настройки наложения dynlist для поддержки в одном каталоге динамических объектов различных типов. Для разнообразия мы построим наши классические группы на другом объектном классе и с другим атрибутом, в который будет помещаться LDAP URI с параметрами вторичного поиска. Пользуясь тем, что настройки наложения dynlist хранятся в индексированных (X-ORDERED) атрибутах olcDLattrSet, в этом примере мы немного больше внимания уделим работе с атрибутами такого типа.

Начинать, как всегда, будем с исходного положения каталога. Порядок работы будет такой: модификация записей пользователей, добавление записей динамических объектов, настройка наложения.

Шаг 1. Модификация учётных записей пользователей

Сначала дополним учётные записи каталога перечисленными в предпосылках к задаче атрибутами с признаком принадлежности сотрудника к тому или иному отделу, а также адресом электронной почты:

dn: uid=antonova,ou=People,dc=mycompany,dc=ru
changetype: modify
add: ou
ou: Designers
-
add: mail
mail: antonova@mycompany.ru

dn: uid=ivanov,ou=People,dc=mycompany,dc=ru
changetype: modify
add: ou
ou: Developers
-
add: mail
mail: ivanov@mycompany.ru

dn: uid=petrov,ou=People,dc=mycompany,dc=ru
changetype: modify
add: ou
ou: Developers
-
add: mail
mail: petrov@mycompany.ru

dn: uid=sidorov,ou=People,dc=mycompany,dc=ru
changetype: modify
add: ou
ou: Designers
-
add: mail
mail: sidorov@mycompany.ru

Применим изменения и посмотрим, в каком состоянии будут записи пользователей после модификации:

$ ldapmodify -x -D cn=manager,ou=System,dc=mycompany,dc=ru -W -f ./002-add_users_attributes.ldif
Enter LDAP Password:
modifying entry "uid=antonova,ou=People,dc=mycompany,dc=ru"

modifying entry "uid=ivanov,ou=People,dc=mycompany,dc=ru"

modifying entry "uid=petrov,ou=People,dc=mycompany,dc=ru"

modifying entry "uid=sidorov,ou=People,dc=mycompany,dc=ru"

$ ldapsearch -xLLL -b ou=People,dc=mycompany,dc=ru -s one
dn: uid=ivanov,ou=People,dc=mycompany,dc=ru
objectClass: inetOrgPerson
uid: ivanov
cn: Ivan Ivanov
sn: Ivanov
ou: Developers
mail: ivanov@mycompany.ru

dn: uid=petrov,ou=People,dc=mycompany,dc=ru
objectClass: inetOrgPerson
uid: petrov
cn: Petr Petrov
sn: Petrov
ou: Developers
mail: petrov@mycompany.ru

dn: uid=sidorov,ou=People,dc=mycompany,dc=ru
objectClass: inetOrgPerson
uid: sidorov
cn: Sidor Sidorov
sn: Sidorov
ou: Designers
mail: sidorov@mycompany.ru

dn: uid=antonova,ou=People,dc=mycompany,dc=ru
objectClass: inetOrgPerson
uid: antonova
cn: Antonina Antonova
sn: Antonova
ou: Designers
mail: antonova@mycompany.ru

Интересующие нас атрибуты на месте. Переходим к следующему этапу.

Шаг 2. Добавление записей динамических списков и групп

Наложение ещё не настроено, но ничто не мешает нам добавить записи, которые в дальнейшем станут динамическими объектами. По условиям задачи мы создадим списки рассылки в отдельном контейнере MailingLists, POSIX-группы в отдельном контейнере PosixGroups и классические группы в уже имеющемся контейнере Groups:

# Контейнер для списков рассылки
dn: ou=MailingLists,dc=mycompany,dc=ru
objectClass: organizationalUnit
ou: MailingLists

# Списки рассылки
dn: cn=All,ou=MailingLists,dc=mycompany,dc=ru
objectClass: groupOfURLs
cn: All
memberURL: ldap:///ou=People,dc=mycompany,dc=ru?mail?one?(objectClass=inetOrgPerson)

dn: cn=Developers,ou=MailingLists,dc=mycompany,dc=ru
objectClass: groupOfURLs
cn: Developers
memberURL: ldap:///ou=People,dc=mycompany,dc=ru?mail?one?(&(objectClass=inetOrgPerson)(ou=Developers))

dn: cn=Designers,ou=MailingLists,dc=mycompany,dc=ru
objectClass: groupOfURLs
cn: Designers
memberURL: ldap:///ou=People,dc=mycompany,dc=ru?mail?one?(&(objectClass=inetOrgPerson)(ou=Designers))

# Контейнер для POSIX-групп
dn: ou=PosixGroups,dc=mycompany,dc=ru
objectClass: organizationalUnit
ou: PosixGroups

# POSIX-группы отделов
dn: cn=Developers,ou=PosixGroups,dc=mycompany,dc=ru
objectClass: posixGroup
objectClass: labeledURIObject
cn: Developers
gidNumber: 10001
labeledURI: ldap:///ou=People,dc=mycompany,dc=ru?uid?one?(&(objectClass=inetOrgPerson)(ou=Developers))

dn: cn=Designers,ou=PosixGroups,dc=mycompany,dc=ru
objectClass: posixGroup
objectClass: labeledURIObject
cn: Designers
gidNumber: 10002
labeledURI: ldap:///ou=People,dc=mycompany,dc=ru?uid?one?(&(objectClass=inetOrgPerson)(ou=Designers))

# Группы отделов
dn: cn=Developers,ou=Groups,dc=mycompany,dc=ru
objectClass: organizationalRole
objectClass: extensibleObject
cn: Developers
memberURL: ldap:///ou=People,dc=mycompany,dc=ru??one?(&(objectClass=inetOrgPerson)(ou=Developers))

dn: cn=Designers,ou=Groups,dc=mycompany,dc=ru
objectClass: organizationalRole
objectClass: extensibleObject
cn: Designers
memberURL: ldap:///ou=People,dc=mycompany,dc=ru??one?(&(objectClass=inetOrgPerson)(ou=Designers))

Ничего принципиально нового в динамических объектах этого примера нет. Списки рассылки, как и в первом примере, строятся на объектном классе groupOfURLs, URI с параметрами вторичного запроса помещаются в атрибут memberURL. POSIX-группы также полностью идентичны тем, что мы рассматривали во втором примере: строятся на объектном классе posixGroup, а атрибут для хранения URI labeledURI берётся из вспомогательного класса labeledURIObject. А вот классические группы, как мы договаривались, на этот раз будут строиться на структурном объектном классе organizationalRole. Атрибут членства этого класса roleOccupant не входит в число его обязательных атрибутов, так что фиктивного члена для соблюдения требований схемы данных каталога заводить не нужно. Как и в предыдущем примере, у нашего класса среди обязательных или допустимых атрибутов нет подходящего для хранения LDAP URI, и опять для добавления такого атрибута в наши записи мы воспользовались специальным вспомогательным объектным классом extensibleObject, позволяющим включать в запись любой известный LDAP-серверу пользовательский атрибут. А раз уж мы можем включить любой атрибут, на этот раз мы решили воспользоваться атрибутом memberURL, который также подходит для хранения LDAP URI, поскольку является производным от типа атрибута labeledURI.

Стоп! А известен ли тип атрибута memberURL нашему серверу slapd? Мы же ещё не добавляли содержащий его описание (а также описание объектного класса groupOfURLs) набор схемы данных dyngroup. Самое время сделать это:

$ ldapadd -x -D cn=config -W -f /etc/ldap/schema/dyngroup.ldif
Enter LDAP Password: 
adding new entry "cn=dyngroup,cn=schema,cn=config"

Ну а теперь можно смело добавлять в каталоги будущие динамические объекты:

$ ldapadd -x -D cn=manager,ou=System,dc=mycompany,dc=ru -W -c -f ./003-add_dynamic_objects.ldif
Enter LDAP Password:
adding new entry "ou=MailingLists,dc=mycompany,dc=ru"

adding new entry "cn=All,ou=MailingLists,dc=mycompany,dc=ru"

adding new entry "cn=Developers,ou=MailingLists,dc=mycompany,dc=ru"

adding new entry "cn=Designers,ou=MailingLists,dc=mycompany,dc=ru"

adding new entry "ou=PosixGroups,dc=mycompany,dc=ru"

adding new entry "cn=Developers,ou=PosixGroups,dc=mycompany,dc=ru"

adding new entry "cn=Designers,ou=PosixGroups,dc=mycompany,dc=ru"

adding new entry "cn=Developers,ou=Groups,dc=mycompany,dc=ru"

adding new entry "cn=Designers,ou=Groups,dc=mycompany,dc=ru"

На данном этапе проверять их нет смысла, поскольку динамического содержимого у них всё равно не будет. Пора переходить к последнему этапу — настроить наложение dynlist.

Шаг 3. Настройка наложения dynlist

Начнём, как всегда, с загрузки динамического модуля наложения. Традиционный LDIF:

dn: cn=module{0},cn=config
changetype: modify
add: olcModuleLoad
olcModuleLoad: dynlist.la

Применим его и проверим загруженные модули:

$ ldapmodify -x -D cn=config -W -f ./101-add_dynlist_module.ldif
Enter LDAP Password:
modifying entry "cn=module{0},cn=config"

$ ldapsearch -xLLL -D cn=config -W -b cn=module{0},cn=config olcModuleLoad
Enter LDAP Password:
dn: cn=module{0},cn=config
olcModuleLoad: {0}back_mdb.la
olcModuleLoad: {1}dynlist.la

Пора переходить к тому, ради чего и затевался этот пример. Принцип хранения настроек в виде строки, в которой параметры наложения разделяются пробельными символами, а также тот факт, что тип атрибута olcDLattrSet является многозначным (то есть позволяет иметь несколько значений этого атрибута в одной записи), даёт администраторам возможность задавать настройки для построения различных динамических объектов в рамках одной записи конфигурационного каталога, описывающей наложение dynlist. Кроме того, в определении типа атрибута olcDLattrSet имеется ключевое слово X-ORDERED, а значит значения этого атрибута индексируются и с ними можно работать как с упорядоченным множеством.

Разберёмся со всем этим на практике. Для начала добавим запись наложения к рабочей базе данных нашего каталога и уже при добавлении укажем настройки для двух различных динамических объектов:

dn: olcOverlay=dynlist,olcDatabase={1}mdb,cn=config
objectClass: olcDynamicList
olcOverlay: dynlist
olcDLattrSet: {0}groupOfURLs memberURL
olcDLattrSet: {1}organizationalRole memberURL roleOccupant

Как мы видим, в строке 4 указаны настройки для организации динамических списков рассылки электронной почты, а в строке 5 — настройки для организации классических динамических групп с объектным классом organizationalRole и с атрибутом членства roleOccupant. Смысл параметров настройки известен нам по предыдущим примерам. Поскольку мы только добавляем запись наложения, значения атрибута olcDLattrSet можно было и не индексировать (то есть не указывать предваряющие эти значения числа в фигурных скобках), при добавлении они автоматически получили бы индекс в порядке их указания в LDIF-файле. Кроме того, порядок указания настроек наложения (то есть значений атрибута olcDLattrSet) неважен — наложение в любом случае обработает все динамические объекты, соответствующие критериям, указанным в этих настройках.

Применим наш LDIF и проверим получившуюся запись наложения:

$ ldapadd -x -D cn=config -W -f ./102-add_dynlist_overlay.ldif
Enter LDAP Password:
adding new entry "olcOverlay=dynlist,olcDatabase={1}mdb,cn=config"

$ ldapsearch -xLLL -D cn=config -W -b olcDatabase={1}mdb,cn=config -s one
Enter LDAP Password:
dn: olcOverlay={0}dynlist,olcDatabase={1}mdb,cn=config
objectClass: olcDynamicList
olcOverlay: {0}dynlist
olcDlAttrSet: {0}groupOfURLs memberURL
olcDlAttrSet: {1}organizationalRole memberURL roleOccupant

Теперь проверим, как это повлияло на состояние динамических объектов в рабочем каталоге:

$ ldapsearch -xLLL -b dc=mycompany,dc=ru '(|(objectClass=groupOfURLs)(objectClass=posixGroup)(objectClass=organizationalRole))' mail memberUid roleOccupant
dn: cn=All,ou=MailingLists,dc=mycompany,dc=ru
mail: antonova@mycompany.ru
mail: ivanov@mycompany.ru
mail: petrov@mycompany.ru
mail: sidorov@mycompany.ru

dn: cn=Developers,ou=MailingLists,dc=mycompany,dc=ru
mail: ivanov@mycompany.ru
mail: petrov@mycompany.ru

dn: cn=Designers,ou=MailingLists,dc=mycompany,dc=ru
mail: antonova@mycompany.ru
mail: sidorov@mycompany.ru

dn: cn=Developers,ou=PosixGroups,dc=mycompany,dc=ru

dn: cn=Designers,ou=PosixGroups,dc=mycompany,dc=ru

dn: cn=Developers,ou=Groups,dc=mycompany,dc=ru
roleOccupant: uid=ivanov,ou=People,dc=mycompany,dc=ru
roleOccupant: uid=petrov,ou=People,dc=mycompany,dc=ru

dn: cn=Designers,ou=Groups,dc=mycompany,dc=ru
roleOccupant: uid=antonova,ou=People,dc=mycompany,dc=ru
roleOccupant: uid=sidorov,ou=People,dc=mycompany,dc=ru

Всё правильно: списки рассылки и классические группы наполнились динамическим содержимым, а POSIX-группы — нет. Добавим настройки и для них:

dn: olcOverlay={0}dynlist,olcDatabase={1}mdb,cn=config
changetype: modify
add: olcDLattrSet
olcDLattrSet: {1}labeledURIObject ldap:///ou=PosixGroups,dc=mycompany,dc=ru??one? labeledURI memberUid:uid

В строке 4 этого фрагмента LDIF добавляется новое значение атрибута olcDLattrSet. Оно будет иметь индекс 1, то есть станет вторым среди значений этого атрибута и разместится между двумя уже имеющимися. Если бы мы не задавали явно индекс этому значению, оно было бы помещено в конец упорядоченного множества значений (стало бы третьим) и автоматически получило бы индекс 2. Опять же, на работу наложения порядок указания настроек динамических объектов не влияет.

Обратите внимание, что в данном случае мы указали промежуточный параметр настройки наложения (LDAP URI), ограничивающий область применения данных настроек (подробнее смотрите здесь). Также обратите внимание, что в качестве объектного класса, на который будет реагировать наложение dynlist при отборе записей среди результатов операции поиска LDAP, мы указали не структурный класс posixGroup (как мы поступали во втором примере), а вспомогательный класс labeledURIObject. В данном случае допустимо было использовать любой из этих объектных классов, мы выбрали второй для разнообразия.

Применим наш LDIF и посмотрим получившиеся в результате настройки наложения:

$ ldapmodify -x -D cn=config -W -f ./103-dynlist_overlay_mod1.ldif
Enter LDAP Password:
modifying entry "olcOverlay={0}dynlist,olcDatabase={1}mdb,cn=config"

$ ldapsearch -xLLL -D cn=config -W -b olcDatabase={1}mdb,cn=config -s one -o ldif-wrap=no
Enter LDAP Password:
dn: olcOverlay={0}dynlist,olcDatabase={1}mdb,cn=config
objectClass: olcDynamicList
olcOverlay: {0}dynlist
olcDlAttrSet: {0}groupOfURLs memberURL
olcDlAttrSet: {1}labeledURIObject ldap:///ou=PosixGroups,dc=mycompany,dc=ru??one? labeledURI memberUid:uid
olcDlAttrSet: {2}organizationalRole memberURL roleOccupant

Всё в порядке. Посмотрим как дела с динамическими объектами в каталоге:

$ ldapsearch -xLLL -b dc=mycompany,dc=ru '(|(objectClass=groupOfURLs)(objectClass=posixGroup)(objectClass=organizationalRole))' mail memberUid roleOccupant
dn: cn=All,ou=MailingLists,dc=mycompany,dc=ru
mail: antonova@mycompany.ru
mail: ivanov@mycompany.ru
mail: petrov@mycompany.ru
mail: sidorov@mycompany.ru

dn: cn=Developers,ou=MailingLists,dc=mycompany,dc=ru
mail: ivanov@mycompany.ru
mail: petrov@mycompany.ru

dn: cn=Designers,ou=MailingLists,dc=mycompany,dc=ru
mail: antonova@mycompany.ru
mail: sidorov@mycompany.ru

dn: cn=Developers,ou=PosixGroups,dc=mycompany,dc=ru
memberUid: ivanov
memberUid: petrov

dn: cn=Designers,ou=PosixGroups,dc=mycompany,dc=ru
memberUid: antonova
memberUid: sidorov

dn: cn=Developers,ou=Groups,dc=mycompany,dc=ru
roleOccupant: uid=ivanov,ou=People,dc=mycompany,dc=ru
roleOccupant: uid=petrov,ou=People,dc=mycompany,dc=ru

dn: cn=Designers,ou=Groups,dc=mycompany,dc=ru
roleOccupant: uid=antonova,ou=People,dc=mycompany,dc=ru
roleOccupant: uid=sidorov,ou=People,dc=mycompany,dc=ru

Все наши объекты получили динамическое содержимое.

На этом можно было бы и завершить пример. Но что, если нам вдруг понадобилось что-то изменить в настройках наложения? Допустим, мы хотим добавить промежуточный параметр (ограничительный LDAP URI) к первой и последней настройке. Можно просто заменить все настройки разом:

dn: olcOverlay={0}dynlist,olcDatabase={1}mdb,cn=config
changetype: modify
replace: olcDLattrSet
olcDLattrSet: groupOfURLs ldap:///ou=MailingLists,dc=mycompany,dc=ru??one? memberURL
olcDLattrSet: labeledURIObject ldap:///ou=PosixGroups,dc=mycompany,dc=ru??one? labeledURI memberUid:uid
olcDLattrSet: organizationalRole ldap:///ou=Groups,dc=mycompany,dc=ru??one? memberURL roleOccupant

А можно поступить хитрее, и, воспользовавшись тем, что у атрибута olcDLattrSet упорядоченное множество значений, заменить только конкретные значения:

dn: olcOverlay={0}dynlist,olcDatabase={1}mdb,cn=config
changetype: modify
delete: olcDLattrSet
olcDLattrSet: {0}
-
add: olcDLattrSet
olcDLattrSet: {0}groupOfURLs ldap:///ou=MailingLists,dc=mycompany,dc=ru??one? memberURL
-
delete: olcDLattrSet
olcDLattrSet: {2}
-
add: olcDLattrSet
olcDLattrSet: {2}organizationalRole ldap:///ou=Groups,dc=mycompany,dc=ru??one? memberURL roleOccupant

Какой из вариантов выбрать — решать Вам, мы же воспользуемся вторым:

$ ldapmodify -x -D cn=config -W -f ./104-dynlist_overlay_mod2.ldif
Enter LDAP Password:
modifying entry "olcOverlay={0}dynlist,olcDatabase={1}mdb,cn=config"

Посмотрим, что получилось в итоге с записью наложения dynlist:

$ ldapsearch -xLLL -D cn=config -W -b olcDatabase={1}mdb,cn=config -s one -o ldif-wrap=no
Enter LDAP Password: 
dn: olcOverlay={0}dynlist,olcDatabase={1}mdb,cn=config
objectClass: olcDynamicList
olcOverlay: {0}dynlist
olcDlAttrSet: {0}groupOfURLs ldap:///ou=MailingLists,dc=mycompany,dc=ru??one? memberURL
olcDlAttrSet: {1}labeledURIObject ldap:///ou=PosixGroups,dc=mycompany,dc=ru??one? labeledURI memberUid:uid
olcDlAttrSet: {2}organizationalRole ldap:///ou=Groups,dc=mycompany,dc=ru??one? memberURL roleOccupant

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

Справедливости ради необходимо отметить, что внесение таких сложных изменений всё же повлияло на работу наложения dynlist: в нашем тестовом каталоге (OpenLDAP версии 2.4.46) часть динамических объектов (списки рассылки) перестала наполняться динамическим содержимым. После перезапуска процесса slapd всё встало на свои места. Что ж, динамическая конфигурация cn=config всё ещё находится в стадии совершенствования.

Что ещё можно сделать

В man-странице наложения dynlist сказано, что при использовании элемента управления LDAP ManageDsaIT (RFC 3296) наполнение динамического объекта динамическим содержимым не происходит, и в ответ на поисковый запрос с таким элементом управления возвращается исходная запись динамического объекта. Это может оказаться полезным, если мы хотим проверить, какие же данные на самом деле находятся в нашем каталоге.

К примеру, работая с каталогом, пользователь может извлечь POSIX-группу Developers:

$ ldapsearch -xLLL -b cn=Developers,ou=PosixGroups,dc=mycompany,dc=ru -o ldif-wrap=no
dn: cn=Developers,ou=PosixGroups,dc=mycompany,dc=ru
objectClass: posixGroup
objectClass: labeledURIObject
cn: Developers
gidNumber: 10001
labeledURI: ldap:///ou=People,dc=mycompany,dc=ru?uid?one?(&(objectClass=inetOrgPerson)(ou=Developers))
memberUid: ivanov
memberUid: petrov

Наличие замысловатого URI в атрибуте labeledURI может навести его на мысль, что на самом деле это динамически наполняемый объект. Чтобы убедиться в этом, он должен выполнить такой же поисковый запрос, но с указанием элемента управления ManageDsaIT (для утилиты ldapsearch он задаётся параметром -M):

$ ldapsearch -xLLL -b cn=Developers,ou=PosixGroups,dc=mycompany,dc=ru -o ldif-wrap=no -M
dn: cn=Developers,ou=PosixGroups,dc=mycompany,dc=ru
objectClass: posixGroup
objectClass: labeledURIObject
cn: Developers
gidNumber: 10001
labeledURI: ldap:///ou=People,dc=mycompany,dc=ru?uid?one?(&(objectClass=inetOrgPerson)(ou=Developers))

В итоге из каталога извлекается исходная запись без динамического содержимого.

Ну хорошо, а можно ли не показывать пользователю тот самый замысловатый URI? Это несложно, нужно всего лишь ограничить доступ к атрибуту labeledURI:

dn: olcDatabase={1}mdb,cn=config
changetype: modify
add: olcAccess
olcAccess: {1}to attrs=labeledURI
  by * none

Поскольку это правило ACL имеет индекс 1, оно займёт место между двумя уже имеющимися правилами в настройках рабочей базы данных. Сам ACL предельно прост: мы запрещаем любой доступ к атрибуту labeledURI всем пользователям (само собой, кроме пользователя, чьё DN указано в атрибуте olcRootDN в настройках базы данных).

Добавим этот ACL и выведем список имеющихся:

$ ldapmodify -x -D cn=config -W -f ./105-add_labeleduri_acl.ldif
Enter LDAP Password:
modifying entry "olcDatabase={1}mdb,cn=config"

$ ldapsearch -xLLL -o ldif-wrap=no -D cn=config -W -b olcDatabase={1}mdb,cn=config -s base olcAccess
Enter LDAP Password:
dn: olcDatabase={1}mdb,cn=config
olcAccess: {0}to attrs=userPassword by self write by anonymous auth by * none
olcAccess: {1}to attrs=labeledURI by * none
olcAccess: {2}to * by self write by * read

А теперь вновь попробуем запросить POSIX-группу Developers:

$ ldapsearch -xLLL -b cn=Developers,ou=PosixGroups,dc=mycompany,dc=ru -o ldif-wrap=no
dn: cn=Developers,ou=PosixGroups,dc=mycompany,dc=ru
objectClass: posixGroup
objectClass: labeledURIObject
cn: Developers
gidNumber: 10001
memberUid: ivanov
memberUid: petrov

Атрибут labeledURI больше не отображается пользователю (в данном случае анонимному), поскольку у него нет на этот атрибут никаких прав. Но динамическое содержимое в записи появилось, а значит наложение dynlist (которое также выполняется с правами текущего пользователя) сработало, следовательно, оно смогло прочитать значение атрибута labeledURI записи cn=Developers,ou=PosixGroups,dc=mycompany,dc=ru. То есть наше ACL работает только наполовину? На самом деле нет, всё дело в порядке применения ACL (который можно отследить, установив уровень журналирования slapd в значение acl):

  1. Сервер slapd, согласно критериям поиска, переданным утилитой ldapsearch, нашел требуемую запись, но не стал возвращать её утилите, а передал на обработку наложению dynlist.
  2. Наложение dynlist проанализировало полученную запись и обнаружило, что она отвечает критериям одного из динамических объектов (содержит класс labeledURIObject и атрибут labeledURI, а также находится в ветке ou=PosixGroups,dc=mycompany,dc=ru) и поэтому должна быть наполнена динамическим содержимым.
  3. Поскольку наложение dynlist получило запись целиком (без ограничений), то оно считало параметры вторичного поиска из LDAP URI в значении атрибута labeledURI и осуществило этот вторичный поиск (во время выполнения которого правила ACL применялись). По результатам поиска было сформировано динамическое содержимое, которым была дополнена исходная запись.
  4. Дополненная запись готова для возврата клиенту (утилите ldapsearch). Но перед возвратом slapd применяет к ней правила ACL, так что динамическое содержимое в записи осталось, а атрибут labeledURI — нет. Итоговая запись передаётся утилите ldapsearch, которая отображает её пользователю.

Так что никаких противоречий тут нет. Кстати, у добавленного нами правила ACL есть один побочный эффект: оно распространяется и на типы атрибутов, производные от labeledURI. Убедимся в этом, получив динамический объект с другим атрибутом, в котором хранится LDAP URI:

$ ldapsearch -xLLL -b cn=Developers,ou=MailingLists,dc=mycompany,dc=ru -o ldif-wrap=no
dn: cn=Developers,ou=MailingLists,dc=mycompany,dc=ru
objectClass: groupOfURLs
cn: Developers
mail: ivanov@mycompany.ru
mail: petrov@mycompany.ru

Как видите, атрибут memberURL (производный от labeledURI) также не отображается пользователю. Если же мы поступили бы наоборот, то есть в ACL ограничили доступ к атрибуту memberURL, то на вышестоящий атрибут labeledURI действие этого правила не распространялось бы. Такой побочный эффект от иерархии типов атрибутов всегда следует иметь ввиду при составлении ACL.

После того, как мы разобрали порядок применения ACL в OpenLDAP, становится очевидно, что ограничения можно наложить и на вывод самого динамического содержимого. Предположим, что мы хотим не показывать динамическое содержимое при анонимном подключении к каталогу (то есть показывать его только пользователям, прошедшим аутентификацию). Для этого можно составить такой ACL:

dn: olcDatabase={1}mdb,cn=config
changetype: modify
add: olcAccess
olcAccess: {2}to attrs=mail,memberUid,roleOccupant
  by users read
  by * none

Поскольку это правило ACL имеет индекс 2, оно займёт третье место в списке ACL (перед последним в настоящий момент правилом). В строке 4 мы перечислили атрибуты, составляющие в нашем примере динамическое содержимое в динамических объектах. В строке 5 мы позволяем пользователям, прошедшим аутентификацию, считывать значения этих атрибутов, а в строке 6 запрещаем всем остальным доступ к ним. Таким образом, динамическое содержимое не будет отображаться при анонимном запросе в каталог.

Тут возникает небольшая проблема: атрибуты mail содержатся не только в динамических списках рассылки электронной почты, но и во вполне статических записях пользователей в ветке ou=People,dc=mycompany,dc=ru. Чтобы ограничить доступ именно к динамическому содержимому, разобьём наше правило на три, ограничив доступ к конкретным атрибутам именно в тех ветках каталога, в которых содержатся динамические объекты:

dn: olcDatabase={1}mdb,cn=config
changetype: modify
add: olcAccess
olcAccess: {2}to dn.one="ou=MailingLists,dc=mycompany,dc=ru" attrs=mail
  by users read
  by * none
olcAccess: {3}to dn.one="ou=PosixGroups,dc=mycompany,dc=ru" attrs=memberUid
  by users read
  by * none
olcAccess: {4}to dn.one="ou=Groups,dc=mycompany,dc=ru" attrs=roleOccupant
  by users read
  by * none

Наши правила ACL имеют индексы 2, 3 и 4, то есть они последовательно будут вставлены перед последним (самым общим) правилом в списке ACL. Применим изменения и проверим итоговые ACL:

$ ldapmodify -x -D cn=config -W -f ./106-add_dynamic_attrs_acl.ldif
Enter LDAP Password:
modifying entry "olcDatabase={1}mdb,cn=config"

$ ldapsearch -xLLL -o ldif-wrap=no -D cn=config -W -b olcDatabase={1}mdb,cn=config -s base olcAccess
Enter LDAP Password:
dn: olcDatabase={1}mdb,cn=config
olcAccess: {0}to attrs=userPassword by self write by anonymous auth by * none
olcAccess: {1}to attrs=labeledURI by * none
olcAccess: {2}to dn.one="ou=MailingLists,dc=mycompany,dc=ru" attrs=mail by users read by * none
olcAccess: {3}to dn.one="ou=PosixGroups,dc=mycompany,dc=ru" attrs=memberUid by users read by * none
olcAccess: {4}to dn.one="ou=Groups,dc=mycompany,dc=ru" attrs=roleOccupant by users read by * none
olcAccess: {5}to * by self write by * read

Осталось проверить работу новых правил. Сначала запросим динамические объекты для отдела Developers анонимно:

$ ldapsearch -xLLL -b dc=mycompany,dc=ru '(cn=Developers)'
dn: cn=Developers,ou=MailingLists,dc=mycompany,dc=ru
objectClass: groupOfURLs
cn: Developers

dn: cn=Developers,ou=PosixGroups,dc=mycompany,dc=ru
objectClass: posixGroup
objectClass: labeledURIObject
cn: Developers
gidNumber: 10001

dn: cn=Developers,ou=Groups,dc=mycompany,dc=ru
objectClass: organizationalRole
objectClass: extensibleObject
cn: Developers

Всё верно, динамическое содержимое не отображается. Теперь выполним этот же запрос от имени пользователя ivanov:

$ ldapsearch -xLLL -D uid=ivanov,ou=People,dc=mycompany,dc=ru -w ivanovPassword -b dc=mycompany,dc=ru '(cn=Developers)'
dn: cn=Developers,ou=MailingLists,dc=mycompany,dc=ru
objectClass: groupOfURLs
cn: Developers
mail: ivanov@mycompany.ru
mail: petrov@mycompany.ru

dn: cn=Developers,ou=PosixGroups,dc=mycompany,dc=ru
objectClass: posixGroup
objectClass: labeledURIObject
cn: Developers
gidNumber: 10001
memberUid: ivanov
memberUid: petrov

dn: cn=Developers,ou=Groups,dc=mycompany,dc=ru
objectClass: organizationalRole
objectClass: extensibleObject
cn: Developers
roleOccupant: uid=ivanov,ou=People,dc=mycompany,dc=ru
roleOccupant: uid=petrov,ou=People,dc=mycompany,dc=ru

Пользователь, прошедший аутентификацию, получает доступ к динамическому содержимому записей. Наши ACL работают как ожидалось.

Итоговые настройки

В ходе этого урока мы ознакомились в возможностью настройки наложения dynlist для отбора и обработки сразу нескольких динамических объектов различных типов. Особое внимание было уделено приёмам работы с атрибутами X-ORDERED, которые часто используются в объектах конфигурационного каталога cn=config. Кроме того, была рассмотрена работа наложения dynlist при запросах в каталог с указанием элемента управления ManageDsaIT, а также некоторые аспекты работы ACL OpenLDAP, связанные с построением и отображением динамического содержимого объектов. Итоговые настройки каталога dc=mycompany,dc=ru выглядят так (для удобства чтения ACL мы разбили их вывод на несколько строк):

$ ldapsearch -xLLL -o ldif-wrap=no -D cn=config -W -b olcDatabase={1}mdb,cn=config
Enter LDAP Password:
dn: olcDatabase={1}mdb,cn=config
objectClass: olcMdbConfig
olcDatabase: {1}mdb
olcDbDirectory: /var/lib/ldap/dc=mycompany,dc=ru
olcSuffix: dc=mycompany,dc=ru
olcAccess: {0}to attrs=userPassword
  by self write
  by anonymous auth
  by * none
olcAccess: {1}to attrs=labeledURI
  by * none
olcAccess: {2}to dn.one="ou=MailingLists,dc=mycompany,dc=ru" attrs=mail
  by users read
  by * none
olcAccess: {3}to dn.one="ou=PosixGroups,dc=mycompany,dc=ru" attrs=memberUid
  by users read
  by * none
olcAccess: {4}to dn.one="ou=Groups,dc=mycompany,dc=ru" attrs=roleOccupant
  by users read
  by * none
olcAccess: {5}to *
  by self write
  by * read
olcRootDN: cn=manager,ou=System,dc=mycompany,dc=ru
olcRootPW: {SSHA}PKFrwbIL/zLd3gabPPLxn1vNq2jQHj4g
olcDbIndex: objectClass eq
olcDbIndex: cn eq,sub,subinitial

dn: olcOverlay={0}dynlist,olcDatabase={1}mdb,cn=config
objectClass: olcDynamicList
olcOverlay: {0}dynlist
olcDlAttrSet: {0}groupOfURLs ldap:///ou=MailingLists,dc=mycompany,dc=ru??one? memberURL
olcDlAttrSet: {1}labeledURIObject ldap:///ou=PosixGroups,dc=mycompany,dc=ru??one? labeledURI memberUid:uid
olcDlAttrSet: {2}organizationalRole ldap:///ou=Groups,dc=mycompany,dc=ru??one? memberURL roleOccupant
Pro-LDAP.ru 2013-2020 г. Последнее изменение страницы — 13 января 2019 г. Вопросы и предложения принимаются на форуме проекта.