Application: "389 Administration & Directory Server v.1.3.7".
Задача: развернуть и настроить LDAP-сервер "389-AS/DS", с последующим созданием заготовки структуры записей и учётных записей для управления таковыми, а также активацией необходимых плагинов и схем данных.
Последовательность дальнейших действий такова:
1. Подготовка системного окружения (отдельная инструкция).
2. Установка и первичная настройка.
3. Создание простой структуры записей данных.
4. Запрещение анонимного доступа к данным LDAP.
5. Разрешение анонимного поиска по выделенным атрибутам.
6. Разрешение пользователям читать и корректировать свои профили.
7. Создание учётных записей администраторов DIT.
8. Создание учётных записей операторов DIT.
9. Создание сервисных учётных записей.
10. Оптимизация процедур выявления групповой принадлежности.
11. Оптимизация процедуры выделения "Posix UID & GID".
12. Добавление атрибутов и классов в схему данных.
13. Экспорт и импорт данных.
2. Установка и первичная настройка.
3. Создание простой структуры записей данных.
4. Запрещение анонимного доступа к данным LDAP.
5. Разрешение анонимного поиска по выделенным атрибутам.
6. Разрешение пользователям читать и корректировать свои профили.
7. Создание учётных записей администраторов DIT.
8. Создание учётных записей операторов DIT.
9. Создание сервисных учётных записей.
10. Оптимизация процедур выявления групповой принадлежности.
11. Оптимизация процедуры выделения "Posix UID & GID".
12. Добавление атрибутов и классов в схему данных.
13. Экспорт и импорт данных.
Установка и первичная настройка.
Дистрибутив LDAP-сервера "389-AS/DS" состоит из нескольких компонентов:
1. Сервер администрирования "Administration Server". Это web-сервис для конфигурирования, состоящий из "Apache2" и модулей взаимодействия с "ns-slapd".
2. Непосредственно сервер каталогов "Directory Server". Это приложение "ns-slapd", принимающее и обрабатывающее пользовательские запросы.
3. GUI-консоль администрирования. Это java-приложение, подключающееся к "Administration Server" и позволяющее производить настройки через удобный интерфейс.
2. Непосредственно сервер каталогов "Directory Server". Это приложение "ns-slapd", принимающее и обрабатывающее пользовательские запросы.
3. GUI-консоль администрирования. Это java-приложение, подключающееся к "Administration Server" и позволяющее производить настройки через удобный интерфейс.
LDAP-сервер "389-AS/DS" выпускается в двух вариантах: коммерческий "Red Hat Directory Server (RHDS)" и бесплатная реализация "Fedora Directory Server (FDS)" на полностью открытых компонентах. В дистрибутивах вроде "Linux Debian/Ubuntu" поставляется только FDS, которая просто называется "389 Directory Server".
Нумерация дистрибутивов у RHDS и FDS различается, но чётко сопоставима:
"FDS v1.0" соответствует "RHDS v7".
"FDS v1.1" соответствует "RHDS v8" (наиболее подробная документация).
"FDS v1.2" соответствует "RHDS v9".
"FDS v1.3" соответствует "RHDS v10" (документация, документация).
"FDS v1.1" соответствует "RHDS v8" (наиболее подробная документация).
"FDS v1.2" соответствует "RHDS v9".
"FDS v1.3" соответствует "RHDS v10" (документация, документация).
Установим компоненты LDAP-сервера в реализации "389 Directory Server (1.3.7)":
# apt-get update && apt-get upgrade
# apt-get --no-install-recommends --no-install-suggests install 389-ds-base 389-admin 389-admin-console ldap-utils net-tools
# apt-get --no-install-recommends --no-install-suggests install 389-ds-base 389-admin 389-admin-console ldap-utils net-tools
Для установки были выбраны лишь необходимые APT-пакеты:
"389-ds-base" - 389 Directory Server.
"389-admin" - 389 Administration Server.
"389-admin-console" - Java components for 389-AS.
"ldap-utils" - OpenLDAP utilities.
"net-tools" - Misc networking toolkit.
"389-admin" - 389 Administration Server.
"389-admin-console" - Java components for 389-AS.
"ldap-utils" - OpenLDAP utilities.
"net-tools" - Misc networking toolkit.
Опционально можно установить компоненты для сопроводительных утилит ("dsconf", "dscreate", "dsctl" & etc.), написанных на Python:
# apt-get install python3-ldap python3-lib389
Одна из зависимостей дистрибутива "389 Administration Server" - web-сервер "Apache2", который сразу после установки запускается, в дальнейшем вхолостую прослушивая порт TCP:80. Если этот web-сервер нигде более не задействован, то его следует сразу отключить:
# systemctl stop apache2
# systemctl disable apache2
# systemctl disable apache2
Перед установкой и вводом в работу LDAP-сервиса есть смысл проверить готовность несущей операционной системы - соответствие её параметров рекомендациям разработчиков. Для этого в дистрибутиве имеется соответствующая утилита:
# dsktune
....
NOTICE : The net.ipv4.tcp_keepalive_time is set to 7200000 milliseconds (120 minutes).
....
WARNING: There are only 1024 file descriptors (soft limit) available...
NOTICE : The net.ipv4.tcp_keepalive_time is set to 7200000 milliseconds (120 minutes).
....
WARNING: There are only 1024 file descriptors (soft limit) available...
В моём случае предупреждений два - очень долгое время ожидания активности в открытом соединении (позволит легко обеспечить DDoS) и слишком жёсткое ограничение на количество одновременно открытых файлов, что может затормаживать работу сервиса в ожидании высвобождения ресурсов.
Уменьшаем время жизни неактивного TCP-соединения до пяти минут:
# vi /etc/sysctl.d/30-tcp-keepalive.conf
# Reduce connection timeout (for 389-AS/DS)
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_keepalive_time = 300
# sysctl -p -f /etc/sysctl.d/30-tcp-keepalive.conf
Увеличиваем лимит количества одновременно открытых файлов (отдельная запись для "root" - обход специального системного ограничения):
# /etc/security/limits.d/30-nofile.conf
# Expand the limit for 389-AS/DS
root - nofile 8192
* - nofile 8192
root - nofile 8192
* - nofile 8192
Утилита конфигурации LDAP-сервера от "RedHat" требовательна к сетевой адресации - в процессе понадобится указать действительное доменное имя (FQDN), которое используется для приёма запросов к сервису. Если нет возможности зарегистрировать символическое имя в системе DNS, то придётся объявить его связку с IP-адресом локально (внутри несущего сервера):
# vi /etc/hosts
....
127.0.0.1 ldap0.example.net ldap0
....
127.0.0.1 ldap0.example.net ldap0
....
Запускаем интерактивный конфигуратор "389 Administration & Directory Server" (написан на "Perl"):
# setup-ds-admin
В процессе подготовки конфигурации задаётся несколько вопросов на простом английском - приведу подсказки и примеры ответов на них:
Choose a setup type [2]: 3
# Уровень детализации запросов конфигуратора (наш вариант "3 - Custom").
....
Computer name [ldap0]: ldap0.example.net
# FQDN несущего LDAP-сервис компьютера (в примере "ldap0.example.net").
....
System User [dirsrv]:
System Group [dirsrv]:
# Пользователь и группа, в контексте которых будет работать сервис (по умолчанию "dirsrv").
....
Do you want to register this software with an existing configuration directory server? [no]: no
# Адрес для подключения к возможно имеющемуся уже серверу администрирования (один сервер "389-AS" может управлять несколькими инстансами "389-DS" - до четырёх, но сейчас нам это не нужно, так как мы запускаем полностью автономную схему).
....
Configuration directory server administrator ID [admin]: admin
# Задаём логин и пароль для локального администратора развёртываемого сервиса "389-AS/DS" (проще именовать традиционно "admin"; это будет почти суперпользователь, наделяемый правами через ACL).
....
Administration Domain [example.net]: local
# (ВАЖНО) Задаём идентификатор структуры набора параметров, называемого "Administration Domain", в которой будет хранится конфигурация сервера администрирования "389-AS" (важно наглядно отделить эту ветвь DIT от дерева обслуживаемых LDAP-сервером "389-DS" пользовательских данных, потому я всегда задаю его в виде "local", что хорошо отражает его предназначение для нереплицируемых параметров конфигурации "локального инстанса").
....
Directory server network port [389]: 389
# Изменяем или подтверждаем номер TCP-порта, на котором будут приниматься подключения пользователей к сервису "389-DS" (проще оставить общепринятый "389").
....
Directory server identifier [ldap0]: ldap0-example-net
# (ВАЖНО) Задаём символический идентификатор LDAP-сервиса, обслуживающего некий набор данных в DIT (возможно не в одном), которым группа настроек и процессы будут отделены от других в рамках одного LDAP-сервера (я предпочитаю задавать идентификаторы отталкиваясь от FQDN, в примере "ldap0-example-net").
....
Suffix [dc=example, dc=net]: dc=example, dc=net
# (ВАЖНО) Задаём "суффикс" корня DIT - виртуального дерева LDAP-данных (можно ограничиться одним атрибутом, но я предпочитаю привязываться к FQDN предприятия, и, если таковой двухуровневый, то и суффикс - тоже: "dc=example,dc=net").
....
Directory Manager DN [cn=Directory Manager]: cn=Directory Manager
# Задаём пароль обязательно создаваемому суперпользователю сервиса (лучше оставить общепринятого "cn=Directory Manager").
....
Do you want to install the sample entries? [no]: no
# Опционально можно внести в свежесозданный LDAP набор примеров структуры данных (при тестировании интересно, но в действующем сервисе ни к чему).
....
Type the full path and filename, the word suggest, or the word none [suggest]:
# Опционально можно импортировать произвольные структуру и данные из имеющегося LDIF-файла.
....
Administration port [9830]: 9830
IP address [0.0.0.0]: 0.0.0.0
# Изменяем или подтверждаем номер TCP-порта и перечень IP-адресов, на которых будут приниматься подключения к сервису администрирования "389-AS" (проще оставить общепринятый "9830" и "0.0.0.0").
....
Run Administration Server as [dirsrv]: dirsrv
# Напоследок указываем имя системного пользователя, в контексте которых будет работать сервис администрирования "389-AS" (по умолчанию "dirsrv") и получаем через пару минут готовый к работе LDAP-сервис "389 Administration & Directory Server".
# Уровень детализации запросов конфигуратора (наш вариант "3 - Custom").
....
Computer name [ldap0]: ldap0.example.net
# FQDN несущего LDAP-сервис компьютера (в примере "ldap0.example.net").
....
System User [dirsrv]:
System Group [dirsrv]:
# Пользователь и группа, в контексте которых будет работать сервис (по умолчанию "dirsrv").
....
Do you want to register this software with an existing configuration directory server? [no]: no
# Адрес для подключения к возможно имеющемуся уже серверу администрирования (один сервер "389-AS" может управлять несколькими инстансами "389-DS" - до четырёх, но сейчас нам это не нужно, так как мы запускаем полностью автономную схему).
....
Configuration directory server administrator ID [admin]: admin
# Задаём логин и пароль для локального администратора развёртываемого сервиса "389-AS/DS" (проще именовать традиционно "admin"; это будет почти суперпользователь, наделяемый правами через ACL).
....
Administration Domain [example.net]: local
# (ВАЖНО) Задаём идентификатор структуры набора параметров, называемого "Administration Domain", в которой будет хранится конфигурация сервера администрирования "389-AS" (важно наглядно отделить эту ветвь DIT от дерева обслуживаемых LDAP-сервером "389-DS" пользовательских данных, потому я всегда задаю его в виде "local", что хорошо отражает его предназначение для нереплицируемых параметров конфигурации "локального инстанса").
....
Directory server network port [389]: 389
# Изменяем или подтверждаем номер TCP-порта, на котором будут приниматься подключения пользователей к сервису "389-DS" (проще оставить общепринятый "389").
....
Directory server identifier [ldap0]: ldap0-example-net
# (ВАЖНО) Задаём символический идентификатор LDAP-сервиса, обслуживающего некий набор данных в DIT (возможно не в одном), которым группа настроек и процессы будут отделены от других в рамках одного LDAP-сервера (я предпочитаю задавать идентификаторы отталкиваясь от FQDN, в примере "ldap0-example-net").
....
Suffix [dc=example, dc=net]: dc=example, dc=net
# (ВАЖНО) Задаём "суффикс" корня DIT - виртуального дерева LDAP-данных (можно ограничиться одним атрибутом, но я предпочитаю привязываться к FQDN предприятия, и, если таковой двухуровневый, то и суффикс - тоже: "dc=example,dc=net").
....
Directory Manager DN [cn=Directory Manager]: cn=Directory Manager
# Задаём пароль обязательно создаваемому суперпользователю сервиса (лучше оставить общепринятого "cn=Directory Manager").
....
Do you want to install the sample entries? [no]: no
# Опционально можно внести в свежесозданный LDAP набор примеров структуры данных (при тестировании интересно, но в действующем сервисе ни к чему).
....
Type the full path and filename, the word suggest, or the word none [suggest]:
# Опционально можно импортировать произвольные структуру и данные из имеющегося LDIF-файла.
....
Administration port [9830]: 9830
IP address [0.0.0.0]: 0.0.0.0
# Изменяем или подтверждаем номер TCP-порта и перечень IP-адресов, на которых будут приниматься подключения к сервису администрирования "389-AS" (проще оставить общепринятый "9830" и "0.0.0.0").
....
Run Administration Server as [dirsrv]: dirsrv
# Напоследок указываем имя системного пользователя, в контексте которых будет работать сервис администрирования "389-AS" (по умолчанию "dirsrv") и получаем через пару минут готовый к работе LDAP-сервис "389 Administration & Directory Server".
Сервисы "389-AS/DS" не запустятся автоматически, пока это явно не будет разрешено. Сервер администрирования "389-AS" (специально настроенный "Apache2" с набором модулей от "RedHat") запускается просто, а для старта LDAP-сервисов "389-DS" наверчена дополнительная systemd-прослойка, позволяющая через один "unit" управлять произвольным количеством LDAP-инстансов.
Активируем автозапуск и стартуем LDAP-сервис (где "dirsrv" имя systemd-сервиса, а после символа "@" - имя "инстанса" 389-DS):
# systemctl enable dirsrv@ldap0-example-net.service
# systemctl start dirsrv@ldap0-example-net.service
# systemctl start dirsrv@ldap0-example-net.service
Активируем автозапуск и стартуем "389 Administration Server", после LDAP-инстанса (сервис администрирования хранит свою конфигурацию в одном из LDAP-инстансов, и без такового его запуск бессмыслен):
# systemctl enable dirsrv-admin.service
# systemctl start dirsrv-admin.service
# journalctl -xe
# systemctl start dirsrv-admin.service
# journalctl -xe
Проверим статус systemd-сервисов:
# systemctl | grep -i "dirsrv.*service"
dirsrv-admin.service loaded active running 389 Administration Server.
dirsrv@ldap0-example-net.service loaded active running 389 Directory Server ldap0-example-net.
dirsrv@ldap0-example-net.service loaded active running 389 Directory Server ldap0-example-net.
Проверим, прослушивают ли сервисы "389-AS/DS" выделенные им сетевые порты:
# netstat -apn | grep -i "^tcp" | grep -i listen | grep -iE "apache|slapd"
tcp ... 0.0.0.0:9830 0.0.0.0:* LISTEN .../apache2
tcp ... 0.0.0.0:389 0.0.0.0:* LISTEN .../ns-slapd
tcp ... 0.0.0.0:389 0.0.0.0:* LISTEN .../ns-slapd
Самый первый и простой способ проверки работоспособности и отчасти выявления неисправностей - воспользоваться web-интерфейсом сервера администрирования, доступным через обычный "браузер" - в нашем случае по адресу "http://ldap0.example.net:9830".
Первый запрос к структуре данных свежеустановленного сервера "389-DS" наверное покажет, что таковым уже поддерживается два "Directory Information Tree (DIT)" - создаваемое по умолчанию для служебных целей "netscaperoot" и дерево пользовательских данных, с параметрами, определёнными во время конфигурирования скриптом "setup-ds-admin":
$ ldapsearch -x -h 127.0.0.1 -p 389 -D "cn=Directory Manager" -W -s base -b "" + | grep -i "context"
namingContexts: dc=example,dc=net
namingContexts: o=netscaperoot
namingContexts: o=netscaperoot
Для упрощения последующей автоматизации фоновых задач есть смысл сохранить пароль суперпользователя LDAP-сервера "cn=Directory Manager" в файле, доступном только суперпользователю несущей операционной системы "root" - в дальнейшем скрипты, аутентифицирующиеся от имени этого пользователя смогут здесь взять необходимый пароль:
# vi /root/.389-ds
Обязательно защищаем файл с паролем от посторонних:
# chown root:root /root/.389-ds
# chmod go-rwx /root/.389-ds
# chmod go-rwx /root/.389-ds
Создание простой структуры записей данных.
Для разминки пройдём по этапам создания типовой структуры хранения записей данных о пользователях. В зависимости от способа установки и предварительной настройки часть из нижеследующего уже будет создана автоматически (ради тестирования можно просто удалить всё дерево DIT под суфиксом "dc=example,dc=net" и воспроизвести его заново).
Создаём корневую структуру данных (прикрепляя её к уже имеющемуся "пространству имён", иначе говоря набору файловых ресурсов, предназначенному для хранения данных; тип корневой записи "domain" вместо обычной в таких случаях "organization" выбирается для красоты и поддержки мультидоменности в перспективе):
$ vi ./add-dit.ldif
dn: dc=example,dc=net
objectClass: top
objectClass: domain
dc: example
organizationName: Example, Inc.
objectClass: top
objectClass: domain
dc: example
organizationName: Example, Inc.
$ ldapadd -x -h 127.0.0.1 -D "cn=Directory Manager" -W -f ./add-dit.ldif
Учитывая то, что DIT создаётся вручную, права доступа к нему тоже нужно будет задавать самостоятельно. Для начала добавим ACL, дающий фигурантам встроенной административной группе (в частности пользователю "uid=admin") полный доступ на правку записей нового DIT:
$ ldapmodify -h 127.0.0.1 -D "cn=Directory Manager" -W
dn: dc=example,dc=net
changetype: modify
add: aci
aci: (targetattr = "*") (version 3.0; acl "Configuration Administrators Group";
allow (all) (groupdn="ldap:///cn=Configuration Administrators,ou=Groups,ou=TopologyManagement,o=NetscapeRoot");)
-
^d
changetype: modify
add: aci
aci: (targetattr = "*") (version 3.0; acl "Configuration Administrators Group";
allow (all) (groupdn="ldap:///cn=Configuration Administrators,ou=Groups,ou=TopologyManagement,o=NetscapeRoot");)
-
^d
Идеологически пользователи-объекты "ou=Administrators,ou=TopologyManagement,o=NetscapeRoot" и "cn=Configuration Administrators,ou=Groups,ou=TopologyManagement,o=NetscapeRoot" приравниваются по уровню доступа к суперпользователю "cn=Directory Manager", но права им всё-таки выдаются посредством ACL явно и могут быть ограничены при необходимости.
Создадим первые три ветви DIT, для групп пользователей, пользователей и сервисных учётных записей:
$ vi ./add-ou-level-one.ldif
dn: ou=Groups,dc=example,dc=net
objectClass: top
objectClass: organizationalUnit
ou: Groups
description: All people group in organisation
dn: ou=People,dc=example,dc=net
objectClass: top
objectClass: organizationalUnit
ou: People
description: All people in organisation
dn: ou=Services,dc=example,dc=net
objectClass: top
objectClass: organizationalUnit
ou: Services
description: All services in organisation
dn: ou=Groups,ou=Services,dc=example,dc=net
objectClass: top
objectClass: organizationalUnit
ou: Groups
description: Groups service accounts
dn: ou=Accounts,ou=Services,dc=example,dc=net
objectClass: top
objectClass: organizationalUnit
ou: Accounts
description: All service accounts
objectClass: top
objectClass: organizationalUnit
ou: Groups
description: All people group in organisation
dn: ou=People,dc=example,dc=net
objectClass: top
objectClass: organizationalUnit
ou: People
description: All people in organisation
dn: ou=Services,dc=example,dc=net
objectClass: top
objectClass: organizationalUnit
ou: Services
description: All services in organisation
dn: ou=Groups,ou=Services,dc=example,dc=net
objectClass: top
objectClass: organizationalUnit
ou: Groups
description: Groups service accounts
dn: ou=Accounts,ou=Services,dc=example,dc=net
objectClass: top
objectClass: organizationalUnit
ou: Accounts
description: All service accounts
$ ldapadd -x -h 127.0.0.1 -D "cn=Directory Manager" -W -f ./add-ou-level-one.ldif
Создадим демонстрационную запись для группы пользователей:
$ ldapmodify -h 127.0.0.1 -D "cn=Directory Manager" -W
dn: cn=Group0,ou=Groups,dc=example,dc=net
changetype: add
objectClass: top
objectClass: groupOfUniqueNames
ou: Group0
description: Example group
^d
changetype: add
objectClass: top
objectClass: groupOfUniqueNames
ou: Group0
description: Example group
^d
Создадим запись описания произвольного пользователя с минимальным набором атрибутов:
$ ldapmodify -h 127.0.0.1 -D "cn=Directory Manager" -W
dn: uid=userOne,ou=People,dc=example,dc=net
changetype: add
objectClass: top
objectClass: inetOrgPerson
objectClass: inetUser
uid: userOne
cn: Ivan Ivanov
givenName: Ivan
sn: Ivanov
userPassword: ***
^d
changetype: add
objectClass: top
objectClass: inetOrgPerson
objectClass: inetUser
uid: userOne
cn: Ivan Ivanov
givenName: Ivan
sn: Ivanov
userPassword: ***
^d
Теперь включим пользователя "uid=userOne" в группу "cn=Group0":
$ ldapmodify -h 127.0.0.1 -D "cn=Directory Manager" -W
dn: cn=Group0,ou=Groups,dc=example,dc=net
changetype: modify
add: uniqueMember
uniqueMember: uid=userOne,ou=People,dc=example,dc=net
^d
changetype: modify
add: uniqueMember
uniqueMember: uid=userOne,ou=People,dc=example,dc=net
^d
Проверим, доступны ли атрибуты пользователя ему самому:
$ ldapsearch -x -LLL -h 127.0.0.1 -D "uid=userOne,ou=People,dc=example,dc=net" -W -b dc=example,dc=net "(uid=userOne)"
Запрещение анонимного доступа к данным LDAP.
По умолчанию в "389-DS" возможно анонимное чтение - это разрешено как на уровне функциональности LDAP-сервера, так и соответствующим ACL "Default anonymous access", автоматически создаваемыми для всех пользовательских DIT.
Например, любой может подключиться к сервису и узнать о его текущем статусе:
$ ldapsearch -x -h ldap0.example.net -b "" -s base
dn:
objectClass: top
defaultnamingcontext: dc=example,dc=net
....
objectClass: top
defaultnamingcontext: dc=example,dc=net
....
Также кто угодно может запросить сведения о любой записи или атрибуте (не в DIT конфигурации, конечно):
$ ldapsearch -x -h ldap0.example.net -b dc=example,dc=net "(uid=userOne)"
dn: uid=userOne,ou=people,dc=example,dc=net
....
uid: userOne
userPassword: {SSHA}e1N...==
....
....
uid: userOne
userPassword: {SSHA}e1N...==
....
Режим анонимного доступа задаётся соответствующим параметром (default: on):
$ ldapsearch -LLL -h 127.0.0.1 -D "cn=Directory Manager" -W -b cn=config -s base "(cn=config)" nsslapd-allow-anonymous-access
dn: cn=config
nsslapd-allow-anonymous-access: on
nsslapd-allow-anonymous-access: on
Вопрос о полном запрете анонимного доступа к LDAP дискуссионный и единого мнения или отраслевого стандарта нет. Если абсолютно все варианты использования LDAP подразумевают предварительную аутентификацию, лишь после которой пользователю предоставляется доступ к очерченному ACL-ами набору ресурсов - то анонимный доступ можно запретить. Если LDAP-сервис эксплуатируется в небольшой компании, недоступен извне сети предприятия, то проще предоставить возможность подключаться анонимно для поиска разрешённого перечня атрибутов, что существенно снизит сложность настройки интеграции для клиентов услуги "каталога".
Запрет анонимного (без предъявления логина и пароля) доступа к LDAP осуществляется включением одного из двух режимов: полного запрета (value: off) и запрета для всего, кроме чтения корневого узла, что удобно для проверки доступности сервиса (value: rootdse) - второй вариант мне представляется более удобным:
$ ldapmodify -h 127.0.0.1 -D "cn=Directory Manager" -W
dn: cn=config
changetype: modify
replace: nsslapd-allow-anonymous-access
nsslapd-allow-anonymous-access: rootdse
^d
changetype: modify
replace: nsslapd-allow-anonymous-access
nsslapd-allow-anonymous-access: rootdse
^d
Применение изменений требует перезапуска сервиса:
# stop-dirsrv ldap0-example-net && start-dirsrv ldap0-example-net
Как я упоминал выше, кроме функционального разрешения свободного доступа к данным в "389 DS" при каждом DIT по умолчанию автоматически создаются разрешающие анонимный доступ ACL - их просто найти в списке имеющих отношение к интересующему нас DIT:
$ ldapsearch -LLL -h 127.0.0.1 -D "cn=Directory Manager" -W -b "o=netscaperoot" "(aci=*)" aci
....
dn: o=NetscapeRoot
aci: (targetattr = "*") (targetfilter = (o=NetscapeRoot)) (version 3.0; acl "Default anonymous access"; allow (read, search) (userdn = "ldap:///anyone");)
....
dn: ou=TopologyManagement,o=NetscapeRoot
aci: (targetattr != "userPassword") (version 3.0; acl "Enable anonymous access"; allow (read, search, compare) (userdn = "ldap:///anyone");)
....
dn: ou=Global Preferences,ou=local,o=NetscapeRoot
aci: (targetattr = "*") (version 3.0; acl "Enable anonymous access"; allow (read,search) (userdn = "ldap:///anyone");)
....
dn: o=NetscapeRoot
aci: (targetattr = "*") (targetfilter = (o=NetscapeRoot)) (version 3.0; acl "Default anonymous access"; allow (read, search) (userdn = "ldap:///anyone");)
....
dn: ou=TopologyManagement,o=NetscapeRoot
aci: (targetattr != "userPassword") (version 3.0; acl "Enable anonymous access"; allow (read, search, compare) (userdn = "ldap:///anyone");)
....
dn: ou=Global Preferences,ou=local,o=NetscapeRoot
aci: (targetattr = "*") (version 3.0; acl "Enable anonymous access"; allow (read,search) (userdn = "ldap:///anyone");)
....
$ ldapsearch -LLL -h 127.0.0.1 -D "cn=Directory Manager" -W -b "dc=example,dc=net" "(aci=*)" aci
....
aci: (targetattr = "*") (version 3.0; acl "Enable anonymous access"; allow (read,compare,search) (userdn = "ldap:///anyone");)
....
aci: (targetattr = "*") (version 3.0; acl "Enable anonymous access"; allow (read,compare,search) (userdn = "ldap:///anyone");)
....
Удаляем ACL-ы, разрешающий анонимный доступ (обратите внимание - содержимое атрибута "aci" должно быть указано буквально так, как оно зафиксировано в LDAP - утилита "ldapmodify" оперирует с текстом, а не логическими конструкциями):
$ ldapmodify -h 127.0.0.1 -D "cn=Directory Manager" -W
dn: o=NetscapeRoot
changetype: modify
delete: aci
aci: (targetattr = "*") (targetfilter = (o=NetscapeRoot)) (version 3.0; acl "Default anonymous access"; allow (read, search) (userdn = "ldap:///anyone");)
dn: ou=TopologyManagement,o=NetscapeRoot
changetype: modify
delete: aci
aci: (targetattr != "userPassword") (version 3.0; acl "Enable anonymous access"; allow (read, search, compare) (userdn = "ldap:///anyone");)
dn: ou=Global Preferences,ou=local,o=NetscapeRoot
changetype: modify
delete: aci
aci: (targetattr = "*") (version 3.0; acl "Enable anonymous access"; allow (read,search) (userdn = "ldap:///anyone");)
dn: dc=example,dc=net
changetype: modify
delete: aci
aci: (targetattr = "*") (version 3.0; acl "Enable anonymous access"; allow (read,compare,search) (userdn = "ldap:///anyone");)
^d
changetype: modify
delete: aci
aci: (targetattr = "*") (targetfilter = (o=NetscapeRoot)) (version 3.0; acl "Default anonymous access"; allow (read, search) (userdn = "ldap:///anyone");)
dn: ou=TopologyManagement,o=NetscapeRoot
changetype: modify
delete: aci
aci: (targetattr != "userPassword") (version 3.0; acl "Enable anonymous access"; allow (read, search, compare) (userdn = "ldap:///anyone");)
dn: ou=Global Preferences,ou=local,o=NetscapeRoot
changetype: modify
delete: aci
aci: (targetattr = "*") (version 3.0; acl "Enable anonymous access"; allow (read,search) (userdn = "ldap:///anyone");)
dn: dc=example,dc=net
changetype: modify
delete: aci
aci: (targetattr = "*") (version 3.0; acl "Enable anonymous access"; allow (read,compare,search) (userdn = "ldap:///anyone");)
^d
Разрешение анонимного поиска по выделенным атрибутам.
Отдельно рассмотрим варианты предварительного поиска указателя (DN) на пользователя среди всех записей в LDAP. Если каталог закрыт напрочь от доступа без аутентификации (параметром "nsslapd-allow-anonymous-access: {off|rootdse}"), то для предварительного поиска потребуется служебная учётная запись, через которую вначале находятся сведения о пользователе, считываются его атрибуты и только следующим действием запрашивается аутентификация в LDAP уже от имени целевого пользователя с указанием его полного DN.
В случае, если жёстких требований на сокрытие сведений о перечне имеющихся пользователей на предприятии нет, то проще предоставить всем возможность выяснять ссылку на учётную запись (параметр "nsslapd-allow-anonymous-access: on") - процедура вроде поиска номера телефона в справочнике штатного расписания:
$ ldapmodify -h 127.0.0.1 -D "cn=Directory Manager" -W
dn: ou=People,dc=example,dc=net
changetype: modify
add: aci
aci: (targetattr = "uid") (version 3.0; acl "Anonymous search and read UIDs only";
allow (search,read) (userdn = "ldap:///anyone");)
^d
changetype: modify
add: aci
aci: (targetattr = "uid") (version 3.0; acl "Anonymous search and read UIDs only";
allow (search,read) (userdn = "ldap:///anyone");)
^d
Пробуем найти запись о пользователе через анонимное подключение:
$ ldapsearch -x -v ldap0.example.net -b 'ou=people,dc=example,dc=net' "(uid=userOne)" dn
Пробуем прочитать свою запись с предварительной аутентификацией:
$ ldapsearch -x -v ldap0.example.net -D 'uid=userOne,ou=People,dc=example,dc=net' -W -b 'uid=userOne,ou=people,dc=example,dc=net'
Разрешение пользователям читать и корректировать свои профили.
Выше мы приняли меры по запрету чтения данных посторонними, возможно даже закрыв доступ к таковым всем, кроме администраторов. Теперь явно обеспечим возможность самим пользователям читать атрибуты их собственной записи:
$ ldapmodify -h 127.0.0.1 -D "cn=Directory Manager" -W
dn: ou=People,dc=example,dc=net
changetype: modify
add: aci
aci: (targetattr = "*") (version 3.0; acl "Allow self entry search";
allow (read,search) (userdn = "ldap:///self");)
^d
changetype: modify
add: aci
aci: (targetattr = "*") (version 3.0; acl "Allow self entry search";
allow (read,search) (userdn = "ldap:///self");)
^d
Предположим, LDAP-сервис обслуживает команду, в которой каждый пользователь волен указывать о себе всё, что угодно - в таком случае просто разрешаем ему правку своей записи без ограничений (разумеется, для этого он должен предварительно аутентифицироваться):
$ ldapmodify -h 127.0.0.1 -D "cn=Directory Manager" -W
dn: ou=People,dc=example,dc=net
changetype: modify
add: aci
aci: (targetattr = "*") (version 3.0; acl "Allow self entry modification";
allow (read,add,write,delete) (userdn = "ldap:///self");)
^d
changetype: modify
add: aci
aci: (targetattr = "*") (version 3.0; acl "Allow self entry modification";
allow (read,add,write,delete) (userdn = "ldap:///self");)
^d
В случае, если наполнение записи атрибутами пользователь не контролирует, разрешаем ему лишь частичную правку своих атрибутов (как минимум позволяющую сменить пароль, например):
$ ldapmodify -h 127.0.0.1 -D "cn=Directory Manager" -W
dn: ou=People,dc=example,dc=net
changetype: modify
add: aci
aci: (targetattr = "description || seeAlso || telephoneNumber || userPassword") (version 3.0; acl "Allow users self entry modification some attributes";
allow (write) (userdn= "ldap:///self");)
^d
changetype: modify
add: aci
aci: (targetattr = "description || seeAlso || telephoneNumber || userPassword") (version 3.0; acl "Allow users self entry modification some attributes";
allow (write) (userdn= "ldap:///self");)
^d
Создание учётных записей администраторов DIT.
Прежде всего разберёмся, какие бывают административные учётные записи в LDAP "389-AS/DS":
1. Встроенный суперпользователь "cn=Directory Manager" - автоматически создаётся при инсталляции сервиса, практически не может быть удалён - абсолютный властитель, не подверженный никаким ограничениям.
2. Член встроенной группы "ou=Administrators,ou=TopologyManagement,o=NetscapeRoot" - пользователь "uid=admin" автоматически создаётся при инсталляции, может быть удалён или модифицирован - наделяется максимальными полномочиями посредством ACL "Configuration Administrators Group", но может быть их лишён изменением или отключением ACL для целевых объектов.
3. Произвольный пользователь - создаётся, модифицируется и удаляется по необходимости в целевом DIT - наделяется полномочиями посредством произвольных ACL, которыми регулируется доступ к объектам.
2. Член встроенной группы "ou=Administrators,ou=TopologyManagement,o=NetscapeRoot" - пользователь "uid=admin" автоматически создаётся при инсталляции, может быть удалён или модифицирован - наделяется максимальными полномочиями посредством ACL "Configuration Administrators Group", но может быть их лишён изменением или отключением ACL для целевых объектов.
3. Произвольный пользователь - создаётся, модифицируется и удаляется по необходимости в целевом DIT - наделяется полномочиями посредством произвольных ACL, которыми регулируется доступ к объектам.
Очевидно, что суперпользователь первого типа должен использоваться только на этапе создания структуры LDAP-сервиса или запуска важных фоновых процедур.
Суперпользователи второго типа могут быть использованы как для выполнения более широкого круга задач - удобство в том, что их учётные записи могут быть безболезненно изменены или заблокированы при необходимости. Важно только помнить, что эти записи хранятся в локальном контексте и не будут реплицированы на другие "инстансы" LDAP-сервиса.
Пользователи третьего типа, размещаемые в контексте управляемого и реплицируемого DIT наиболее практичны - далее о них.
Создадим пользователя "cn=Directory Administrator", предназначенного для неограниченного администрирования структуры целевого DIT:
$ ldapmodify -h 127.0.0.1 -D "cn=Directory Manager" -W
dn: cn=Directory Administrator,dc=example,dc=net
changetype: add
objectClass: top
objectClass: account
objectClass: person
uid: admin
cn: Directory Administrator
userPassword: ***
sn: LDAP Account
description: LDAP Directory Administrator Account
organizationName: Example Organization
^d
changetype: add
objectClass: top
objectClass: account
objectClass: person
uid: admin
cn: Directory Administrator
userPassword: ***
sn: LDAP Account
description: LDAP Directory Administrator Account
organizationName: Example Organization
^d
Создадим группу "cn=Directory Administrators" (эта группа создаётся по умолчанию, если воспользоваться установкой по шаблону), пользователи которой получат возможность редактировать любые записи целевого DIT:
$ ldapmodify -h 127.0.0.1 -D "cn=Directory Manager" -W
dn: cn=Directory Administrators,dc=example,dc=net
changetype: add
objectClass: top
objectClass: groupOfUniqueNames
ou: Directory Administrators
description: Group for Directory Administrators
uniqueMember: cn=Directory Manager
uniqueMember: cn=Directory Administrator,dc=example,dc=net
^d
changetype: add
objectClass: top
objectClass: groupOfUniqueNames
ou: Directory Administrators
description: Group for Directory Administrators
uniqueMember: cn=Directory Manager
uniqueMember: cn=Directory Administrator,dc=example,dc=net
^d
Добавим ACL, дающий указанной группе "cn=Directory Administrators" полный доступ на редактирование записей целевого DIT:
$ ldapmodify -h 127.0.0.1 -D "cn=Directory Manager" -W
dn: dc=example,dc=net
changetype: modify
add: aci
aci: (targetattr = "*") (version 3.0; acl "Allow all for LDAP Directory Administrators";
allow (all) (groupdn = "ldap:///cn=Directory Administrators,dc=example,dc=net");)
^d
changetype: modify
add: aci
aci: (targetattr = "*") (version 3.0; acl "Allow all for LDAP Directory Administrators";
allow (all) (groupdn = "ldap:///cn=Directory Administrators,dc=example,dc=net");)
^d
Теперь для редактирования структуры DIT "dc=example,dc=net" мы располагаем как минимум полноправным пользователем "cn=Directory Administrator,dc=example,dc=net", а в дополнение к нему в группу могут быть добавлены любые другие произвольные пользователи.
Обращаю внимание на то, что часто встречается подход к созданию учётных записей подобного уровня доступа как объект в "ou=Administrators,ou=TopologyManagement,o=NetscapeRoot". Это неправильно. Во первых, таким образом вы даёте административному пользователю максимальные привилегии, урезать которые будет в дальнейшем сложнее - он ведь в контексте конфигурации всего LDAP-сервиса, а не только конкретного DIT. Во вторых, вы лишаетесь возможности полноценной репликации этих учётных записей - так как они за рамками DIT предприятия.
Создание учётных записей операторов DIT.
Наподобие того, как мы создали выше группу администраторов DIT, сделаем чуть менее властную группу "cn=Directory Operators", пользователи которой получат возможность править записи некоторой части целевого DIT:
$ ldapmodify -h 127.0.0.1 -D "cn=Directory Manager" -W
dn: cn=Directory Operators,dc=example,dc=net
changetype: add
objectClass: top
objectClass: groupOfUniqueNames
ou: Directory Operators
description: Group for Directory Operators
^d
changetype: add
objectClass: top
objectClass: groupOfUniqueNames
ou: Directory Operators
description: Group for Directory Operators
^d
Добавим ACL, дающий указанной группе "cn=Directory Operators" доступ на правку записей лишь указанных ветвей DIT:
$ ldapmodify -h 127.0.0.1 -D "cn=Directory Manager" -W
dn: ou=Groups,dc=example,dc=net
changetype: modify
add: aci
aci: (targetattr = "*") (version 3.0; acl "Allow RW for Example Directory Operators";
allow (all) (groupdn = "ldap:///cn=Directory Operators,dc=example,dc=net");)
dn: ou=People,dc=example,dc=net
changetype: modify
add: aci
aci: (targetattr = "*") (version 3.0; acl "Allow RW for Example Directory Operators";
allow (all) (groupdn = "ldap:///cn=Directory Operators,dc=example,dc=net");)
^d
changetype: modify
add: aci
aci: (targetattr = "*") (version 3.0; acl "Allow RW for Example Directory Operators";
allow (all) (groupdn = "ldap:///cn=Directory Operators,dc=example,dc=net");)
dn: ou=People,dc=example,dc=net
changetype: modify
add: aci
aci: (targetattr = "*") (version 3.0; acl "Allow RW for Example Directory Operators";
allow (all) (groupdn = "ldap:///cn=Directory Operators,dc=example,dc=net");)
^d
Развивая идею выделения полномочий создадим группу "cn=Directory Viewers", пользователи которой получат возможность лишь просматривать все атрибуты записей некоторой части целевого DIT:
$ ldapmodify -h 127.0.0.1 -D "cn=Directory Manager" -W
dn: cn=Directory Viewers,dc=example,dc=net
changetype: add
objectClass: top
objectClass: groupOfUniqueNames
ou: Directory Viewers
description: Group for Directory Viewers
^d
changetype: add
objectClass: top
objectClass: groupOfUniqueNames
ou: Directory Viewers
description: Group for Directory Viewers
^d
Добавим ACL, дающий указанной группе "cn=Directory Viewers" доступ на чтение всех атрибутов записей указанных ветвей целевого DIT:
$ ldapmodify -h 127.0.0.1 -D "cn=Directory Manager" -W
dn: ou=Groups,dc=example,dc=net
changetype: modify
add: aci
aci: (targetattr = "*") (version 3.0; acl "Allow ROnly for Example Directory Viewers";
allow (read,compare,search) (groupdn = "ldap:///cn=Directory Viewers,dc=example,dc=net");)
dn: ou=People,dc=example,dc=net
changetype: modify
add: aci
aci: (targetattr = "*") (version 3.0; acl "Allow ROnly for Example Directory Viewers";
allow (read,compare,search) (groupdn = "ldap:///cn=Directory Viewers,dc=example,dc=net");)
^d
changetype: modify
add: aci
aci: (targetattr = "*") (version 3.0; acl "Allow ROnly for Example Directory Viewers";
allow (read,compare,search) (groupdn = "ldap:///cn=Directory Viewers,dc=example,dc=net");)
dn: ou=People,dc=example,dc=net
changetype: modify
add: aci
aci: (targetattr = "*") (version 3.0; acl "Allow ROnly for Example Directory Viewers";
allow (read,compare,search) (groupdn = "ldap:///cn=Directory Viewers,dc=example,dc=net");)
^d
Создание сервисных учётных записей.
Кроме записей с данными пользователей в реальной эксплуатации удобно использовать выделенные в отдельную иерархию учётные записи для внешних сервисов, которым требуется подключение к LDAP-серверу.
Создадим сервисные группы для выделения их членам определённых привилегий:
$ ldapmodify -h 127.0.0.1 -D "cn=Directory Manager" -W
dn: cn=ROPeople,ou=Groups,ou=Services,dc=example,dc=net
changetype: add
objectClass: top
objectClass: groupOfUniqueNames
cn: ROPeople
description: Simple read-only services accounts with access only to People
dn: cn=ROGroups,ou=Groups,ou=Services,dc=example,dc=net
changetype: add
objectClass: top
objectClass: groupOfUniqueNames
cn: ROGroups
description: Simple read-only services accounts with access only to Groups
^d
changetype: add
objectClass: top
objectClass: groupOfUniqueNames
cn: ROPeople
description: Simple read-only services accounts with access only to People
dn: cn=ROGroups,ou=Groups,ou=Services,dc=example,dc=net
changetype: add
objectClass: top
objectClass: groupOfUniqueNames
cn: ROGroups
description: Simple read-only services accounts with access only to Groups
^d
Добавим ACL-ы, дающие группе учётных записей доступ на чтение записей определённой ветви DIT (в частности, одной только записей пользователей, но не групп или других сервисов, а другой - только записей групп):
$ ldapmodify -h 127.0.0.1 -D "cn=Directory Manager" -W
dn: ou=People,dc=example,dc=net
changetype: modify
add: aci
aci: (targetattr = "*") (version 3.0; acl "Allow group ROPeople read-only access only to People";
allow (read,compare,search) (groupdn = "ldap:///cn=ROPeople,ou=Groups,ou=Services,dc=example,dc=net");)
dn: ou=Groups,dc=example,dc=net
changetype: modify
add: aci
aci: (targetattr = "*") (version 3.0; acl "Allow group ROGroups read-only access only to Groups";
allow (read,compare,search) (groupdn = "ldap:///cn=ROGroups,ou=Groups,ou=Services,dc=example,dc=net");)
^d
changetype: modify
add: aci
aci: (targetattr = "*") (version 3.0; acl "Allow group ROPeople read-only access only to People";
allow (read,compare,search) (groupdn = "ldap:///cn=ROPeople,ou=Groups,ou=Services,dc=example,dc=net");)
dn: ou=Groups,dc=example,dc=net
changetype: modify
add: aci
aci: (targetattr = "*") (version 3.0; acl "Allow group ROGroups read-only access only to Groups";
allow (read,compare,search) (groupdn = "ldap:///cn=ROGroups,ou=Groups,ou=Services,dc=example,dc=net");)
^d
Разумеется, по аналогии с группами и ACL "только для чтения" можно создать и другие, с правами на редактирование записей - очертив им любой ареал доступа.
Создадим сервисную учётную запись с минимальным набором атрибутов:
$ ldapmodify -h 127.0.0.1 -D "cn=Directory Manager" -W
dn: uid=app0,ou=Accounts,ou=Services,dc=example,dc=net
changetype: add
objectClass: top
objectClass: account
objectClass: inetUser
uid: app0
cn: Example App
userPassword: ***
description: Service account for binding service of Example Apps to LDAP
organizationName: Example Organization
organizationalUnitName: AD
host: app0.example.net
host: 12.34.56.78
nsSizeLimit: -1
nsLookThroughLimit: -1
^d
changetype: add
objectClass: top
objectClass: account
objectClass: inetUser
uid: app0
cn: Example App
userPassword: ***
description: Service account for binding service of Example Apps to LDAP
organizationName: Example Organization
organizationalUnitName: AD
host: app0.example.net
host: 12.34.56.78
nsSizeLimit: -1
nsLookThroughLimit: -1
^d
Параметрами атрибутов "nsSizeLimit" и "nsLookThroughLimit" мы снимаем для сервисной учётной записи ограничения на размер и количество строк запрашиваемых данных. Это служебный (так называемый "операционный") атрибут, не принадлежащий никакому классу схемы данных, так что в GUI-браузерах по умолчанию может не отображаться в перечне атрибутов записи.
Далее включим пользователя "uid=app0" в группу с доступом на чтение только записей пользователей:
$ ldapmodify -h 127.0.0.1 -D "cn=Directory Manager" -W
dn: cn=ROPeople,ou=Groups,ou=Services,dc=example,dc=net
changetype: modify
add: uniqueMember
uniqueMember: uid=app0,ou=Accounts,ou=Services,dc=example,dc=net
^d
changetype: modify
add: uniqueMember
uniqueMember: uid=app0,ou=Accounts,ou=Services,dc=example,dc=net
^d
Теперь, если запросить через созданную только что сервисную учётную запись все доступные ей данные, то мы получим сведения о ветви "ou=People", записи с атрибутами всех её фигурантов и сведения о самой сервисной учётной записи "uid=app0":
$ ldapsearch -x -LLL -h 127.0.0.1 -D "uid=app0,ou=Accounts,ou=Services,dc=example,dc=net" -W -b dc=example,dc=net
Таким образом, учётную запись "uid=app0" можно использовать для "биндинга" при аутентификации пользователей какого-нибудь стороннего сервиса, вроде "Freeradius" или "Atlassian Crowd".
Оптимизация процедур выявления групповой принадлежности.
Перед началом эксплуатации LDAP в условиях активного группирования пользователей очень желательно включить функционал синхронизации атрибутов присутствия в группе в записи группы с атрибутами принадлежности группе в записи пользователя.
Обычно для включения записи пользователя в группу к записи последней добавляется атрибут "member" или "uniqueMember". Для большей наглядности, чтобы отобразить принадлежность к записям групп в запись пользователя также могут быть добавлены атрибуты "memberOf". Таким образом при просмотре записи группы по атрибутам "member" или "uniqueMember" просто узнать всех её пользователей, а при просмотре записи пользователя по атрибутам "memberOf" просто узнать все группы, в которые она включена. Очевидно, что можно потерять связность этих атрибутов при правке записей - и для синхронизации таковых предназначен поставляемый в дистрибутивном комплекте "389-DS" плагин "MemberOf Plugin", который требует явного включения и настройки.
Активируем плагин "MemberOf Plugin":
$ ldapmodify -h 127.0.0.1 -D "cn=Directory Manager" -W
dn: cn=MemberOf Plugin,cn=plugins,cn=config
changetype: modify
replace: nsslapd-pluginEnabled
nsslapd-pluginEnabled: on
^d
changetype: modify
replace: nsslapd-pluginEnabled
nsslapd-pluginEnabled: on
^d
Задаём имя атрибута в записях групп, для идентификаторов включённых в группы пользователей:
$ ldapmodify -h 127.0.0.1 -D "cn=Directory Manager" -W
dn: cn=MemberOf Plugin,cn=plugins,cn=config
changetype: modify
replace: memberofgroupattr
memberofgroupattr: uniqueMember
^d
changetype: modify
replace: memberofgroupattr
memberofgroupattr: uniqueMember
^d
Задаём имя атрибута в записях пользователей, для указания на группы, в которые они включены:
$ ldapmodify -h 127.0.0.1 -D "cn=Directory Manager" -W
dn: cn=MemberOf Plugin,cn=plugins,cn=config
changetype: modify
replace: memberofattr
memberofattr: memberOf
^d
changetype: modify
replace: memberofattr
memberofattr: memberOf
^d
Для активации плагина перезапускаем LDAP-инстанс:
# stop-dirsrv ldap0-example-net && start-dirsrv ldap0-example-net
Важно помнить, что действие плагина "MemberOf Plugin" направлено только в одну сторону - от групп к пользователям - то есть, при создании в записи группы атрибута "uniqueMember" в записи соответствующего пользователя автоматически создаётся атрибут "memberOf". В обратную сторону это не работает. То есть, если вручную создать в записи пользователя атрибут "memberOf", то в записи группы не появится соответствующий пользователю атрибут "uniqueMember" - таким образом в структуре данных образовывается фальшивая связь, показывающая присутствие пользователя в группе, в которую он на самом деле не включён.
Частично проблема образования фальшивой связки может быть снята глобальным ACL, запрещающим пользователям вручную добавлять или изменять атрибуты "memberOf" (помним при этом, что суперпользователь вроде "cn=Directory Manager" совершенно не подвержен действию ACL):
$ ldapmodify -h 127.0.0.1 -D "cn=Directory Manager" -W
dn: dc=example,dc=net
changetype: modify
add: aci
aci: (targetattr = "memberOf") (version 3.0; acl "Deny users change their group memberships";
deny (add,selfwrite,write,delete) (userdn = "ldap:///anyone");)
^d
changetype: modify
add: aci
aci: (targetattr = "memberOf") (version 3.0; acl "Deny users change their group memberships";
deny (add,selfwrite,write,delete) (userdn = "ldap:///anyone");)
^d
Для решения проблемы уже имеющихся записей с некорректными атрибутами "memberOf" в дистрибутиве "389-DS" приложена специальная perl-утилита, создающая (на основе предустановленного шаблона "cn=memberof task") задачу удаления фактически несвязанных с группами атрибутов "memberOf":
$ fixup-memberof -Z ldap0-example-net -D "cn=Directory Manager" -w - -b "dc=example,dc=net"
Successfully added task entry "cn=memberOf_fixup_YYYY_MM_DD_HH_MM_SS, cn=memberOf task, cn=tasks, cn=config"
Запущенная выше задача отработает и будет автоматически удалена после завершения.
Есть смысл поставить запуск этой задачи по расписанию, раз в сутки, часа в три ночи, например (в скриптах для утилиты "fixup-memberof" вместо ввода пароля в соответствии с ключем "-w -" можно забирать таковой из произвольного файла "-j filename"):
# vi /etc/crontab
....
# Fix LDAP "389-DS" records with the wrong attribute "memberOf"
0 3 * * * root /usr/sbin/fixup-memberof -Z ldap0-example-net -D "cn=Directory Manager" -j /root/.389-ds -b "dc=example,dc=net" &
# Fix LDAP "389-DS" records with the wrong attribute "memberOf"
0 3 * * * root /usr/sbin/fixup-memberof -Z ldap0-example-net -D "cn=Directory Manager" -j /root/.389-ds -b "dc=example,dc=net" &
Оптимизация процедуры выделения "Posix UID & GID".
Если учётные записи в LDAP предназначены для аутентификации в операционных системах "Linux", рано или поздно возникает необходимость обеспечения неизменности числовых UID и GID пользователей при входе на любой компьютер предприятия - это достигается централизованным хранением идентификаторов в LDAP (в объектном классе "posixAccount") и заданием таковых на компьютерах при аутентификации пользователя.
Ничто не мешает задавать идентификаторы UID и GID вручную, но тогда обеспечение их уникальности и непересекаемости выливается в муторный процесс - и для автоматизации которого предназначен поставляемый в дистрибутиве "389-DS" плагин "Distributed Numeric Assignment Plugin (DNA; libdna-plugin)".
Задача плагина DNA состоит в том, чтобы при обнаружении в атрибутах "uidNumber" и "gidNumber" числа "99999" (недопустимое значение как условный ключ, дающий плагину понять, что нужно действовать; по умолчанию "0") автоматически вместо "99999" установить следующее значение из отсчитываемого плагином последовательного диапазона.
По умолчанию плагин DNA неактивен.
Включение в GUI "389 Console" выглядит так:
DS Console -> Configuration -> Plug-ins -> Distributed Numeric Assignment Plugin:
Enable: on
Save.
Enable: on
Save.
В случае использования репликации в режиме "multi-master" возникают проблемы наложения идентификаторов, так как плагин DNA не проверяет занятость идентификаторов, просто отсчитывая их по своему внутреннему счётчику. Как вариант решения проблемы ранее предлагалось установить разные диапазоны для разных LDAP-серверов, а сейчас лучшим вариантом считается использование "разделяемой конфигурации", к которой плагины DNA периодически обращаются для согласования свободных диапазонов.
Добавление атрибутов и классов в схему данных.
Возможно имеющейся в дистрибутиве "389-DS" схемы данных окажется недостаточно для решения поставленных производством задач. Это легко исправляется - достаточно корректно (строго в соответствии с документацией) описать дополнительные сущности и добавить их к общей схеме.
Схема данных LDAP выстраивается из множества компонентов, объединяемых и наслаиваемых. Атрибуты и классы загружаются поэтапно и на каждом следующем могут переопределять имеющиеся. В общем случае порядок таков:
/usr/share/dirsrv/schema/*.ldif
/etc/dirsrv/schema/*.ldif
/etc/dirsrv/slapd-ldap0-example-net/schema/*.ldif
/etc/dirsrv/schema/*.ldif
/etc/dirsrv/slapd-ldap0-example-net/schema/*.ldif
Предположим, нам необходимо хранить в профиле пользователя номер его читательского билета и дату последнего посещения. Создадим набор атрибутов и дополнительный класс для данных, имеющих отношение к библиотеке (синтаксис весьма строгий, регистр символов имеет значение; отбивка текстовых блоков знаками "#" полезна для отслеживания разрешённой длины строки - всего 80 символов):
$ vi /etc/dirsrv/slapd-ldap0-example-net/schema/90libraryCust.ldif
#
dn: cn=schema
#
################################################################################
#
attributeTypes: ( libraryCardID-oid
NAME 'libraryCardID'
DESC 'Example library card number'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
X-ORIGIN 'user defined' )
#
################################################################################
#
attributeTypes: ( libraryDateLast-oid
NAME 'libraryDateLast'
DESC 'Date of last visit to the library'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
X-ORIGIN 'user defined' )
#
################################################################################
# (adding a new class 'libraryCust' based on existing 'inetOrgPerson')
#
objectClasses: ( libraryCust-oid
NAME 'libraryCust'
DESC 'A class that adds specific user attributes'
SUP inetorgperson
STRUCTURAL
MAY ( libraryCardID $ libraryDateLast )
X-ORIGIN 'user defined' )
#
################################################################################
#
dn: cn=schema
#
################################################################################
#
attributeTypes: ( libraryCardID-oid
NAME 'libraryCardID'
DESC 'Example library card number'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
X-ORIGIN 'user defined' )
#
################################################################################
#
attributeTypes: ( libraryDateLast-oid
NAME 'libraryDateLast'
DESC 'Date of last visit to the library'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
X-ORIGIN 'user defined' )
#
################################################################################
# (adding a new class 'libraryCust' based on existing 'inetOrgPerson')
#
objectClasses: ( libraryCust-oid
NAME 'libraryCust'
DESC 'A class that adds specific user attributes'
SUP inetorgperson
STRUCTURAL
MAY ( libraryCardID $ libraryDateLast )
X-ORIGIN 'user defined' )
#
################################################################################
#
Конфигурационные файлы схемы данных загружаются поочерёдно, в алфавитном порядке их имён - потому наш, начинающийся с "90*", будет применён примерно последним, уже поверх типовой схемы.
Для применения достаточно перезапустить LDAP-инстанс:
# stop-dirsrv ldap0-example-net && start-dirsrv ldap0-example-net
# journalctl -xe
# journalctl -xe
Схему данных можно дополнять и с помощью команд утилиты "ldapmodify", но в таком случае изменения для применения при последующем запуске LDAP-сервиса будут выведены в автоматически генерируемый конфигурационный файл "99user.ldif" - с корявыми переносами, возможной перекодировкой в "base64" и вообще неудобный для восприятия.
Экспорт и импорт данных (не резервное копирование).
Рассмотрим предлагаемые документацией способы экспорта записей (для импорта подходы аналогичные).
В дистрибутиве "389-DS" имеются специализированные утилиты для экспорта данных из "BerkeleyDB" в LDIF-файл. Написанная на "Bash" утилита "db2ldif" является обёрткой для более низкоуровневой утилиты "ns-slapd" и для работы требует кратковременной остановки LDAP-сервиса. Написанная на "Perl" утилита "db2ldif-online" является обвязкой для вызова команды создания задачи посредством "DSUtil::ldapmod", не требует остановки LDAP-сервиса, но не выдаёт результата немедленно.
Оба варианта не подходят для частых небольших выборок - остановка сервиса при этом неприемлема, а ожидание выполнения внутренней задачи и поиск её результатов слишком сложны и неочевидны.
Есть способ сделать это проще, посредством утилит "ldapsearch" и "ldapadd" (обе оперируют с форматом LDIF - "LDAP Data Interchange Format").
Запрашиваем все записи с атрибутами нужной нам ветви данных и выгружаем в текстовый файл:
$ ldapsearch -x -LLL -h 127.0.0.1 -D "cn=Directory Manager" -W -b "dc=example,dc=net" > ./dump_ldap0.example.net-YYYYMMDD-HHMM.ldif
Полученный LDIF-файл используем как источник данных для загрузки в другой LDAP-сервис:
$ ldapadd -x -h 127.0.0.1 -D "cn=Directory Manager" -W -c -S ./dump_ldap0.example.net-YYYYMMDD-HHMM.ldif.report -f ./dump_ldap0.example.net-YYYYMMDD-HHMM.ldif
Может статься так, что "Distinguished Name (DN)" записей в LDAP-сервисе, куда импортируются данные, отличается от исходного. Учитывая простоту синтаксиса LDIF, заменить атрибуты DN перед импортом "дампа" несложно:
$ sed -i -E 's/(dn:.*)(dc=example,dc=net)$/\1dc=example,dc=com/' ./dump_ldap0.example.net-YYYYMMDD-HHMM.ldif
Удаление конфигурации и данных "389-AS/DS".
После того, как наигрались с тестовыми "инстансами", их легко удалить посредством дистрибутивных perl-утилит "remove-ds" и "remove-ds-admin".
Файлы сервиса по большей части размещаются в следующих директориях - возможно их потребуется зачистить вручную:
# ls -l /etc/dirsrv /var/*/dirsrv
Переход к настройке SSL-шифрования клиентских подключений.
31 мая 2021 в 17:28
1 июня 2021 в 20:10