UMGUM.COM (лучше) 

Репликация между 389-DS ( Наладка равноправной взаимной "multi-master" репликации между LDAP-серверами "389-DS". )

12 декабря 2019  (обновлено 15 марта 2020)

OS: "Linux Ubuntu 18 (Bionic) LTS".
Application: "389 Administration & Directory Server v.1.3.7", "Stunnel".

Задача: наладить равноправную взаимную "multi-master" репликацию пользовательской области данных между LDAP-серверами "389-DS", предварительно настроенными в соответствии с отдельной инструкцией на этом сайте.

В документации заявлено, что "RHDS v10" поддерживает до 20 (двадцати) одновременно работающих связей репликации "multi-master". Не уверен, но возможно у его бесплатного аналога "FDS v1.3" есть ограничение до 4 (четырёх) связей репликации "multi-master" - но нам этого вполне хватит, так как планируется одновременная работа трёх LDAP-серверов.

Последовательность настройки связей и репликации как таковой следующая:

1. Настраиваем "потребителей (consumers)" данных.
2. Настраиваем "поставщиков (suppliers)" данных.
3. Инициируем связи, поочерёдно для всех поставщиков.
4. Проверяем успешность и полноту синхронизации данных.
5. Опционально настраиваем связку с сервером, не поддерживающим SSL.
6. Обнаруженные проблемы.


Сведения и подходы, используемые в этой инструкции, почерпнуты в официально документации от разработчиков "RHDS v10 (FDS v1.3)" и "RHDS v8 (FDS v1.1)".

Учитывая то, что все LDAP-сервера нашей схемы репликации будут работать в режиме "multi-master", то и настройки "потребителей (consumers)" и "поставщиков (suppliers)" везде будут сделаны по единому принципу, только с различием в именованиях.

Настраиваем "потребителей (consumers)" данных.

Прежде всего, для работы репликации на LDAP-серверах всех участников нужно активировать подсистему журналирования изменений - "Changelog":

$ vi ./changelog-enable.ldif

dn: cn=changelog5,cn=config
objectclass: top
objectclass: extensibleObject
cn: changelog5
nsslapd-changelogdir: /var/lib/dirsrv/slapd-ldap0-example-net/changelogdb
nsslapd-changelogmaxage: 7d

$ ldapadd -x -h 127.0.0.1 -D "cn=Directory Manager" -W -f ./changelog-enable.ldif

В GUI "389 Console" это выглядит так:

DS Console -> Configuration -> Replication -> Supplier Settings:
  Enable Changelog: on
  Changelog database directory: Use default
  Max changelog records: unlimited
  Max changelog age: 7 days
Save.

На каждом LDAP-сервере, работающем в роли "потребителя (consumer)" данных, необходимо создать учётную запись "Supplier Bind DN", которая будет использоваться для аутентификации при подключении к нему "поставщиков (suppliers)" данных:

$ ldapmodify -h 127.0.0.1 -D "cn=Directory Manager" -W

dn: cn=Replication Manager,cn=config
changetype: add
objectClass: inetorgperson
objectClass: person
objectClass: top
cn: Replication Manager
sn: Supplier Bind DN Entry
userPassword: ***
passwordExpirationTime: 20380119031407Z
nsIdleTimeout: 0

^d

Обращаю внимание, что учётная запись для репликационных связей должна быть вне зоны действия самой репликации - так как в противном случае мы не сможем задать отличающиеся параметры и пароли для разных серверов. Лучше следовать рекомендациям разработчиков и создать её в контексте настроек "cn=config".

На каждом сервере, работающем в роли "потребителя (consumer)" данных, настраиваем и активируем подсистему приёма данных репликации:

$ vi ./replication-consumer-config.ldif

dn: cn=replica,cn=dc\3Dexample\2Cdc\3Dnet,cn=mapping tree,cn=config
objectclass: top
objectclass: nsDS5Replica
objectclass: extensibleObject
cn: replica
nsds5replicaroot: dc=example,dc=net
nsds5replicaid: 1
nsds5replicatype: 3
nsds5flags: 1
nsds5ReplicaPurgeDelay: 604800
nsds5ReplicaBindDN: cn=Replication Manager,cn=config

