3. Классические динамические группы

Содержание

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

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

Задача: Требуется создать классические группы, постоянно отражающие актуальную структуру организации.

Решение

Как мы помним, объекты групп в каталоге можно построить на разных объектных классах. В данном примере для построения групп мы будем использовать класс groupOfNames с атрибутом членства member. Если Вы планируете строить динамические группы на классе groupOfUniqueNames (атрибут членства uniqueMember) или organizationalRole (атрибут членства roleOccupant), просто внесите в примеры настройки соответствующие изменения.

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

Шаг 1. Настройка наложения 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

Переходим к настройкам наложения. Для данного примера мы сформируем следующую запись наложения с объектным классом olcDynamicList (дочернюю по отношению к рабочей базе данных):

dn: olcOverlay=dynlist,olcDatabase={1}mdb,cn=config
objectClass: olcDynamicList
olcOverlay: dynlist
olcDLattrSet: groupOfNames labeledURI member

Параметры настройки наложения указываются в значении атрибута olcDLattrSet (строка 4). Как и в предыдущем примере, у нас есть три разделённых пробелами параметра. Первые два из них, как мы уже знаем, задают имена объектного класса и типа атрибута, по которым наложение dynlist отбирает среди результирующего набора операции поиска LDAP те записи, которые требуется дополнить динамическим содержимым. В качестве объектного класса мы выбрали общепринятый для групповых записей каталога класс groupOfNames, а тип атрибута labeledURI является стандартным для хранения URI, которые необходимы нам для задания параметров поиска динамического содержимого.

Наиболее интересным в данном примере является третий параметр. Здесь он представляет собой ещё одно имя типа атрибута (member). В этом случае, то есть когда третьим параметром наложения является единичное имя типа атрибута, наложение dynlist переходит в режим составления динамических групп. В этом режиме наложения также осуществляется вторичный поиск в каталоге по критериям, задаваемым в LDAP URI той записи, которую требуется дополнить динамическим содержимым. Если в результате такого вторичного поиска будут найдены какие-либо записи, то уникальные имена (DN) этих записей будут подставлены в запись динамической группы в качестве значений атрибута, имя которого указано в третьем параметре наложения. То есть динамическим содержимым будут имена записей-членов группы в значениях атрибута членства. В данном случае был выбран стандартный для объектного класса groupOfNames атрибут членства member.

Как видно, алгоритм работы наложения в режиме составления динамических групп даже несколько проще, чем при составлении динамических списков.

Добавим нашу запись с настройками наложения и убедимся в её наличии:

$ 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}groupOfNames labeledURI member

Наложение стало первым (индекс 0) для нашей рабочей базы данных. Переходим к следующему этапу.

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

Дополним записи пользователей признаком принадлежности к отделам. Для этого сформируем такой LDIF-файл:

dn: uid=antonova,ou=People,dc=mycompany,dc=ru
changetype: modify
add: ou
ou: Designers

dn: uid=ivanov,ou=People,dc=mycompany,dc=ru
changetype: modify
add: ou
ou: Developers

dn: uid=petrov,ou=People,dc=mycompany,dc=ru
changetype: modify
add: ou
ou: Developers

dn: uid=sidorov,ou=People,dc=mycompany,dc=ru
changetype: modify
add: ou
ou: Designers

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

$ 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

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

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

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

Отлично! Переходим к следующему этапу.

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

Наконец, сформируем LDIF для добавления в каталог записей динамических групп двух наших отделов:

dn: cn=Developers,ou=Groups,dc=mycompany,dc=ru
objectClass: groupOfNames
objectClass: extensibleObject
cn: Developers
member: cn=dummy
labeledURI: ldap:///ou=People,dc=mycompany,dc=ru??one?(&(objectClass=inetOrgPerson)(ou=Developers))

dn: cn=Designers,ou=Groups,dc=mycompany,dc=ru
objectClass: groupOfNames
objectClass: extensibleObject
cn: Designers
member: cn=dummy
labeledURI: ldap:///ou=People,dc=mycompany,dc=ru??one?(&(objectClass=inetOrgPerson)(ou=Designers))

Записи групп строятся на структурном объектном классе groupOfNames, который мы указывали в настройках наложения dynlist. Обязательные атрибуты этого класса: cn, который мы использовали для формирования RDN, и member, в котором хранятся DN записей-членов группы. В связи с тем, что атрибут member должен обязательно присутствовать в записи, мы добавили в наши группы фиктивных членов (строки 5 и 12). Это общепринятая практика для групп с объектным классом groupOfNames.

