Статические группы: POSIX-группы

Содержание

О POSIX-группах

Как уже было сказано ранее, объекты POSIX-групп в каталоге — это записи, построенные на объектном классе posixGroup, определённом в RFC 2307. Этот RFC был принят в далёком 1998 году с целью представления в виде записей каталога объектов сетевой информационной системы (NIS) UNIX. Как и в настоящих POSIX-группах NIS, у таких записей имеется числовой идентификатор группы (в обязательном атрибуте gidNumber), а в качестве членов в атрибутах memberUid указываются текстовые имена учётных записей пользователей UNIX:

dn: cn=Cool Users,ou=Groups,dc=mycompany,dc=ru
objectClass: posixGroup
cn: Cool Users
gidNumber: 10000
memberUid: ivanov
memberUid: petrov
memberUid: sidorov
memberUid: antonova

Как наследие NIS, объекты POSIX-групп повсеместно использовались во времена расцвета samba3, и до сих пор многие библиотеки систем NSS/PAM, взаимодействующие с каталогами LDAP, по умолчанию работают именно с такими группами.

С точки зрения каталога, объекты POSIX-групп нельзя рассматривать как записи-группы, так как они не дают возможности напрямую сопоставлять значения атрибутов членства с записями-членами группы в каталоге. Они представляют собой записи-списки, в которых перечислены текстовые имена учётных записей пользователей UNIX, а сопоставление членства в группе с реальной записью-членом в каталоге полностью возлагается на клиентское приложение. Именно поэтому с POSIX-группами не работают ориентированные на группы наложения OpenLDAP refint и memberof, их также нельзя использовать в групповых условиях ACL OpenLDAP. Тем не менее, если в каталоге всё же используются POSIX-группы, с помощью механизма наборов в ACL OpenLDAP их также можно задействовать для организации разграничения доступа к содержимому каталога.

Разграничение доступа к каталогу OpenLDAP на основе POSIX-групп

Попробуем воссоздать ситуацию, которую мы рассматривали для "обычных" групп каталога: имеются две группы, — OpenLDAP Admins и OpenLDAP Operators. Членам первой группы будет разрешено менять пароли учётных записей пользователей в каталоге, а членам второй — изменять любые данные в каталоге (за исключением паролей пользователей). Только теперь наши записи будут POSIX-группами:

# POSIX-группа "Администраторы каталога"
dn: cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru
objectClass: posixGroup
cn: OpenLDAP Admins
gidNumber: 10001
memberUid: ivanov

# POSIX-группа "Операторы каталога"
dn: cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru
objectClass: posixGroup
cn: OpenLDAP Operators
gidNumber: 10002
memberUid: antonova

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

$ ldapadd -x -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 (атрибуты olcAccess) условиями by, отражающими требования нашей задачи:

dn: olcDatabase={1}mdb,cn=config
changetype: modify
replace: olcAccess
olcAccess: to attrs=userPassword
  by self write
  by set="[cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru]/memberUid & user/uid" write
  by anonymous auth
  by * none
olcAccess: to *
  by self write
  by set="[cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru]/memberUid & user/uid" write
  by * read

Интересующие нас условия задаются в строках 6 и 11. По структуре они аналогичны, поэтому подробнее рассмотрим работу такого условия в первом из ACL.

При попытке пользователя обратиться к атрибуту userPassword какой-либо записи сработает первый ACL (строки 4-8). Если пользователь авторизован от имени той же записи, к которой он пытается обратиться (то есть пользователь обращается к своей собственной записи каталога), то сработает условие by в строке 5, и пользователь получит неограниченный доступ к атрибуту userPassword собственной записи. Пока всё просто и очевидно. Если же пользователь, уже прошедший аутентификацию (то есть ключевое слово user условия-набора окажется непустым), обращается к атрибуту userPassword, произойдёт проверка условия by в строке 6. Заключённое в кавычки условие-набор в этой строке состоит из двух поднаборов, соединённых знаком пересечения этих поднаборов (&). Вместо ключевого слова user во втором поднаборе будет подставлена запись каталога, соответствующая учётной записи пользователя, прошедшего аутентификацию, и из этой записи каталога будут извлечены значения атрибутов uid, которые и составят второй поднабор. DN записи, фигурирующей в первом поднаборе, указано явно в квадратных скобках (cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru), из этой записи будут извлечены значения атрибутов memberUid, они составят первый поднабор. Если при пересечении двух поднаборов будут найдены совпадающие значения (то есть одно из значений атрибута uid учётной записи пользователя совпадёт с одним из значений атрибута memberUid записи POSIX-группы), то условие будет считаться совпавшим, и подключившемуся пользователю будут предоставлены права на изменения атрибута userPassword. Если же такого пересечения найдено не будет, то условие by не совпадёт, и проверка перейдёт к условию by в строке 7.