$ ldapadd -x -h 127.0.0.1 -D "cn=Directory Manager" -W -f ./replication-consumer-config.ldif

В GUI "389 Console" это выглядит так:

DS Console -> Configuration -> Replication -> userRoot (for "dc=example,dc=net") -> Replica Settings:
  Enable Replica: on
  Replica Role: Multi Master
  Common Settings:
    Replica ID: 1 # (для каждого сервера отличающийся, разумеется)
    Purge delay: 7 days
  Update Settings:
    # Локальная учётная запись, через которую разрешено принимать данные репликации.
    Current Supplier DNs: cn=Replication Manager,cn=config
Save.

Настраиваем "поставщиков (suppliers)" данных.

На каждом сервере, работающем в роли "поставщика (supplier)" данных, активируем и настраиваем подсистему отправки данных репликации - этих записей может быть несколько, по количеству целевых LDAP-серверов, куда будет осуществляться отправка данных:

$ vi ./replication-supplier-config.ldif

dn: cn=ldap1.example.net,cn=replica,cn=dc\3Dexample\2Cdc\3Dnet,cn=mapping tree,cn=config
objectclass: top
objectclass: nsDS5ReplicationAgreement
cn: ldap1.example.net
nsds5replicahost: ldap1.example.net
nsds5replicaport: 636
nsds5ReplicaTransportInfo: SSL
nsds5ReplicaBindDN: cn=Replication Manager,cn=config
nsDS5ReplicaBindCredentials: ***
nsds5replicabindmethod: SIMPLE
nsds5replicaroot: dc=example,dc=net
description: Sending replication data from ldap0.example.net to ldap1.example.net

$ ldapadd -x -h 127.0.0.1 -D "cn=Directory Manager" -W -f ./replication-supplier-config.ldif

В GUI "389 Console" это выглядит так:

DS Console -> Configuration -> Replication -> userRoot (for "dc=example,dc=net") -> Context Menu -> New Replication Agreement:
  Supplier:
    Name: ldap0.example.net
    Description: Sending replication data from ldap0.example.net to ldap1.example.net
  Consumer: ldap1.example.net:636
  Connection: Use SSL
  Authentication mechanism:
    Simple Bind (BindDN/Password):
      Bind as: cn=Replication Manager,cn=config
      Password: ***
  Subtree: dc=example,dc=net
  Select replication criteria:
    Enable Fractional Replications: off # (реплицируем всё)
  Provide schedule information: Always keep directories in sync
  Initialaze Consumer: Do not initialise consumer
Save.

Иногда полезно настроить блокировку передачи некоторых атрибутов - если получатель данных не нуждается во всём их множестве. В дальнейшем управление исключениями возможно как через GUI "389 Console", так и через атрибут в записи параметров репликации - например: "nsDS5ReplicatedAttributeList: (objectclass=*) $ EXCLUDE atribute1 atribute2".

Инициируем связи, поочерёдно для всех поставщиков.

После того, как "поставщика (supplier)" данных настроен, запускаем процесс репликации:

$ vi ./replication-supplier-start.ldif

dn: cn=ldap1.example.net,cn=replica,cn=dc\3Dexample\2Cdc\3Dnet,cn=mapping tree,cn=config
changetype: modify
replace: nsDS5BeginReplicaRefresh
nsDS5BeginReplicaRefresh: start

$ ldapadd -x -h 127.0.0.1 -D "cn=Directory Manager" -W -f ./replication-supplier-start.ldif

В GUI "389 Console" это выглядит так:

DS Console -> Configuration -> Replication -> userRoot (for "dc=example,dc=net") -> "ldap1.example.net" -> Context Menu -> Initialaze Consumer.