Условия для поиска динамического содержимого оформлены в виде LDAP URI в значениях атрибутов labeledURI динамических записей (строки 6 и 13). Тип атрибута labeledURI не входит в число обязательных или допустимых атрибутов класса groupOfNames. В данном примере для включения таких атрибутов в наши записи мы воспользовались специальным вспомогательным объектным классом extensibleObject, позволяющим включать в запись любой известный LDAP-серверу пользовательский атрибут.

Критерии поиска, задаваемые в LDAP URI, практически полностью совпадают с критериями из предыдущего примера. Единственное отличие — отсутствие списка возвращаемых атрибутов (4-й компонент URI), поскольку для построения динамических групп никаких атрибутов не требуется. Если список возвращаемых атрибутов будет не пуст (например, если Вы скопируете откуда-то готовый URI и забудете очистить список атрибутов), наложение всё равно построит динамическую группу. Но в некоторых случаях, например, при составлении ACL на основе динамических групп, наличие атрибутов в 4-м компоненте URI является критичным: ACL просто не будут работать. Так что хорошей практикой является составление корректных URI, соответствующих условиям решаемой задачи.

Добавим записи наших групп в каталог:

$ $ ldapadd -D cn=manager,ou=System,dc=mycompany,dc=ru -W -f ./003-add_groups.ldif
Enter LDAP Password:
adding new entry "cn=Developers,ou=Groups,dc=mycompany,dc=ru"

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

А теперь попробуем запросить их:

$ ldapsearch -xLLL -b ou=Groups,dc=mycompany,dc=ru -s one -o ldif-wrap=no
dn: cn=Designers,ou=Groups,dc=mycompany,dc=ru
objectClass: groupOfNames
objectClass: extensibleObject
cn: Designers
member: cn=dummy
member: uid=antonova,ou=People,dc=mycompany,dc=ru
member: uid=sidorov,ou=People,dc=mycompany,dc=ru
labeledURI: ldap:///ou=People,dc=mycompany,dc=ru??one?(&(objectClass=inetOrgPerson)(ou=Designers))

dn: cn=Developers,ou=Groups,dc=mycompany,dc=ru
objectClass: groupOfNames
objectClass: extensibleObject
cn: Developers
member: cn=dummy
member: uid=ivanov,ou=People,dc=mycompany,dc=ru
member: uid=petrov,ou=People,dc=mycompany,dc=ru
labeledURI: ldap:///ou=People,dc=mycompany,dc=ru??one?(&(objectClass=inetOrgPerson)(ou=Developers))

Как видно, кроме фиктивных членов группы наполнились атрибутами членства, значения которых представляют собой уникальные имена (DN) записей пользователей, работающих в соответствующих отделах. Наложение dynlist сработало, как ожидалось.

Что ещё можно сделать: ACL на основе динамических групп

Итак, у нас есть группы с динамическими членами. Но можем ли мы составить на основе таких групп групповые ACL для разграничения доступа к каталогу OpenLDAP? То есть, чтобы пользователи на основе принадлежности к динамическим группам получали те или иные права доступа к каталогу? Давайте попробуем.

Предположим, что мы хотим разрешить пользователям, принадлежащим группе cn=Developers,ou=Groups,dc=mycompany,dc=ru, изменять значения атрибута description в учётных записях любых пользователей в каталоге. Для этого мы можем добавить в запись настроек нашей рабочей базы данных следующий ACL:

dn: olcDatabase={1}mdb,cn=config
changetype: modify
add: olcAccess
olcAccess: {1}to dn.children="ou=People,dc=mycompany,dc=ru" attrs=description
  by self write
  by group/groupOfNames/member="cn=Developers,ou=Groups,dc=mycompany,dc=ru" write
  by * read

Как мы помним, для нашей базы данных уже определено два ACL, так что при добавлении приведённого выше ACL он займёт место между ними. Применим изменения и посмотрим, что получилось:

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

$ ldapsearch -xLLL -D cn=config -W -b olcDatabase={1}mdb,cn=config -s base -o ldif-wrap=no 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 dn.children="ou=People,dc=mycompany,dc=ru" attrs=description by self write by group/groupOfNames/member="cn=Developers,ou=Groups,dc=mycompany,dc=ru" write by * read
olcAccess: {2}to * by self write by * read

