На этом этапе мы попробуем разобраться с действиями системного администратора при ведении групповых записей в каталоге, а также с тем, как использовать группы для разграничения доступа к самому каталогу на базе OpenLDAP.
Сначала добавим в каталог две группы: OpenLDAP Admins и OpenLDAP Operators. Членам первой группы будет разрешено менять пароли учётных записей пользователей в каталоге, а членам второй — изменять любые данные в каталоге (за исключением паролей пользователей). Построим записи групп на объектном классе groupOfNames:
# Группа "Администраторы каталога"dn: cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ruobjectClass: groupOfNamescn: OpenLDAP Adminsmember: uid=ivanov,ou=People,dc=mycompany,dc=ru# Группа "Операторы каталога"dn: cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ruobjectClass: groupOfNamescn: OpenLDAP Operatorsmember: uid=antonova,ou=People,dc=mycompany,dc=ru
Добавим их в каталог:
$ ldapadd -D cn=manager,ou=System,dc=mycompany,dc=ru -W -f ./002-add_groups.ldif Enter LDAP Password: adding new entry "cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru" adding new entry "cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru"
Теперь мы можем усовершенствовать наши ACL (относительно исходных):
dn: olcDatabase={1}mdb,cn=configchangetype: modifyreplace: olcAccessolcAccess: to attrs=userPasswordby self writeby group.exact="cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru" writeby anonymous authby * noneolcAccess: to *by self writeby group.exact="cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru" writeby * read
Обратите внимание, что по правилам переноса строк в LDIF строка, являющаяся продолжением предыдущей, должна начинаться с пробельного символа, поэтому в данном случае, для сохранения правильного формата ACL, перед условиями by ACL мы выставили два пробела.
В оба имеющихся правила мы добавили стандартное групповое условие by в ACL (строки 6 и 11). Модификатор .exact после ключевого слова group говорит о том, что в групповом условии указано полное уникальное имя (DN) записи группы. Этот модификатор применяется по умолчанию, его можно было не задавать явно.
Согласно групповому условию в первом ACL (строка 6) члены группы cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru, то есть те записи, DN которых перечислены в атрибутах member этой группы, получат право на изменение (write) атрибута userPassword. А согласно групповому условию во втором ACL (строка 11) члены группы cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru получат право на изменение всего остального содержимого каталога (за исключением атрибута userPassword).
Применим изменения и проверим:
$ ldapmodify -D cn=config -W -f ./101-modify_acl_1.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 olcAccess
Enter LDAP Password:
dn: olcDatabase={1}mdb,cn=config
olcAccess: {0}to attrs=userPassword by self write by group.exact="cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru" write by anonymous auth by * none
olcAccess: {1}to * by self write by group.exact="cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru" write by * read
А теперь протестируем наши ACL утилитой slapacl. Пользователь, авторизовавшийся от имени учётной записи uid=ivanov,ou=People,dc=mycompany,dc=ru, как член группы cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru должен получить право на изменение атрибута userPassword, а пользователь uid=antonova,ou=People,dc=mycompany,dc=ru, не являющийся членом этой группы, — вообще не иметь никакого доступа (кроме userPassword своей записи). Убедимся в этом, протестировав доступ к атрибуту userPassword пользователя uid=petrov,ou=People,dc=mycompany,dc=ru:
# slapacl -D uid=ivanov,ou=People,dc=mycompany,dc=ru -b uid=petrov,ou=People,dc=mycompany,dc=ru userPassword authcDN: "uid=ivanov,ou=people,dc=mycompany,dc=ru" userPassword: write(=wrscxd) # slapacl -D uid=antonova,ou=People,dc=mycompany,dc=ru -b uid=petrov,ou=People,dc=mycompany,dc=ru userPassword authcDN: "uid=antonova,ou=people,dc=mycompany,dc=ru" userPassword: none(=0)
Утилите slapacl для работы требуется считать списки контроля доступа из конфигурационной директории slapd.d, поэтому мы запускаем её с правами пользователя root.
Всё отлично. Теперь протестируем обратную ситуацию: согласно второму ACL пользователь uid=antonova,ou=People,dc=mycompany,dc=ru, являющийся членом группы cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru, должен иметь право изменять любую запись в каталоге (кроме атрибута userPassword), а пользователь uid=ivanov,ou=People,dc=mycompany,dc=ru, не являющийся членом этой группы, — только читать содержимое записей (свою собственную запись он, конечно же, может изменять). Убедимся в этом, протестировав доступ к атрибуту cn пользователя uid=petrov,ou=People,dc=mycompany,dc=ru:
# slapacl -D uid=antonova,ou=People,dc=mycompany,dc=ru -b uid=petrov,ou=People,dc=mycompany,dc=ru cn authcDN: "uid=antonova,ou=people,dc=mycompany,dc=ru" cn: write(=wrscxd) # slapacl -D uid=ivanov,ou=People,dc=mycompany,dc=ru -b uid=petrov,ou=People,dc=mycompany,dc=ru cn authcDN: "uid=ivanov,ou=people,dc=mycompany,dc=ru" cn: read(=rscxd)
Наши ACL работают. Но, согласитесь, оставлять за администраторами каталога лишь право изменять пароли выглядит нелогично. Можно поступить просто и добавить пользователя uid=ivanov,ou=People,dc=mycompany,dc=ru в группу cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru:
dn: cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ruchangetype: modifyadd: membermember: uid=ivanov,ou=People,dc=mycompany,dc=ru
Однако не будем спешить и вспомним о том, что умный администратор — это "ленивый" администратор. В ACL OpenLDAP есть понятие набора (set), позволяющее создавать различные сложные варианты контроля доступа. В данном случае нам подойдёт вариант рекурсивной проверки "вложенных" групп. Сначала мы добавим в качестве члена в группу cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru "вложенную" группу cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru:
dn: cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ruchangetype: modifyadd: membermember: cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru
Применим изменения от имени пользователя antonova (у которого есть соответствующие права на редактирование каталога):
$ ldapmodify -D uid=antonova,ou=People,dc=mycompany,dc=ru -w antonovaPassword -f ./003-add_group_to_group.ldif modifying entry "cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru" $ ldapsearch -xLLL -b 'cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru' member dn: cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru member: uid=antonova,ou=People,dc=mycompany,dc=ru member: cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru
Теперь модифицируем наш второй ACL:
dn: olcDatabase={1}mdb,cn=configchangetype: modifydelete: olcAccessolcAccess: {1}-add: olcAccessolcAccess: {1}to *by self writeby set="[cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru]/member* & user" writeby * read
Интересующее нас определение набора находится в условии by в 9-ой строке. Его можно "расшифровать" так: в качестве исходной точки берётся группа cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru, ищутся атрибуты member в ней, а также рекурсивно вниз во всех тех записях, которые были обнаружены в атрибутах member первоначальной и последующих записей (необходимость рекурсивного поиска обозначается знаком "звёздочки" *). В данном случае в итоге такого рекурсивного обхода у нас появляется набор из трёх записей (выдержка из log-файла slapd, запущенного в режиме журналирования acl):
ACL set[0]=uid=antonova,ou=people,dc=mycompany,dc=ru ACL set[1]=cn=openldap admins,ou=groups,dc=mycompany,dc=ru ACL set[2]=uid=ivanov,ou=people,dc=mycompany,dc=ru
После построения набора определяется, входит ли в него DN пользователя, прошедшего аутентификацию (подставляется вместо ключевого слова условий-наборов user). Если да, этому пользователю будут предоставлены соответствующие права. Подробно алгоритм работы данного набора разбирается в разделе 8.5.1 руководства администратора OpenLDAP 2.4.
Применим изменения и проверим (от имени rootDN конфигурационного каталога cn=config):
$ ldapmodify -D cn=config -W -f ./102-modify_acl_2.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 olcAccess
Enter LDAP Password:
dn: olcDatabase={1}mdb,cn=config
olcAccess: {0}to attrs=userPassword by self write by group.exact="cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru" write by anonymous auth by * none
olcAccess: {1}to * by self write by set="[cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru]/member* & user" write by * read
Протестируем работу ACL утилитой slapacl. Проверим, получит ли пользователь uid=ivanov,ou=People,dc=mycompany,dc=ru доступ к атрибуту cn пользователя uid=petrov,ou=People,dc=mycompany,dc=ru:
# slapacl -D uid=ivanov,ou=People,dc=mycompany,dc=ru -b uid=petrov,ou=People,dc=mycompany,dc=ru cn authcDN: "uid=ivanov,ou=people,dc=mycompany,dc=ru" cn: write(=wrscxd)
Да, всё работает, как ожидалось. Для убедительности проведём реальную проверку, изменив атрибут cn пользователя uid=petrov,ou=People,dc=mycompany,dc=ru:
dn: uid=petrov,ou=People,dc=mycompany,dc=ruchangetype: modifyreplace: cncn: Petr Petrovich Petrov
Изменения выполняем от имени пользователя ivanov:
$ ldapmodify -D uid=ivanov,ou=People,dc=mycompany,dc=ru -w ivanovPassword -f ./004-modify_petrov.ldif modifying entry "uid=petrov,ou=People,dc=mycompany,dc=ru" $ ldapsearch -xLLL -b uid=petrov,ou=People,dc=mycompany,dc=ru dn: uid=petrov,ou=People,dc=mycompany,dc=ru objectClass: inetOrgPerson uid: petrov sn: Petrov cn: Petr Petrovich Petrov
Всё прошло успешно. Теперь администраторы каталога будут получать тот же доступ на изменение записей, что и операторы, и, сверх того, смогут изменять пароли учётных записей пользователей.
Но вернёмся к нашим групповым условиям ACL. Группы в предыдущих примерах строились на объектном классе groupOfNames, члены в них указывались в атрибутах member, и slapd при оценке ACL принимал это как само собой разумеющееся. Но что, если у нас группы строятся на совсем другом объектном классе и атрибутах? Для slapd это тоже не проблема. Для нашего следующего примера заведём "роли", то есть группы на объектном классе organizationalRole:
# Контейнер для ролейdn: ou=Roles,dc=mycompany,dc=ruobjectClass: organizationalUnitou: Roles# Роль "Системный администратор"dn: cn=SysAdmin,ou=Roles,dc=mycompany,dc=ruobjectClass: organizationalRolecn: SysAdminroleOccupant: uid=ivanov,ou=People,dc=mycompany,dc=ruroleOccupant: uid=petrov,ou=People,dc=mycompany,dc=ru# Роль "Разработчик"dn: cn=Developer,ou=Roles,dc=mycompany,dc=ruobjectClass: organizationalRolecn: DeveloperroleOccupant: uid=antonova,ou=People,dc=mycompany,dc=ruroleOccupant: uid=sidorov,ou=People,dc=mycompany,dc=ru
Мы добавляем контейнер для ролей (чтобы не путаться в ролях и группах) и две роли: системный администратор и разработчик. Применим наши изменения:
$ ldapadd -D uid=ivanov,ou=People,dc=mycompany,dc=ru -w ivanovPassword -f ./005-add_roles.ldif adding new entry "ou=Roles,dc=mycompany,dc=ru" adding new entry "cn=SysAdmin,ou=Roles,dc=mycompany,dc=ru" adding new entry "cn=Developer,ou=Roles,dc=mycompany,dc=ru"
Предположим, что в компании разработан веб-интерфейс для ведения учётных записей пользователей. Разным пользователям в нём предоставляются различные права в зависимости от роли в организации. Пользователям, выполняющим роль системного администратора, предоставлено право изменять пароли всех пользователей. Для реализации этой функции им необходимо предоставить соответствующие привилегии на уровне каталога. Изменим наш первый ACL:
dn: olcDatabase={1}mdb,cn=configchangetype: modifydelete: olcAccessolcAccess: {0}-add: olcAccessolcAccess: {0}to dn.children="ou=People,dc=mycompany,dc=ru" attrs=userPasswordby self writeby group.exact="cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru" writeby group/organizationalRole/roleOccupant.exact="cn=SysAdmin,ou=Roles,dc=mycompany,dc=ru" writeby anonymous authby * none
Мы сузили диапазон действия нашего ACL записями в ветке ou=People,dc=mycompany,dc=ru (строка 7), поскольку учётные записи пользователей находятся именно там. Но основное изменение — это условие by в строке 10. По сути, это развёрнутый формат стандартного группового условия, в котором явно указывается, запись с каким объектным классом считать группой, и значения каких атрибутов считать членами этой группы. Условие в строке 10 говорит о том, что члены построенной на объектном классе organizationalRole группы cn=SysAdmin,ou=Roles,dc=mycompany,dc=ru (DN которой задано полностью, на что указывает модификатор .exact), то есть записи, DN которых перечислены в значениях атрибута roleOccupant этой группы, получат права на изменения атрибута userPassword записей в ветке ou=People,dc=mycompany,dc=ru (строка 7). Групповое условие by в строке 9 точно такое же, просто объектный класс groupOfNames и атрибут member считаются значениями по умолчанию, и поэтому опущены.
Полный формат группового условия by даёт возможность использовать в качестве группы записи практически с любыми объектными классами и любыми атрибутами с синтаксисом DN, например, запись с классом inetOrgPerson, членами которой будут считаться значения атрибута secretary. Но прибегать к таким ухищрениям нецелесообразно, если, конечно, Вы не собираетесь всех запутать.
Применим изменения и проверим:
$ ldapmodify -D cn=config -W -f ./103-modify_acl_3.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 olcAccess
Enter LDAP Password:
dn: olcDatabase={1}mdb,cn=config
olcAccess: {0}to dn.children="ou=People,dc=mycompany,dc=ru" attrs=userPassword by self write by group.exact="cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru" write by group/organizationalRole/roleOccupant.exact="cn=SysAdmin,ou=Roles,dc=mycompany,dc=ru" write by anonymous auth by * none
olcAccess: {1}to * by self write by set="[cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru]/member* & user" write by * read
Убедимся в корректности работы ACL с помощью утилиты slapacl. Проверим, получит ли пользователь uid=petrov,ou=People,dc=mycompany,dc=ru (как член группы cn=SysAdmin,ou=Roles,dc=mycompany,dc=ru) доступ к атрибуту userPassword пользователя uid=antonova,ou=People,dc=mycompany,dc=ru:
# slapacl -D uid=petrov,ou=People,dc=mycompany,dc=ru -b uid=antonova,ou=People,dc=mycompany,dc=ru userPassword authcDN: "uid=petrov,ou=people,dc=mycompany,dc=ru" userPassword: write(=wrscxd)
Всё работает, как ожидалось.
Теперь предположим, что разработчики компании в данный момент работают над телефонным справочником, для чего им требуются права на изменение атрибутов telephoneNumber и description учётных записей пользователей. Чтобы предоставить им такие права на уровне каталога, введём новый ACL:
dn: olcDatabase={1}mdb,cn=configchangetype: modifyadd: olcAccessolcAccess: {1}to dn.regex="^uid=[^,]+,ou=People,(dc=[^,]+,dc=[^,]+)$" attrs=telephoneNumber,descriptionby self writeby group.expand="cn=OpenLDAP Operators,ou=Groups,$1" writeby group/organizationalRole/roleOccupant.expand="cn=Developer,ou=Roles,$1" writeby * none
Этот ACL будет вторым (индекс 1), он поместится между двумя существующими. Мы сознательно несколько избыточно усложнили его структуру, чтобы продемострировать использование регулярных выражений и модификатора подстановки .expand. В строке 4 мы задаём условие, что контроль доступа будет осуществляться над атрибутами telephoneNumber и description записей каталога, DN которых соответствуют указанному регулярному выражению. В данном случае регулярное выражение говорит о том, что RDN записи должно быть построено на значении атрибута uid, RDN первого предка должно быть ou=People, а RDN двух последующих предков должны быть построены на значении атрибута dc (эти два RDN, за счёт заключения их в скобки, будут помещены в переменную $1), и на этом DN записи заканчивается. Строки 5 и 6 нужны для того, чтобы контроль над этими атрибутами не был потерян самим пользователем и членами группы OpenLDAP Operators (обратите внимание на использование модификатора .expand и подстановки значения переменной $1 в DN группы в строке 6). Строка 7 — это уже знакомая нам полная форма группового условия by, просто немного видоизменённая модификатором .expand и подстановкой в DN группы значения переменной $1. Смысл строки 8 очевиден.
Применим изменения и проверим:
$ ldapmodify -D cn=config -W -f ./104-modify_acl_4.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 olcAccess
Enter LDAP Password:
dn: olcDatabase={1}mdb,cn=config
olcAccess: {0}to dn.children="ou=People,dc=mycompany,dc=ru" attrs=userPassword by self write by group.exact="cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru" write by group/organizationalRole/roleOccupant.exact="cn=SysAdmin,ou=Roles,dc=mycompany,dc=ru" write by anonymous auth by * none
olcAccess: {1}to dn.regex="^uid=[^,]+,ou=People,(dc=[^,]+,dc=[^,]+)$" attrs=telephoneNumber,description by self write by group.expand="cn=OpenLDAP Operators,ou=Groups,$1" write by group/organizationalRole/roleOccupant.expand="cn=Developer,ou=Roles,$1" write by * none
olcAccess: {2}to * by self write by set="[cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru]/member* & user" write by * read
А теперь убедимся, что пользователь uid=sidorov,ou=People,dc=mycompany,dc=ru, как член группы cn=Developer,ou=Roles,dc=mycompany,dc=ru, получит доступ к атрибутам telephoneNumber, description, а заодно и к атрибуту cn пользователя uid=ivanov,ou=People,dc=mycompany,dc=ru:
# slapacl -D uid=sidorov,ou=People,dc=mycompany,dc=ru -b uid=ivanov,ou=People,dc=mycompany,dc=ru telephoneNumber description cn authcDN: "uid=sidorov,ou=people,dc=mycompany,dc=ru" telephoneNumber: write(=wrscxd) description: write(=wrscxd) cn: read(=rscxd)
По второму ACL получены права на изменение атрибутов telephoneNumber и description, а по третьему — на чтение атрибута cn.
Наконец, удостоверимся, что уже рассмотренное нами условие by с использованием наборов работает также и с нестандартными группами. Предоставим для пользователей, исполняющих роль SysAdmin те же права, что и для исполняющих роль Developer. Для этого добавим DN роли cn=SysAdmin,ou=Roles,dc=mycompany,dc=ru в качестве значения атрибута roleOccupant в запись cn=Developer,ou=Roles,dc=mycompany,dc=ru:
dn: cn=Developer,ou=Roles,dc=mycompany,dc=ruchangetype: modifyadd: roleOccupantroleOccupant: cn=SysAdmin,ou=Roles,dc=mycompany,dc=ru
Применим изменения и проверим:
$ ldapmodify -D uid=ivanov,ou=People,dc=mycompany,dc=ru -w ivanovPassword -f ./006-add_role_to_role.ldif modifying entry "cn=Developer,ou=Roles,dc=mycompany,dc=ru" $ ldapsearch -xLLL -b 'cn=Developer,ou=Roles,dc=mycompany,dc=ru' roleOccupant dn: cn=Developer,ou=Roles,dc=mycompany,dc=ru roleOccupant: uid=antonova,ou=People,dc=mycompany,dc=ru roleOccupant: uid=sidorov,ou=People,dc=mycompany,dc=ru roleOccupant: cn=SysAdmin,ou=Roles,dc=mycompany,dc=ru
Модифицируем наш второй ACL. Теперь мы будем рекурсивно искать значения атрибута roleOccupant в записях, начиная с записи роли Developer (обратите внимание, что модификатор .expand и подстановка переменных работает и в наборах):
dn: olcDatabase={1}mdb,cn=configchangetype: modifydelete: olcAccessolcAccess: {1}-add: olcAccessolcAccess: {1}to dn.regex="^uid=[^,]+,ou=People,(dc=[^,]+,dc=[^,]+)$" attrs=telephoneNumber,descriptionby self writeby group.expand="cn=OpenLDAP Operators,ou=Groups,$1" writeby set.expand="[cn=Developer,ou=Roles,$1]/roleOccupant* & user" writeby * none
Применим изменения и проверим:
$ ldapmodify -D cn=config -W -f ./105-modify_acl_5.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 olcAccess
Enter LDAP Password:
dn: olcDatabase={1}mdb,cn=config
olcAccess: {0}to dn.children="ou=People,dc=mycompany,dc=ru" attrs=userPassword by self write by group.exact="cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru" write by group/organizationalRole/roleOccupant.exact="cn=SysAdmin,ou=Roles,dc=mycompany,dc=ru" write by anonymous auth by * none
olcAccess: {1}to dn.regex="^uid=[^,]+,ou=People,(dc=[^,]+,dc=[^,]+)$" attrs=telephoneNumber,description by self write by group.expand="cn=OpenLDAP Operators,ou=Groups,$1" write by set.expand="[cn=Developer,ou=Roles,$1]/roleOccupant* & user" write by * none
olcAccess: {2}to * by self write by set="[cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru]/member* & user" write by * read
Осталось убедиться в нашей правоте. Удостоверимся, что пользователь uid=petrov,ou=People,dc=mycompany,dc=ru, как член группы cn=SysAdmin,ou=Roles,dc=mycompany,dc=ru, "вложенной" в группу cn=Developer,ou=Roles,dc=mycompany,dc=ru, получит доступ к атрибутам telephoneNumber, description и cn пользователя uid=ivanov,ou=People,dc=mycompany,dc=ru:
# slapacl -D uid=petrov,ou=People,dc=mycompany,dc=ru -b uid=ivanov,ou=People,dc=mycompany,dc=ru telephoneNumber description cn authcDN: "uid=petrov,ou=people,dc=mycompany,dc=ru" telephoneNumber: write(=wrscxd) description: write(=wrscxd) cn: read(=rscxd)
Всё работает, как ожидалось. Таким образом, мы рассмотрели использование разных вариантов групповых условий by ACL slapd, а также условия by с наборами для рекурсивного поиска членов "вложенных" групп. Осталось рассмотреть ещё один интересный вариант ACL, связанный с группами: управление значениями атрибутов записи, в которых могут находиться DN других записей. Звучит запутанно, проще продемонстрировать на примере. Типичная задача с таким типом ACL формулируется так: есть некоторая группа, требуется позволить пользователям добавлять себя в члены этой группы (и удалять себя из её членов). К примеру, на корпоративном портале есть лента новостей, для доступа к ней нужно состоять в группе News Readers:
dn: cn=News Readers,ou=Groups,dc=mycompany,dc=ruobjectClass: groupOfNamescn: News Readersmember: cn=dummy_member
Поскольку группа пока пуста, а атрибут member обязателен для объектного класса groupOfNames, пришлось ввести фиктивного члена cn=dummy_member (это общепринятая практика для классов groupOfNames и groupOfUniqueNames). Добавим эту запись в каталог:
$ ldapadd -D uid=ivanov,ou=People,dc=mycompany,dc=ru -w ivanovPassword -f ./007-add_news_readers_group.ldif adding new entry "cn=News Readers,ou=Groups,dc=mycompany,dc=ru"
Чтобы любой желающий мог получить доступ к корпоративным новостям, предоставим пользователям добавлять и удалять DN своих записей в атрибут member нашей группы. Составим такой ACL:
dn: olcDatabase={1}mdb,cn=configchangetype: modifyadd: olcAccessolcAccess: {0}to dn.base="cn=News Readers,ou=Groups,dc=mycompany,dc=ru" attrs=memberby dnattr=member selfwrite
Этот ACL будет добавлен перед всеми уже существующими (индекс 0). Согласно строке 4, мы ограничиваем доступ к атрибуту member группы News Readers. Самая интересная для нас строка 5. Условие by в ней говорит о том, что записи, DN которых перечислены в атрибуте member (формулировка dnattr=member) получат доступ уровня write только для добавления/удаления своего собственного DN (формулировка selfwrite) в атрибут member группы News Readers (согласно условию to в строке 4).
Чтобы члены группы OpenLDAP Operators не утратили контроль над записью группы News Readers, мы можем, как это делалось во всех предыдущих примерах, добавить им явно привилегии write:
dn: olcDatabase={1}mdb,cn=configchangetype: modifyadd: olcAccessolcAccess: {0}to dn.base="cn=News Readers,ou=Groups,dc=mycompany,dc=ru" attrs=memberby dnattr=member selfwriteby group.exact="cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru" write
Но в этот раз мы поступим иначе, и укажем продолжать обход ACL, если было найдено совпадение с условием to, но не найдено ни одного совпадения с условиями by:
dn: olcDatabase={1}mdb,cn=configchangetype: modifyadd: olcAccessolcAccess: {0}to dn.base="cn=News Readers,ou=Groups,dc=mycompany,dc=ru" attrs=memberby dnattr=member selfwriteby * break
В этом случае, если член группы OpenLDAP Operators захочет модифицировать атрибут member группы News Readers, он сможет это сделать, миновав этот ACL и получив привилегии в последнем из списка ACL. Применим наши изменения и проверим результат:
$ ldapmodify -D cn=config -W -f ./106-modify_acl_6.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 olcAccess
Enter LDAP Password:
dn: olcDatabase={1}mdb,cn=config
olcAccess: {0}to dn.base="cn=News Readers,ou=Groups,dc=mycompany,dc=ru" attrs=member by dnattr=member selfwrite by * break
olcAccess: {1}to dn.children="ou=People,dc=mycompany,dc=ru" attrs=userPassword by self write by group.exact="cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru" write by group/organizationalRole/roleOccupant.exact="cn=SysAdmin,ou=Roles,dc=mycompany,dc=ru" write by anonymous auth by * none
olcAccess: {2}to dn.regex="^uid=[^,]+,ou=People,(dc=[^,]+,dc=[^,]+)$" attrs=telephoneNumber,description by self write by group.expand="cn=OpenLDAP Operators,ou=Groups,$1" write by set.expand="[cn=Developer,ou=Roles,$1]/roleOccupant* & user" write by * none
olcAccess: {3}to * by self write by set="[cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru]/member* & user" write by * read
К сожалению, инструмент slapacl (по крайней мере в OpenLDAP версии 2.4.45) не справляется с таким условием ACL:
# slapacl -D uid=sidorov,ou=People,dc=mycompany,dc=ru -b 'cn=News Readers,ou=Groups,dc=mycompany,dc=ru' member/write:uid=sidorov,ou=People,dc=mycompany,dc=ru authcDN: "uid=sidorov,ou=people,dc=mycompany,dc=ru" write access to member=uid=sidorov,ou=People,dc=mycompany,dc=ru: DENIED
Поэтому протестируем его путём внесения изменений в каталог. Сначала проверим, сможет ли пользователь sidorov добавить самого себя в группу News Readers:
dn: cn=News Readers,ou=Groups,dc=mycompany,dc=ruchangetype: modifyadd: membermember: uid=sidorov,ou=People,dc=mycompany,dc=ru
Попробуем применить эти изменения:
$ ldapmodify -D uid=sidorov,ou=People,dc=mycompany,dc=ru -w sidorovPassword -f ./008-add_self_to_news_readres.ldif modifying entry "cn=News Readers,ou=Groups,dc=mycompany,dc=ru" $ ldapsearch -xLLL -b 'cn=News Readers,ou=Groups,dc=mycompany,dc=ru' member dn: cn=News Readers,ou=Groups,dc=mycompany,dc=ru member: cn=dummy_member member: uid=sidorov,ou=People,dc=mycompany,dc=ru
Всё работает как задумано. Теперь проверим, сможет ли он добавить в эту группу другого пользователя:
dn: cn=News Readers,ou=Groups,dc=mycompany,dc=ruchangetype: modifyadd: membermember: uid=petrov,ou=People,dc=mycompany,dc=ru
Попробуем применить эти изменения:
$ ldapmodify -D uid=sidorov,ou=People,dc=mycompany,dc=ru -w sidorovPassword -f ./009-add_another_to_news_readers.ldif modifying entry "cn=News Readers,ou=Groups,dc=mycompany,dc=ru" ldap_modify: Insufficient access (50)
Опять же, результат ожидаем: прав для внесения подобных изменений не хватает. Наконец проверим, сможет ли sidorov удалить себя из членов группы News Readers:
dn: cn=News Readers,ou=Groups,dc=mycompany,dc=ruchangetype: modifydelete: membermember: uid=sidorov,ou=People,dc=mycompany,dc=ru
Попробуем применить эти изменения:
$ ldapmodify -D uid=sidorov,ou=People,dc=mycompany,dc=ru -w sidorovPassword -f ./010-del_self_from_news_readers.ldif modifying entry "cn=News Readers,ou=Groups,dc=mycompany,dc=ru" $ ldapsearch -xLLL -b 'cn=News Readers,ou=Groups,dc=mycompany,dc=ru' member dn: cn=News Readers,ou=Groups,dc=mycompany,dc=ru member: cn=dummy_member
Все наши тесты прошли успешно. Теперь составим аналогичный ACL для класса organizationalRole. Предположим, для доступа к корпоративному wiki-контенту требуется получить роль Wiki Reader:
dn: cn=Wiki Reader,ou=Roles,dc=mycompany,dc=ruobjectClass: organizationalRolecn: Wiki Reader
Добавим эту запись в каталог:
$ ldapadd -D uid=ivanov,ou=People,dc=mycompany,dc=ru -w ivanovPassword -f ./011-add_wiki_reader_role.ldif adding new entry "cn=Wiki Reader,ou=Roles,dc=mycompany,dc=ru"
Чтобы любой желающий мог получить доступ к корпоративному wiki, предоставим пользователям добавлять и удалять DN своих записей в атрибут roleOccupant записи нашей роли. Составим такой ACL (по аналогии с предыдущим):
dn: olcDatabase={1}mdb,cn=configchangetype: modifyadd: olcAccessolcAccess: {0}to dn.base="cn=Wiki Reader,ou=Roles,dc=mycompany,dc=ru" attrs=roleOccupantby dnattr=roleOccupant selfwriteby * break
Поскольку у этого ACL индекс 0, он будет оцениваться первым в списке ACL. Применим наши изменения и проверим результат:
$ ldapmodify -D cn=config -W -f ./107-modify_acl_7.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 olcAccess
Enter LDAP Password:
dn: olcDatabase={1}mdb,cn=config
olcAccess: {0}to dn.base="cn=Wiki Reader,ou=Roles,dc=mycompany,dc=ru" attrs=roleOccupant by dnattr=roleOccupant selfwrite by * break
olcAccess: {1}to dn.base="cn=News Readers,ou=Groups,dc=mycompany,dc=ru" attrs=member by dnattr=member selfwrite by * break
olcAccess: {2}to dn.children="ou=People,dc=mycompany,dc=ru" attrs=userPassword by self write by group.exact="cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru" write by group/organizationalRole/roleOccupant.exact="cn=SysAdmin,ou=Roles,dc=mycompany,dc=ru" write by anonymous auth by * none
olcAccess: {3}to dn.regex="^uid=[^,]+,ou=People,(dc=[^,]+,dc=[^,]+)$" attrs=telephoneNumber,description by self write by group.expand="cn=OpenLDAP Operators,ou=Groups,$1" write by set.expand="[cn=Developer,ou=Roles,$1]/roleOccupant* & user" write by * none
olcAccess: {4}to * by self write by set="[cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru]/member* & user" write by * read
Для тестирования этого ACL выполним модификации каталога, аналогичные тем, которые мы проводили для группы News Readers (будем выполнять их от имени пользователя sidorov):
# Модификация 1: добавление себя в группуdn: cn=Wiki Reader,ou=Roles,dc=mycompany,dc=ruchangetype: modifyadd: roleOccupantroleOccupant: uid=sidorov,ou=People,dc=mycompany,dc=ru# Модификация 2: добавление другого человека в группуdn: cn=Wiki Reader,ou=Roles,dc=mycompany,dc=ruchangetype: modifyadd: roleOccupantroleOccupant: uid=petrov,ou=People,dc=mycompany,dc=ru# Модификация 3: удаление себя из группыdn: cn=Wiki Reader,ou=Roles,dc=mycompany,dc=ruchangetype: modifydelete: roleOccupantroleOccupant: uid=sidorov,ou=People,dc=mycompany,dc=ru
Попробуем выполнить эти модификации:
$ ldapmodify -c -D uid=sidorov,ou=People,dc=mycompany,dc=ru -w sidorovPassword -f ./012-mod_wiki_reader.ldif modifying entry "cn=Wiki Reader,ou=Roles,dc=mycompany,dc=ru" modifying entry "cn=Wiki Reader,ou=Roles,dc=mycompany,dc=ru" ldap_modify: Insufficient access (50) modifying entry "cn=Wiki Reader,ou=Roles,dc=mycompany,dc=ru"
Аргумент -c команды ldapmodify позволяет продолжить выполнение модификации каталога, если какая-то из операций завершится неудачно.
Все наши тесты завершились, как ожидалось. На этом мы практически закончили рассмотрение возможных групповых условий в ACL OpenLDAP.
Ежедневная рутина системного администратора предполагает, кроме всего прочего, ведение базы учётных записей пользователей и их привилегий доступа к информационным ресурсам организации. С точки зрения каталога это означает добавление/удаление/модификацию записей пользователей, а также поддержание в актуальном состоянии членства в записях-группах. И если с добавлением новых членов в записи-группы обычно проблем не возникает, то необходимость изменения атрибутов членства в многочисленных группах при удалении учётной записи пользователя или изменении её DN нередко выпадает из поля зрения администраторов. Испокон веков для решения этой проблемы администраторы использовали различные скрипты. Разработчики OpenLDAP предлагают своё решение — наложение refint.
Суть работы этого наложения заключается в поддержании целостности "ссылочных" атрибутов, то есть тех атрибутов, значениями которых являются DN записей каталога. При изменении DN записи каталога, либо при удалении записи из каталога, соответствующие изменения будут внесены в значения тех "ссылочных" атрибутов, на слежение за которыми было настроено наложение refint.
Как видно, это наложение идеально подходит для атрибутов членства в группах: member, uniqueMember и roleOccupant. Но оно может быть использовано и с любыми другими атрибутами с синтаксисом DN: manager, secretary, seeAlso и другими. Поэтому, для того, чтобы сделать примеры настроек более интересными, назначим группам из нашего каталога "владельцев" (атрибут owner):
dn: cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ruchangetype: modifyadd: ownerowner: uid=ivanov,ou=People,dc=mycompany,dc=rudn: cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ruchangetype: modifyadd: ownerowner: uid=antonova,ou=People,dc=mycompany,dc=rudn: cn=SysAdmin,ou=Roles,dc=mycompany,dc=ruchangetype: modifyadd: objectclassobjectclass: extensibleObject-add: ownerowner: uid=ivanov,ou=People,dc=mycompany,dc=rudn: cn=Developer,ou=Roles,dc=mycompany,dc=ruchangetype: modifyadd: objectclassobjectclass: extensibleObject-add: ownerowner: uid=petrov,ou=People,dc=mycompany,dc=rudn: cn=News Readers,ou=Groups,dc=mycompany,dc=ruchangetype: modifyadd: ownerowner: uid=sidorov,ou=People,dc=mycompany,dc=rudn: cn=Wiki Reader,ou=Roles,dc=mycompany,dc=ruchangetype: modifyadd: objectclassobjectclass: extensibleObject-add: ownerowner: uid=sidorov,ou=People,dc=mycompany,dc=ru
У объектного класса organizationalRole нет атрибута owner, чтобы добавить этот атрибут в запись пришлось воспользоваться специальным классом extensibleObject.
Внесём изменения и проверим:
$ ldapmodify -D uid=ivanov,ou=People,dc=mycompany,dc=ru -w ivanovPassword -f ./013-set_groups_owners.ldif modifying entry "cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru" modifying entry "cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru" modifying entry "cn=SysAdmin,ou=Roles,dc=mycompany,dc=ru" modifying entry "cn=Developer,ou=Roles,dc=mycompany,dc=ru" modifying entry "cn=News Readers,ou=Groups,dc=mycompany,dc=ru" modifying entry "cn=Wiki Reader,ou=Roles,dc=mycompany,dc=ru" $ ldapsearch -xLLL -b dc=mycompany,dc=ru '(owner=*)' owner dn: cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru owner: uid=ivanov,ou=People,dc=mycompany,dc=ru dn: cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru owner: uid=antonova,ou=People,dc=mycompany,dc=ru dn: cn=SysAdmin,ou=Roles,dc=mycompany,dc=ru owner: uid=ivanov,ou=People,dc=mycompany,dc=ru dn: cn=Developer,ou=Roles,dc=mycompany,dc=ru owner: uid=petrov,ou=People,dc=mycompany,dc=ru dn: cn=News Readers,ou=Groups,dc=mycompany,dc=ru owner: uid=sidorov,ou=People,dc=mycompany,dc=ru dn: cn=Wiki Reader,ou=Roles,dc=mycompany,dc=ru owner: uid=sidorov,ou=People,dc=mycompany,dc=ru
Теперь приступим к настройке наложения refint. Для начала подгрузим соответствующий динамический модуль:
dn: cn=module{0},cn=configchangetype: modifyadd: olcModuleLoadolcModuleLoad: refint.la
Применим изменения и проверим:
$ ldapmodify -D cn=config -W -f ./108-add_refint_module.ldif
Enter LDAP Password:
modifying entry "cn=module{0},cn=config"
$ ldapsearch -xLLL -o ldif-wrap=no -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}refint.la
А теперь настроим слежение за целостностью атрибутов member, roleOccupant и owner в каталоге dc=mycompany,dc=ru:
dn: olcOverlay=refint,olcDatabase={1}mdb,cn=configobjectClass: olcRefintConfigolcOverlay: refintolcRefintAttribute: memberolcRefintAttribute: roleOccupantolcRefintAttribute: ownerolcRefintNothing: cn=dummy_member
В данном случае мы указали два атрибута настроек наложения: olcRefintAttribute и olcRefintNothing. В первом из них задаются имена атрибутов, за целостностью значений которых будет следить наложение. Во втором атрибуте задаётся DN, которое будет подставлено в качестве значения отслеживаемого атрибута в случае, если все реальные значения этого атрибута будут удалены. Как уже упоминалось выше, некоторые объектные классы, например groupOfNames, требуют обязательного наличия атрибута членства, и при удалении всех членов возникнет нарушение целостности схемы данных каталога. Для предотвращения такой ситуации как раз и предназначена настройка, выполняемая с помощью атрибута olcRefintNothing. Значением этого атрибута должно быть правильно сформированное DN, в нашем случае cn=dummy_member.
Применим изменения и проверим:
$ ldapadd -D cn=config -W -f ./109-add_refint_overlay.ldif
Enter LDAP Password:
adding new entry "olcOverlay=refint,olcDatabase={1}mdb,cn=config"
$ ldapsearch -xLLL -o ldif-wrap=no -D cn=config -W -b olcOverlay={0}refint,olcDatabase={1}mdb,cn=config
Enter LDAP Password:
dn: olcOverlay={0}refint,olcDatabase={1}mdb,cn=config
objectClass: olcRefintConfig
olcOverlay: {0}refint
olcRefintAttribute: member
olcRefintAttribute: roleOccupant
olcRefintAttribute: owner
olcRefintNothing: cn=dummy_member
Теперь посмотрим как это работает. Предположим, что из организации уволился администратор Иван Иванов, и мы собираемся удалить его учётную запись uid=ivanov,ou=People,dc=mycompany,dc=ru. Но сначала проверим, в какие группы входит этот пользователь:
$ ldapsearch -xLLL -b dc=mycompany,dc=ru '(|(member=uid=ivanov,ou=People,dc=mycompany,dc=ru)(roleOccupant=uid=ivanov,ou=People,dc=mycompany,dc=ru)(owner=uid=ivanov,ou=People,dc=mycompany,dc=ru))' dn: cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru objectClass: groupOfNames cn: OpenLDAP Admins member: uid=ivanov,ou=People,dc=mycompany,dc=ru owner: uid=ivanov,ou=People,dc=mycompany,dc=ru dn: cn=SysAdmin,ou=Roles,dc=mycompany,dc=ru objectClass: organizationalRole objectClass: extensibleObject cn: SysAdmin roleOccupant: uid=ivanov,ou=People,dc=mycompany,dc=ru roleOccupant: uid=petrov,ou=People,dc=mycompany,dc=ru owner: uid=ivanov,ou=People,dc=mycompany,dc=ru
Итак, у нас есть две таких группы. Обратим внимание, что в обоих группах пользователь ivanov также является "владельцем" группы (его запись указана в атрибуте owner).
Теперь перейдём к удалению записи из каталога. Его можно произвести разными способами. В данном случае мы будем выполнять его с помощью утилиты ldapmodify. Сформируем LDIF для удаления:
dn: uid=ivanov,ou=People,dc=mycompany,dc=ruchangetype: delete
И применим его:
$ ldapmodify -D uid=antonova,ou=People,dc=mycompany,dc=ru -w antonovaPassword -f ./014-delete_user_ivanov.ldif deleting entry "uid=ivanov,ou=People,dc=mycompany,dc=ru"
Мы выполняли удаление от имени пользователя antonova. Однако, можно было бы выполнить его и от имени пользователя ivanov, пока его запись ещё присутствовала в каталоге.
Теперь проверим, что произошло с групповыми записями:
$ ldapsearch -xLLL -b dc=mycompany,dc=ru '(|(member=uid=ivanov,ou=People,dc=mycompany,dc=ru)(roleOccupant=uid=ivanov,ou=People,dc=mycompany,dc=ru)(owner=uid=ivanov,ou=People,dc=mycompany,dc=ru))' $ ldapsearch -xLLL -b dc=mycompany,dc=ru '(cn=*Admin*)' dn: cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru objectClass: groupOfNames cn: OpenLDAP Admins owner: cn=dummy_member member: cn=dummy_member dn: cn=SysAdmin,ou=Roles,dc=mycompany,dc=ru objectClass: organizationalRole objectClass: extensibleObject cn: SysAdmin roleOccupant: uid=petrov,ou=People,dc=mycompany,dc=ru owner: cn=dummy_member
Первый поисковый запрос ничего не вернул, поскольку при удалении записи uid=ivanov,ou=People,dc=mycompany,dc=ru сработало наложение refint и были удалены соответствующие значения тех атрибутов, которые оно отслеживало. Всё закономерно. Вторым запросом мы проверяем содержимое записей интересующих нас групп и убеждаемся, что наложение refint отработало, как и было настроено: атрибуты-ссылки на удалённую запись пользователя были удалены.
Также очевидно, что сработала и вторая настройка нашего наложения: атрибуты, для которых было удалено последнее значение в записи, получили "значение по умолчанию" cn=dummy_member. Если для атрибута member это необходимо (для соблюдения требования схемы данных), то для атрибутов roleOccupant и owner — совершенно необязательно. Чтобы такого не происходило, мы можем разделить настройки наложения refint, определив "значение по умолчанию" только для тех атрибутов, которым это действительно необходимо, а остальные отслеживаемые атрибуты оставить без такой подстановки. Но сначала приведём в порядок наши группы, заменив фиктивных членов и владельцев реальными:
# Замена члена и владельца группы OpenLDAP Adminsdn: cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ruchangetype: modifyreplace: membermember: uid=antonova,ou=People,dc=mycompany,dc=ru-replace: ownerowner: uid=antonova,ou=People,dc=mycompany,dc=ru# Замена владельца роли SysAdmindn: cn=SysAdmin,ou=Roles,dc=mycompany,dc=ruchangetype: modifyreplace: ownerowner: uid=petrov,ou=People,dc=mycompany,dc=ru
Применим изменения и проверим:
$ ldapmodify -D uid=antonova,ou=People,dc=mycompany,dc=ru -w antonovaPassword -f ./015-replace_dn_attributes.ldif modifying entry "cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru" modifying entry "cn=SysAdmin,ou=Roles,dc=mycompany,dc=ru" $ ldapsearch -xLLL -b dc=mycompany,dc=ru '(cn=*Admin*)' dn: cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru objectClass: groupOfNames cn: OpenLDAP Admins member: uid=antonova,ou=People,dc=mycompany,dc=ru owner: uid=antonova,ou=People,dc=mycompany,dc=ru dn: cn=SysAdmin,ou=Roles,dc=mycompany,dc=ru objectClass: organizationalRole objectClass: extensibleObject cn: SysAdmin roleOccupant: uid=petrov,ou=People,dc=mycompany,dc=ru owner: uid=petrov,ou=People,dc=mycompany,dc=ru
Теперь выполним проанонсированные модификации наложения refint:
dn: olcOverlay={0}refint,olcDatabase={1}mdb,cn=configchangetype: modifyreplace: olcRefintAttributeolcRefintAttribute: memberdn: olcOverlay={1}refint,olcDatabase={1}mdb,cn=configchangetype: addobjectClass: olcRefintConfigolcOverlay: {1}refintolcRefintAttribute: roleOccupantolcRefintAttribute: owner
Мы модифицируем уже имеющееся наложение refint и добавляем ещё одно. Удобнее будет рассмотреть получившиеся в итоге этих изменений записи, поэтому сначала применим изменения и выведем результаты:
$ ldapmodify -D cn=config -W -f ./110-modify_refint_overlay.ldif
Enter LDAP Password:
modifying entry "olcOverlay={0}refint,olcDatabase={1}mdb,cn=config"
adding new entry "olcOverlay={1}refint,olcDatabase={1}mdb,cn=config"
$ ldapsearch -xLLL -o ldif-wrap=no -D cn=config -W -b cn=config '(objectClass=olcRefintConfig)'
Enter LDAP Password:
dn: olcOverlay={0}refint,olcDatabase={1}mdb,cn=config
objectClass: olcRefintConfig
olcOverlay: {0}refint
olcRefintNothing: cn=dummy_member
olcRefintAttribute: member
dn: olcOverlay={1}refint,olcDatabase={1}mdb,cn=config
objectClass: olcRefintConfig
olcOverlay: {1}refint
olcRefintAttribute: roleOccupant
olcRefintAttribute: owner
Как мы видим, в первом наложении отслеживается атрибут member, и для него задано "значение по умолчанию" cn=dummy_member. Во втором наложении отслеживаются атрибуты roleOccupant и owner, и "значение по умолчанию" для них не задаётся. Именно такие настройки мы и обсуждали.
Посмотрим, как это работает на примере удаления записи пользователя petrov. На этот раз мы будем выполнять удаление с помощью утилиты ldapdelete. Перед удалением и после его выполнения отследим наличие "ссылочных" атрибутов в записях-группах, в которых этот пользователь был членом или владельцем (как в предыдущем случае):
$ ldapsearch -xLLL -b dc=mycompany,dc=ru '(|(member=uid=petrov,ou=People,dc=mycompany,dc=ru)(roleOccupant=uid=petrov,ou=People,dc=mycompany,dc=ru)(owner=uid=petrov,ou=People,dc=mycompany,dc=ru))' dn: cn=SysAdmin,ou=Roles,dc=mycompany,dc=ru objectClass: organizationalRole objectClass: extensibleObject cn: SysAdmin roleOccupant: uid=petrov,ou=People,dc=mycompany,dc=ru owner: uid=petrov,ou=People,dc=mycompany,dc=ru dn: cn=Developer,ou=Roles,dc=mycompany,dc=ru objectClass: organizationalRole objectClass: extensibleObject cn: Developer roleOccupant: uid=antonova,ou=People,dc=mycompany,dc=ru roleOccupant: uid=sidorov,ou=People,dc=mycompany,dc=ru roleOccupant: cn=SysAdmin,ou=Roles,dc=mycompany,dc=ru owner: uid=petrov,ou=People,dc=mycompany,dc=ru $ ldapdelete -D uid=antonova,ou=People,dc=mycompany,dc=ru -w antonovaPassword uid=petrov,ou=People,dc=mycompany,dc=ru $ ldapsearch -xLLL -b dc=mycompany,dc=ru '(|(member=uid=petrov,ou=People,dc=mycompany,dc=ru)(roleOccupant=uid=petrov,ou=People,dc=mycompany,dc=ru)(owner=uid=petrov,ou=People,dc=mycompany,dc=ru))' $ ldapsearch -xLLL -b dc=mycompany,dc=ru '(|(cn=SysAdmin)(cn=Developer))' dn: cn=SysAdmin,ou=Roles,dc=mycompany,dc=ru objectClass: organizationalRole objectClass: extensibleObject cn: SysAdmin dn: cn=Developer,ou=Roles,dc=mycompany,dc=ru objectClass: organizationalRole objectClass: extensibleObject cn: Developer roleOccupant: uid=antonova,ou=People,dc=mycompany,dc=ru roleOccupant: uid=sidorov,ou=People,dc=mycompany,dc=ru roleOccupant: cn=SysAdmin,ou=Roles,dc=mycompany,dc=ru
Результаты эксперимента показывают, что, во-первых, наложение отработало корректно при удалении "ссылочных" атрибутов (roleOccupant и owner), а во-вторых, излишней подстановки "значения по умолчанию" для этих атрибутов не произошло (роль SysAdmin, например, осталась без владельца и без членов).
Наконец, проверим поведение наложения refint при переименовании записи пользователя в каталоге. Предположим, что Тоня Антонова вышла замуж и взяла фамилию мужа. LDIF для внесения изменений в каталог:
# Переименование записи antonovadn: uid=antonova,ou=People,dc=mycompany,dc=ruchangetype: modrdnnewrdn: uid=zhukovadeleteoldrdn: 1# Модификация записи zhukovadn: uid=zhukova,ou=People,dc=mycompany,dc=ruchangetype: modifyreplace: cncn: Antonina Zhukova-replace: snsn: Zhukova-replace: userPassworduserPassword: zhukovaPassword
Как и в предыдущих случаях, выведем содержимое групповых записей до и после изменений:
$ ldapsearch -xLLL -b dc=mycompany,dc=ru '(|(member=uid=antonova,ou=People,dc=mycompany,dc=ru)(roleOccupant=uid=antonova,ou=People,dc=mycompany,dc=ru)(owner=uid=antonova,ou=People,dc=mycompany,dc=ru))' dn: cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru objectClass: groupOfNames cn: OpenLDAP Admins member: uid=antonova,ou=People,dc=mycompany,dc=ru owner: uid=antonova,ou=People,dc=mycompany,dc=ru dn: cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru objectClass: groupOfNames cn: OpenLDAP Operators member: uid=antonova,ou=People,dc=mycompany,dc=ru member: cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru owner: uid=antonova,ou=People,dc=mycompany,dc=ru dn: cn=Developer,ou=Roles,dc=mycompany,dc=ru objectClass: organizationalRole objectClass: extensibleObject cn: Developer roleOccupant: uid=antonova,ou=People,dc=mycompany,dc=ru roleOccupant: uid=sidorov,ou=People,dc=mycompany,dc=ru roleOccupant: cn=SysAdmin,ou=Roles,dc=mycompany,dc=ru $ ldapmodify -D uid=antonova,ou=People,dc=mycompany,dc=ru -w antonovaPassword -f ./016-rename_antonova.ldif modifying rdn of entry "uid=antonova,ou=People,dc=mycompany,dc=ru" modifying entry "uid=zhukova,ou=People,dc=mycompany,dc=ru" $ ldapsearch -xLLL -b dc=mycompany,dc=ru '(|(member=uid=antonova,ou=People,dc=mycompany,dc=ru)(roleOccupant=uid=antonova,ou=People,dc=mycompany,dc=ru)(owner=uid=antonova,ou=People,dc=mycompany,dc=ru))' $ ldapsearch -xLLL -b dc=mycompany,dc=ru '(|(member=uid=zhukova,ou=People,dc=mycompany,dc=ru)(roleOccupant=uid=zhukova,ou=People,dc=mycompany,dc=ru)(owner=uid=zhukova,ou=People,dc=mycompany,dc=ru))' dn: cn=Developer,ou=Roles,dc=mycompany,dc=ru objectClass: organizationalRole objectClass: extensibleObject cn: Developer roleOccupant: uid=sidorov,ou=People,dc=mycompany,dc=ru roleOccupant: cn=SysAdmin,ou=Roles,dc=mycompany,dc=ru roleOccupant: uid=zhukova,ou=People,dc=mycompany,dc=ru dn: cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru objectClass: groupOfNames cn: OpenLDAP Admins owner: uid=zhukova,ou=People,dc=mycompany,dc=ru member: uid=zhukova,ou=People,dc=mycompany,dc=ru dn: cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru objectClass: groupOfNames cn: OpenLDAP Operators member: cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru member: uid=zhukova,ou=People,dc=mycompany,dc=ru owner: uid=zhukova,ou=People,dc=mycompany,dc=ru
Итак, все наши тесты прошли успешно и показали, что наложение refint — элегантный, эффективный и гибкий инструмент для автоматизации рутинных процессов системного администрирования.
Остаётся добавить, что модификации, выполняемые этим наложением, не распространяются при репликации каталога. Поэтому для корректной работы в каталогах с репликацией наложение refint должно быть идентичным образом настроено на всех поставщиках и потребителях репликации.
Прежде чем подводить итоги, вернёмся ненадолго к нашим ACL. В связи с тем, что у наших групп появились владельцы, мы можем рассмотреть ещё один аспект ACL, связанный с атрибутами, имеющими синтаксис DN (мы уже опробовали конструкцию dnattr в условии by в ACL с индексами 0 и 1). Дополним эти ACL так, чтобы предоставить владельцам групп News Readers и Wiki Reader (записям, DN которых указан в атрибуте owner записей этих групп) право на добавление/удаление членов этих групп:
dn: olcDatabase={1}mdb,cn=configchangetype: modifydelete: olcAccessolcAccess: {0}-add: olcAccessolcAccess: {0}to dn.base="cn=Wiki Reader,ou=Roles,dc=mycompany,dc=ru" attrs=roleOccupantby dnattr=roleOccupant selfwriteby dnattr=owner writeby * break-delete: olcAccessolcAccess: {1}-add: olcAccessolcAccess: {1}to dn.base="cn=News Readers,ou=Groups,dc=mycompany,dc=ru" attrs=memberby dnattr=member selfwriteby dnattr=owner writeby * break
Интересующие нас условия by описаны в строках 9 и 18 данного LDIF-файла. Они достаточно очевидны и в дополнительных разъяснениях не нуждаются. Применим изменения и проверим:
$ ldapmodify -D cn=config -W -f ./111-modify_acl_8.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 '(olcAccess=*)' olcAccess
Enter LDAP Password:
dn: olcDatabase={1}mdb,cn=config
olcAccess: {0}to dn.base="cn=Wiki Reader,ou=Roles,dc=mycompany,dc=ru" attrs=roleOccupant by dnattr=roleOccupant selfwrite by dnattr=owner write by * break
olcAccess: {1}to dn.base="cn=News Readers,ou=Groups,dc=mycompany,dc=ru" attrs=member by dnattr=member selfwrite by dnattr=owner write by * break
olcAccess: {2}to dn.children="ou=People,dc=mycompany,dc=ru" attrs=userPassword by self write by group.exact="cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru" write by group/organizationalRole/roleOccupant.exact="cn=SysAdmin,ou=Roles,dc=mycompany,dc=ru" write by anonymous auth by * none
olcAccess: {3}to dn.regex="^uid=[^,]+,ou=People,(dc=[^,]+,dc=[^,]+)$" attrs=telephoneNumber,description by self write by group.expand="cn=OpenLDAP Operators,ou=Groups,$1" write by set.expand="[cn=Developer,ou=Roles,$1]/roleOccupant* & user" write by * none
olcAccess: {4}to * by self write by set="[cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru]/member* & user" write by * read
Протестируем полученные ACL. Владельцем обеих групп у нас является пользователь sidorov. Попробуем от его имени добавить другого пользователя в члены этих групп:
dn: cn=News Readers,ou=Groups,dc=mycompany,dc=ruchangetype: modifyadd: membermember: uid=zhukova,ou=People,dc=mycompany,dc=rudn: cn=Wiki Reader,ou=Roles,dc=mycompany,dc=ruchangetype: modifyadd: roleOccupantroleOccupant: uid=zhukova,ou=People,dc=mycompany,dc=ru
Применим изменения и проверим:
$ ldapmodify -D uid=sidorov,ou=People,dc=mycompany,dc=ru -w sidorovPassword -f ./017-add_zhukova_to_readers_groups.ldif modifying entry "cn=News Readers,ou=Groups,dc=mycompany,dc=ru" modifying entry "cn=Wiki Reader,ou=Roles,dc=mycompany,dc=ru" $ ldapsearch -xLLL -b dc=mycompany,dc=ru '(cn=*Reader*)' dn: cn=News Readers,ou=Groups,dc=mycompany,dc=ru objectClass: groupOfNames cn: News Readers member: cn=dummy_member member: uid=zhukova,ou=People,dc=mycompany,dc=ru owner: uid=sidorov,ou=People,dc=mycompany,dc=ru dn: cn=Wiki Reader,ou=Roles,dc=mycompany,dc=ru objectClass: organizationalRole objectClass: extensibleObject cn: Wiki Reader owner: uid=sidorov,ou=People,dc=mycompany,dc=ru roleOccupant: uid=zhukova,ou=People,dc=mycompany,dc=ru
Тест успешно пройден, наше условие работает. Остаётся добавить, что конструкцию dnattr можно использовать с любыми атрибутами с синтаксисом DN (например, manager или secretary), для предоставления прав записям, DN которых перечислены в этих атрибутах, на доступ различного уровня к абсолютно любым записям каталога (а не только записям-группам).
В ходе этого урока мы познакомились с различными вариантами групповых условий в ACL OpenLDAP, в том числе с использованием наборов для работы с "вложенными" группами. Кроме того, мы рассмотрели специальную конструкцию dnattr, позволяющую предоставлять права на доступ в зависимости от содержимого записей каталога. Наконец, мы рассмотрели разные варианты настройки наложения refint. Итоговые настройки каталога 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 dn.base="cn=Wiki Reader,ou=Roles,dc=mycompany,dc=ru" attrs=roleOccupant
by dnattr=roleOccupant selfwrite
by dnattr=owner write
by * break
olcAccess: {1}to dn.base="cn=News Readers,ou=Groups,dc=mycompany,dc=ru" attrs=member
by dnattr=member selfwrite
by dnattr=owner write
by * break
olcAccess: {2}to dn.children="ou=People,dc=mycompany,dc=ru" attrs=userPassword
by self write by group.exact="cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru" write
by group/organizationalRole/roleOccupant.exact="cn=SysAdmin,ou=Roles,dc=mycompany,dc=ru" write
by anonymous auth
by * none
olcAccess: {3}to dn.regex="^uid=[^,]+,ou=People,(dc=[^,]+,dc=[^,]+)$" attrs=telephoneNumber,description
by self write
by group.expand="cn=OpenLDAP Operators,ou=Groups,$1" write
by set.expand="[cn=Developer,ou=Roles,$1]/roleOccupant* & user" write
by * none
olcAccess: {4}to *
by self write
by set="[cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru]/member* & user" 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}refint,olcDatabase={1}mdb,cn=config
objectClass: olcRefintConfig
olcOverlay: {0}refint
olcRefintAttribute: member
olcRefintNothing: cn=dummy_member
dn: olcOverlay={1}refint,olcDatabase={1}mdb,cn=config
objectClass: olcRefintConfig
olcOverlay: {1}refint
olcRefintAttribute: roleOccupant
olcRefintAttribute: owner