После запуска процедур в корне реплицируемых структур плагином "Replication Plugin" автоматически будут созданы служебные записи (по количеству связей) вроде "cn=repl keep alive 1,dc=example,dc=net" - очевидно, изменять вручную их параметры не нужно.

Проверяем успешность и полноту синхронизации данных.

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

Выгружаем все данные с исходного сервера:

$ LDAPTLS_REQCERT=allow ldapsearch -x -LLL -H ldaps://ldap0.example.net:636 -D "uid=app0,ou=Accounts,ou=Services,dc=example,dc=net" -W -b dc=example,dc=net > ./ldap0.ldif

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

$ LDAPTLS_REQCERT=allow ldapsearch -x -LLL -H ldaps://ldap1.example.net:636 -D "uid=app0,ou=Accounts,ou=Services,dc=example,dc=net" -W -b dc=example,dc=net > ./ldap1.ldif

Если версии серверов "389-DS" одинаковы, или близки, то сравнение полученных в идентичных условиях LDIF-файлов покажет их минимальное различие или вообще отсутствие таковых:

$ diff -urBN ./ldap0.ldif ./ldap1.ldif ./out.diff

Если версии серверов существенно отличаются, то сортировка атрибутов в выдаче может смутить. Например, от "389-DS v1.3.2.16":

dn: dc=example,dc=net
objectClass: top
objectClass: domain
dc: example
....

...а от "389-DS v1.3.7.10":

dn: dc=example,dc=net
dc: example
objectClass: top
objectClass: domain
....

Очевидно, что с точки зрения протокола LDAP набор атрибутов идентичен, но порядок отличается, и такая перестановка чуть-ли не во всех записях. Простейшее сравнение утилитой "diff" покажет полный бардак между LDIF-файлами.

Для обхода этой проблемы можно применить специализированные утилиты сравнения LDIF-файлов. В комплекте с "SUN/Oracle OpenDS" идёт java-утилита "ldif-diff", но её сложно найти отдельно. Когда-то была популярна "ldapdiff", ныне для истории сохранённая в "launchpad.net", но она старовата и я её не пробовал. Есть ещё perl-скрипт "ldifdiff.pl", но им мне не приходилось пользоваться. Написанная на "Go" утилита "ldifdiff" свежее всех, доступнее и вполне справляется с задачей сравнения LDIF-файлов, опираясь на логику описания, а не последовательности атрибутов:

$ ldifdiff ./ldap0.ldif ./ldap1.ldif -i atribute1 -i atribute2 > ./out.diff

Опционально настраиваем связку с сервером, не поддерживающим SSL.

Наладка репликации между инстансами "389-DS" несложна, но имеется подводный камень на этапе обеспечения защиты от прослушивания посторонними передаваемых данных. Правильнее всего воспользоваться встроенной функциональностью шифрования соединений посредством постоянного "SSL/TLS" или вызываемого "StartTLS", но иногда вмешаться в конфигурацию какого-то из участников схемы обмена данными не представляется возможным.

Иного простого способа защитить передаваемые данные, кроме как провести трафик репликации в отдельно создаваемых шифрованных туннелях, не нашлось. Не принципиально, но из "ipsec" и "stunnel" в данном случае был выбран последний.

# aptitude install stunnel4

Включаем автозапуск сервиса:

# vi /etc/default/stunnel4

....
ENABLED=1
....

Дополняем конфигурацию "по умолчанию" парой дополнительных параметров:

# vi /etc/stunnel/stunnel.conf

output = /var/log/stunnel4/stunnel.log
include = /etc/stunnel/conf.d

Создаём выделенную директорию для конфигураций SSL-туннелей:

# mkdir -p /etc/stunnel/conf.d

Формируем описания SSL-туннеля:

# vi /etc/stunnel/conf.d/ldap1.example.net.conf

[ldap1.example.net]
client = yes
accept = 127.0.0.1:3891
connect = ldap1.example.net:636
verifyChain = no

Перезапускаем сервис:

# systemctl restart stunnel4
# systemctl status stunnel4