Хорошо. Теперь посмотрим, как это повлияло на привилегии пользователей. Для примера мы будем тестировать права на изменения атрибута description записи uid=sidorov,ou=People,dc=mycompany,dc=ru при доступе к каталогу от имени пользователей antonova (не является членом группы Developers) и ivanov (является членом группы Developers).

Сначала протестируем привилегии пользователей утилитой slapacl (для её работы требуется считать списки контроля доступа из конфигурационной директории slapd.d, поэтому мы запускаем её с правами пользователя root):

# slapacl -D uid=antonova,ou=People,dc=mycompany,dc=ru -b uid=sidorov,ou=People,dc=mycompany,dc=ru description
dynlist_db_open: unable to fetch attributeDescription "dgIdentity": 17 (attribute type undefined)
dynlist_db_open: unable to fetch attributeDescription "dgAuthz": 17 (attribute type undefined)
authcDN: "uid=antonova,ou=people,dc=mycompany,dc=ru"
description: read(=rscxd)

# slapacl -D uid=ivanov,ou=People,dc=mycompany,dc=ru -b uid=sidorov,ou=People,dc=mycompany,dc=ru description
dynlist_db_open: unable to fetch attributeDescription "dgIdentity": 17 (attribute type undefined)
dynlist_db_open: unable to fetch attributeDescription "dgAuthz": 17 (attribute type undefined)
authcDN: "uid=ivanov,ou=people,dc=mycompany,dc=ru"
description: read(=rscxd)

Как мы видим, в обоих случаях получены права только на чтение, независимо от того, принадлежит ли пользователь, запрашивающий доступ к каталогу, динамической группе Developers, или нет. Однако возможно, что утилита slapacl просто не умеет работать с динамическими группами. Попробуем провести натурные испытания. Для этого составим такой LDIF с модификацией атрибута description записи пользователя sidorov:

dn: uid=sidorov,ou=People,dc=mycompany,dc=ru
changetype: modify
replace: description
description: The most talented designer

Попробуем применить эти изменения:

$ ldapmodify -x -D uid=antonova,ou=People,dc=mycompany,dc=ru -w antonovaPassword -f ./004-modify_user_description.ldif
modifying entry "uid=sidorov,ou=People,dc=mycompany,dc=ru"
ldap_modify: Insufficient access (50)

$ ldapmodify -x -D uid=ivanov,ou=People,dc=mycompany,dc=ru -w ivanovPassword -f ./004-modify_user_description.ldif
modifying entry "uid=sidorov,ou=People,dc=mycompany,dc=ru"
ldap_modify: Insufficient access (50)

Эксперимент показал, что пользователи и в самом деле не получают права на изменения на основе принадлежности динамической группе. Значит, ACL на основе динамических групп не работают? Всё не так просто, и чтобы докопаться до истины, надо очень внимательно перечитать man-страницу slapd.access(5), а конкретнее, часть материала, касающуюся групповых условий в поле <who> ACL. Приведём здесь несколько сокращённую выдержку:

Выражение group[/<objectclass>[/<attrname>]]=<group> означает, что доступ предоставляется тем запрашивающим, чьё DN перечислено в групповой записи, DN которой задано в <group>. Необязательные параметры <objectclass> и <attrname> определяют объектный класс групповой записи и тип атрибута членства в ней. Значения по умолчанию - groupOfNames и member, соответственно. Для статических групп указанный тип атрибута должен иметь синтаксис DistinguishedName или NameAndOptionalUID. Для динамических групп тип атрибута должен быть подтипом от labeledURI. В динамических группах будут оцениваться только LDAP URI формы ldap:///<base>??<scope>?<filter> с поиском только по локальному серверу.

То есть, для ACL на основе динамических групп нужно указать тип атрибута, содержащий LDAP URI с параметрами вторичного поиска (в нашем примере это как раз labeledURI), и, кроме того, грамотно составить сам URI (о чём мы говорили выше). Но тогда какой объектный класс следует указывать в групповом условии ACL? Экспериментальным путём было установлено, что это должен быть класс, содержащий атрибут, в котором указан LDAP URI. Для нашего примера мы должны указать класс extensibleObject. Если бы, как в предыдущем примере, для подключения атрибута labeledURI мы бы использовали объектный класс labeledURIObject, то в групповом условии ACL указывались бы именно эти класс и тип атрибута. Наконец, если бы для создания динамической группы использовался объектный класс groupOfURLs и его атрибут memberURL (производный от labeledURI), то в групповом условии ACL мы должны были бы указать их имена.