Подробно работа такого условия-набора рассмотрена в разделе 8.5.2 руководства администратора OpenLDAP 2.4. Применительно к нашему примеру, пользователь uid=ivanov,ou=People,dc=mycompany,dc=ru получит доступ на изменение атрибута userPassword, поскольку значение атрибута uid его записи перечислено в значениях атрибута memberUid записи POSIX-группы cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru. Остальные же пользователи не получат такого доступа, поскольку значения их атрибутов uid не перечислены в значениях атрибута memberUid этой POSIX-группы.

Попробуем убедиться в этом. Применим изменения ACL и проверим полученную в результате запись базы данных:

$ ldapmodify -x -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 set="[cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru]/memberUid & user/uid" write by anonymous auth by * none
olcAccess: {1}to * by self write by set="[cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru]/memberUid & user/uid" write by * read

Сначала проверим работу первого из полученных ACL утилитой slapacl. Попробуем протестировать доступ к атрибуту userPassword записи uid=petrov,ou=People,dc=mycompany,dc=ru от имени пользователей ivanov и antonova:

# 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)

Наши тесты, как и ожидалось, показывают, что у пользователя ivanov достаточно привилегий для смены пароля пользователя petrov, а у пользователя antonova нет прав даже на чтение этого пароля.

Теперь проведём "натурные" испытания. Попробуем с помощью утилиты ldappasswd изменить пароль пользователя petrov от имени пользователя antonova:

$ ldappasswd -x -v -D uid=antonova,ou=People,dc=mycompany,dc=ru -w antonovaPassword -s pertovNewPassword uid=petrov,ou=People,dc=mycompany,dc=ru
ldap_initialize( <DEFAULT> )
Result: Insufficient access (50)

Попытка изменения пароля не удалась. Теперь попробуем сделать то же самое от имени пользователя ivanov:

$ ldappasswd -x -v -D uid=ivanov,ou=People,dc=mycompany,dc=ru -w ivanovPassword -s pertovNewPassword uid=petrov,ou=People,dc=mycompany,dc=ru
ldap_initialize( <DEFAULT> )
Result: Success (0)

На этот раз утилита ldappasswd отработала успешно. На всякий случай проверим, что у пользователя petrov новый пароль, выполнив простейший поиск от его имени:

$ ldapsearch -xLLL -b dc=mycompany,dc=ru -D uid=petrov,ou=People,dc=mycompany,dc=ru -w pertovNewPassword '(objectClass=posixGroup)' 1.1
dn: cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru

dn: cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru

Итак, наш первый ACL работает как ожидалось. Убедимся в работоспособности второго ACL. Начнём с тестов утилитой slapacl, проверим доступ к атрибуту cn записи uid=petrov,ou=People,dc=mycompany,dc=ru от имени пользователей ivanov и antonova:

# 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)
# 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)

Пользователь ivanov не перечислен в качестве члена POSIX-группы cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru, поэтому он получает доступ на чтение атрибута cn (как попадающий под условие by * read). Пользователь antonova перечислена в качестве члена этой POSIX-группы, поэтому она имеет доступ на изменение атрибута cn. Всё в порядке.

Выполним также реальную проверку, для чего попробуем изменить значение атрибута cn записи uid=petrov,ou=People,dc=mycompany,dc=ru:

dn: uid=petrov,ou=People,dc=mycompany,dc=ru
changetype: modify
replace: cn
cn: Petr Petrovich Petrov

Сначала попробуем применить эти изменения от имени пользователя ivanov:

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

Попытка неудачная — у пользователя ivanov недостаточно прав для осуществления модификации. Попробуем выполнить то же самое от имени пользователя antonova:

$ ldapmodify -x -D uid=antonova,ou=People,dc=mycompany,dc=ru -w antonovaPassword -f ./003-modify_petrov.ldif
modifying entry "uid=petrov,ou=People,dc=mycompany,dc=ru"

Изменения успешно применились. Убедимся в этом:

$ ldapsearch -xLLL -b uid=petrov,ou=People,dc=mycompany,dc=ru -D uid=petrov,ou=People,dc=mycompany,dc=ru -w pertovNewPassword cn
dn: uid=petrov,ou=People,dc=mycompany,dc=ru
cn: Petr Petrovich Petrov

Итак, второй наш ACL также работает. Задача решена. Но что если, по аналогии с ситуацией с "обычными" группами, мы не хотим ограничивать администраторов каталога полномочиями только изменять пароли, но хотим дать им права изменять любые атрибуты любых записей наравне с операторами каталога? По понятным причинам сценария вложенных групп и их рекурсивного обхода в случае POSIX-групп воссоздать не получится. Тем не менее, пути решения такой задачи есть. Первый, самый простой, очевидный и не требующий дополнительных перенастроек каталога — аккуратно вести членов группы OpenLDAP Operators, добавляя туда (удаляя оттуда) тех же членов, что и в группу OpenLDAP Admins:

dn: uid=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru
changetype: modify
add: memberUid
memberUid: ivanov

Но не все администраторы склонны полагаться на свою аккуратность, а тем более на аккуратность коллег по цеху. Поэтому вторым, также вполне очевидным решением, будет добавление ещё одного условия by во второй ACL:

dn: olcDatabase={1}mdb,cn=config
changetype: modify
delete: olcAccess
olcAccess: {1}
-
add: olcAccess
olcAccess: {1}to *
  by self write
  by set="[cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru]/memberUid & user/uid" write
  by set="[cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru]/memberUid & user/uid" write
  by * read

Но мы попробуем объединить два наших набора в один, заодно продемонстрировав различные варианты операторов в наборах:

dn: olcDatabase={1}mdb,cn=config
changetype: modify
delete: olcAccess
olcAccess: {1}
-
add: olcAccess
olcAccess: {1}to *
  by self write
  by set="([cn=OpenLDAP ] + ([Admins] | [Operators]) + [,ou=Groups,dc=mycompany,dc=ru])/memberUid & user/uid" write
  by * read

Рассмотрим подробно набор в строке 9. Он состоит из двух поднаборов, соединённых оператором пересечения &. Второй поднабор (после оператора &) остался прежним: он будет составлен из значений атрибута uid записи каталога, соответствующей учётной записи того пользователя, который прошёл аутентификацию при подключении к каталогу. Первый же поднабор (перед оператором &) выглядит устрашающе, тем не менее, в нём довольно просто разобраться, если делать это по частям.

Перед началом разбора обратим внимание на строковые значения, заключённые в квадратные скобки. Если они не представляют собой валидное DN (как во всех наших предыдущих примерах с условиями-наборами), то воспринимаются механизмом разборки ACL OpenLDAP не как указатель на запись каталога, а как обычные строковые литералы. Итак, в данном случае cn=OpenLDAP , Admins, Operators и ,ou=Groups,dc=mycompany,dc=ru — это просто строки, из которых мы будем составлять условие поднабора.

Далее нам следует познакомиться с двумя ранее не рассмотренными операторами механизма наборов ACL OpenLDAP. Оператор объединения | предназначен для формирования единого набора из двух смежных с ним поднаборов. Оператор конкатенации + предназначен для "склеивания" значений двух смежных поднаборов, причём в итоговом наборе все значения первого поднабора будут "склеены" со всеми значениями второго поднабора, то есть количество значений в итоговом наборе будет равно произведению количества значений первого поднабора на количество значений второго поднабора. Как и арифметические операторы, операторы механизма наборов ACL OpenLDAP имеют приоритет выполнения: у &, | и + он одинаков, а вот оператор "действия" / имеет более высокий приоритет. По аналогии с арифметикой, для изменения порядка применения операторов используются круглые скобки.