Проверяем, запущено ли прослушивание локального TCP-порта туннеля:

# netstat -apn | grep -i "^tcp" | grep -i "stunnel"

tcp ... 127.0.0.1:3891 0.0.0.0:* LISTEN .../stunnel4

Пробуем запросить удалённый LDAP-сервер через SSL-туннель, обращаясь к нему по незащищённому, казалось бы, соединению:

$ ldapsearch -x -v -H ldap://127.0.0.1:3891 -D "uid=app0,ou=Services,dc=example,dc=net" -W -b uid=userOne,ou=People,dc=example,dc=net

После успешной проверки связи в настройках репликции заменяем адрес удалённого LDAP-сервера, которому отправляются данные репликации, на "локальную петлю" и выделенный туннелю TCP-порт:

$ ldapmodify -h 127.0.0.1 -D "cn=Directory Manager" -W

dn: cn=ldap1.example.net,cn=replica,cn=dc\3Dexample\2Cdc\3Dnet,cn=mapping tree,cn=config
changetype: modify
replace: nsds5replicahost
nsds5replicahost: 127.0.0.1

dn: cn=ldap1.example.net,cn=replica,cn=dc\3Dexample\2Cdc\3Dnet,cn=mapping tree,cn=config
changetype: modify
replace: nsds5replicaport
nsds5replicaport: 3891

^d

Я заметил, что без перезапуска LDAP-инстанса изменения параметров репликации не применяются в полном объёме (TCP-порт изменяется, а вот адрес подключения остаётся прежним):

# stop-dirsrv ldap0-example-net && start-dirsrv ldap0-example-net

Обнаруженные проблемы: атрибут "userPassword".

При наладке репликации от "389-DS v1.3.2.16" к "389-DS v1.3.7.10" выяснилось, что первый не отправляет добавления атрибута "userPassword" - просто его игнорирует со следующим сообщением в журнале событий:

....
... NSMMReplicationPlugin - agmt="cn=ldap0.example.net" (127:3891): Consumer failed to replay change (uniqueid ..., CSN ...): Protocol error (2). Will retry later.
....

То есть, атрибут "userPassword" реплицируются, но не всегда, а по следующему принципу:

Не реплицируются:
  changetype: modify
  add: userPassword

Реплицируются:
  changetype: modify
  replace: userPassword

Проблема известна разработчикам, зафиксирована в трекере ошибок и уже исправлена - между инстансами "389-DS v1.3.7.10" не наблюдается.

Обнаруженные проблемы: атрибут "homeDirectory".

Аналогично вышеприведённому описанию проблемы с атрибутом "userPassword" при наладке репликации от "389-DS v1.3.2.16" к "389-DS v1.3.7.10" выяснилось, что первый не отправляет добавления атрибута "homeDirectory" - просто его игнорирует. При этом на принимающей стороне в журнале событий фиксируется предупреждение о неконсистентности набора данных:

....
... conn=55491 op=196 RESULT err=0 tag=103 nentries=0 etime=0.0007638565 csn=... - missing attribute "homeDirectory" required by object class "posixAccount"
....

Ео есть, атрибут "homeDirectory" реплицируются, но не всегда, а по следующему принципу:

Не реплицируются:
  changetype: modify
  add: homeDirectory

Реплицируются:
  changetype: modify
  replace: homeDirectory

Обнаруженные проблемы: небольшое расхождение между журналами репликации.

При наладке репликации от "389-DS v1.3.2.16" к "389-DS v1.3.7.10" неоднократно случались расхождения на одну запись между "changelog"-ами, что сопровождалось повторяющимся при каждой репликации сообщением в журнале событий:

....
... agmt="cn=ldap0.example.net" (127:3891) - Can't locate CSN ... in the changelog (DB rc=-30988). The consumer may need to be reinitialized.
....

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


Заметки и комментарии к публикации:


Оставьте свой комментарий ( выразите мнение относительно публикации, поделитесь дополнительными сведениями или укажите на ошибку )