Вооружившись этими знаниями, переопределим наш второй ACL (с индексом 1) так:

dn: olcDatabase={1}mdb,cn=config
changetype: modify
delete: olcAccess
olcAccess: {1}
-
add: olcAccess
olcAccess: {1}to dn.children="ou=People,dc=mycompany,dc=ru" attrs=description
  by self write
  by group/extensibleObject/labeledURI="cn=Developers,ou=Groups,dc=mycompany,dc=ru" write
  by * read

Внесём изменения в настройки базы данных и проверим получившиеся в итоге ACL:

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

$ ldapsearch -xLLL -D cn=config -W -b olcDatabase={1}mdb,cn=config -s base -o ldif-wrap=no 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 dn.children="ou=People,dc=mycompany,dc=ru" attrs=description by self write by group/extensibleObject/labeledURI="cn=Developers,ou=Groups,dc=mycompany,dc=ru" write by * read
olcAccess: {2}to * by self write by * read

Всё в порядке. Попробуем повторить наше тестирование на предмет получения пользователями привилегий на основе их принадлежности динамической группе. Сначала протестируем права пользователей утилитой slapacl:

# slapacl -D uid=antonova,ou=People,dc=mycompany,dc=ru -b uid=sidorov,ou=People,dc=mycompany,dc=ru description
dynlist_db_open: unable to fetch attributeDescription "dgIdentity": 17 (attribute type undefined)
dynlist_db_open: unable to fetch attributeDescription "dgAuthz": 17 (attribute type undefined)
authcDN: "uid=antonova,ou=people,dc=mycompany,dc=ru"
description: read(=rscxd)
# slapacl -D uid=ivanov,ou=People,dc=mycompany,dc=ru -b uid=sidorov,ou=People,dc=mycompany,dc=ru description
dynlist_db_open: unable to fetch attributeDescription "dgIdentity": 17 (attribute type undefined)
dynlist_db_open: unable to fetch attributeDescription "dgAuthz": 17 (attribute type undefined)
authcDN: "uid=ivanov,ou=people,dc=mycompany,dc=ru"
description: write(=wrscxd)

Как мы видим, на этот раз пользователь antonova (не является членом группы Developers) получает права на чтение атрибута decsription записи пользователя sidorov, а пользователь ivanov (является членом группы Developers) — права на изменение этого атрибута. То есть наш ACL работает.

Убедимся в этом, проведя наше натурное испытание. Сначала попробуем применить описанный выше LDIF модификации атрибута decsription записи пользователя sidorov от имени пользователя antonova:

$ ldapmodify -x -D uid=antonova,ou=People,dc=mycompany,dc=ru -w antonovaPassword -f ./004-modify_user_description.ldif
modifying entry "uid=sidorov,ou=People,dc=mycompany,dc=ru"
ldap_modify: Insufficient access (50)

Как и следовало ожидать, у пользователя antonova недостаточно прав для выполнения этого действия. Попробуем провести ту же модификацию от имени пользователя ivanov:

$ ldapmodify -x -D uid=ivanov,ou=People,dc=mycompany,dc=ru -w ivanovPassword -f ./004-modify_user_description.ldif
modifying entry "uid=sidorov,ou=People,dc=mycompany,dc=ru"

Модификация проведена. Убедимся в этом:

$ ldapsearch -xLLL -b uid=sidorov,ou=People,dc=mycompany,dc=ru description
dn: uid=sidorov,ou=People,dc=mycompany,dc=ru
description: The most talented designer

Итак, пользователь ivanov получил права на изменение атрибута decsription записи пользователя sidorov на основании его членства в динамической группе cn=Developers,ou=Groups,dc=mycompany,dc=ru, а значит, ACL доступа к каталогу OpenLDAP с групповыми условиями на основе динамических групп работают.

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

В ходе этого урока мы познакомились с ещё одним вариантом третьего параметра настроек наложения dynlist, включающим режим построения динамических групп. Также мы рассмотрели порядок составления групповых условий 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 dn.children="ou=People,dc=mycompany,dc=ru" attrs=description
  by self write
  by group/extensibleObject/labeledURI="cn=Developers,ou=Groups,dc=mycompany,dc=ru" write
  by * read
olcAccess: {2}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}groupOfNames labeledURI member
Pro-LDAP.ru 2013-2019 г. Последнее изменение страницы — 28 декабря 2018 г. Вопросы и предложения принимаются на форуме проекта.