Вооружившись этими нехитрыми знаниями, можно приступать к разбору первого поднабора из нашего примера. Самое приоритетное действие у нас во внутренних круглых скобках: ([Admins] | [Operators]). Здесь создаётся набор, представляющий собой объединение двух строковых значений:

Admins
Operators

Далее полученный набор с помощью первого оператора конкатенации + "склеивается" с набором, состоящим из одного строкового элемента cn=OpenLDAP . В итоге получаем такой набор:

cn=OpenLDAP Admins
cn=OpenLDAP Operators

Далее с помощью второго оператора конкатенации + этот набор "склеивается" с ещё одним набором, также состоящим из одного строкового элемента ,ou=Groups,dc=mycompany,dc=ru. В итоге получаем такой набор:

cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru
cn=OpenLDAP Operators,ou=Groups,dc=mycompany,dc=ru

На этом "высокоприоритетные" действия во внешних фигурных скобках заканчиваются. В итоге мы получили набор, состоящий из двух валидных DN, которые можно использовать для указания на две записи каталога, в данном случае на наши записи POSIX-групп. Далее с помощью оператора действия / из этих записей будут извлечены значения атрибутов memberUid, которые и составят итоговый первый поднабор нашего условия-набора:

ivanov
antonova

Дальнейшая работа условия-набора зависит от пользователя, выполняющего операцию в каталоге. Если в записи каталога, соответствующей учётке этого пользователя, значением атрибута uid будет ivanov или antonova, то пересечение (&) с первым поднабором будет найдено, и условие сработает.

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

$ ldapmodify -x -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 attrs=userPassword by self write by set="[cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru]/memberUid & user/uid" write by anonymous auth by * none
olcAccess: {1}to * by self write by set="([cn=OpenLDAP ] + ([Admins] | [Operators]) + [,ou=Groups,dc=mycompany,dc=ru])/memberUid & user/uid" write by * read

Протестируем работу полученного ACL утилитой slapacl. Проверим доступ к атрибуту cn записи uid=petrov,ou=People,dc=mycompany,dc=ru от имени пользователей ivanov, antonova и sidorov:

# 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)
# 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=sidorov,ou=People,dc=mycompany,dc=ru -b uid=petrov,ou=People,dc=mycompany,dc=ru cn
authcDN: "uid=sidorov,ou=people,dc=mycompany,dc=ru"
cn: read(=rscxd)

Первые два пользователя, являясь членами POSIX-групп OpenLDAP Admins и OpenLDAP Operators, получили доступ на изменение атрибута cn, а пользователь sidorov, не являющийся членом этих групп, получил доступ только на чтение атрибута (согласно условию by * read). Наш ACL работает.

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

В ходе этого урока мы познакомились с записями каталога, представляющими собой объекты POSIX-групп, их предназначением, а также механизмом, позволяющим использовать эти объекты для разграничения доступа к самому каталогу OpenLDAP. Более подробно мы остановились на разных вариантах построения условий-наборов в 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 set="[cn=OpenLDAP Admins,ou=Groups,dc=mycompany,dc=ru]/memberUid & user/uid" write
  by anonymous auth
  by * none
olcAccess: {1}to *
  by self write
  by set="([cn=OpenLDAP ] + ([Admins] | [Operators]) + [,ou=Groups,dc=mycompany,dc=ru])/memberUid & user/uid" write
  by * read
olcRootDN: cn=manager,ou=System,dc=mycompany,dc=ru
olcRootPW: {SSHA}PKFrwbIL/zLd3gabPPLxn1vNq2jQHj4g
olcDbIndex: objectClass eq
olcDbIndex: cn eq,sub,subinitial
Pro-LDAP.ru 2013-2018 г. Последнее изменение страницы — 14 июня 2018 г. Вопросы и предложения принимаются на форуме проекта.