«Эллес» и Global Catalog. Как вычерпать наполняющийся бассейн

от автора

Привет, Хабр! Меня зовут Динар, я один из разработчиков службы каталогов «Эллес» в ИТ-Холдинге Т1. Кратко о продукте «Эллес», откуда он взялся и что из себя представляет, писал мой коллега. Подробное описание и документацию можете найти на официальном сайте. Но если коротко, то «Эллес» — это служба каталогов (в основе которой лежит глубоко модернизированный код проекта Samba), функционально аналогичная Microsoft Active Directory, с возможностью работы в гетерогенной среде с бесшовной интеграцией и миграцией клиентов.

Оглавление

Предисловие

Вот представьте: работаете вы над проектом, никого, как говорится, не трогаете, и тут внезапно врывается владелец продукта и происходит следующий диалог:

— Надо срочно значительно расширить использование нашего продукта! Но… нужен Global Catalog.

— Эмм… ну, у нас ещё даже предварительной аналитики нет…

— Хорошо, давайте попробуем экспертно оценить, сколько нужно времени на аналитику и минимальную реализацию?

— Не, ну если экспертно… пусть будет квартал, думаю, что‑то покажем к тому времени.

— Отлично! Вернёмся к обсуждению в следующем квартале.

Спустя три месяца:

— Ну что, есть, что показать?

— Тут это, такое дело… нам бы ещё немного времени на реализацию…

— Немного — это сколько?

— Годик, полтора…

Знакомая ситуация, правда? Такое случается в каждой первой организации, и тыкать пальцем после срыва сроков в поисках виноватых смысла нет, ведь если ситуация уже случилась, встаёт лишь один вопрос: «Что делать будем, Капитан?!».

Эта статья о том, как наша команда разработки столкнулась осенью 2023 года с похожей ситуацией неопределённости объёмов работы, и во что это всё вылилось в итоге.

Что такое Global Catalog и для чего он нужен?

Если вы работаете в крупной компании (скажем, от 5000 сотрудников), и у вас есть несколько доменов внутри одного леса Active Directory (например, corp.company.org, region1.company.org и так далее), то представьте себе такую ситуацию. Администратор из Москвы пытается добавить сотрудника из Магадана по его имени пользователя IvanovIvanI в очередную группу. Администратор открывает оснастку для управления пользователями и группами, в окне добавления пользователя в целевую группу начинает набирать его имя, нажимает «Enter» и… через мгновение получает пользователя из домена Дальневосточного Федерального Округа.

Как? Откуда это приложение знает про человека из другого домена? Оно ведь не может каждый раз ходить по всем контроллерам во всех доменах леса! Это и есть один из простых примеров работы глобального каталога (GC).

Домен, управляемый службами Active Directory, может состоять из множества секций или контекстов именования (Naming Context). Уникальное имя (Distinguished Name) объекта содержит достаточно сведений, чтобы найти реплику секции, содержащей объект. Однако во многих случаях пользователь или приложение не знает DN целевого объекта или какой раздел может содержать объект. Глобальный каталог позволяет пользователям и приложениям находить объекты в дереве домена Active Directory, учитывая один или несколько атрибутов целевого объекта.

Итак, глобальный каталог — это особый индексированный набор данных на одном или нескольких контроллерах домена, который содержит частичные атрибуты всех объектов во всём лесу Active Directory.

Рисунок 1. GC‑контроллеры в многоуровневом лесе.

Рисунок 1. GC‑контроллеры в многоуровневом лесе.

GC помогает быстро искать данные по всему лесу. Представьте: вам нужно найти всех пользователей с почтой @region5.company.org, работающих в отделе продаж. Примерно так будет выглядеть LDAP‑запрос:

(&(objectClass=user)(mail=*@region5.company.org)(department=Sales))

Если в доменном лесу нету GC‑контроллера, то для поиска вам нужно:

  1. Запросить все домены в лесу.

  2. Для каждого домена отправить запрос на его контроллер.

  3. Ждать ответа от каждого.

  4. Собрать результаты.

  5. Объединить.

  6. Отфильтровать.

Это может занять от нескольких секунд до минуты в отдельных случаях! А если в лесу есть GC‑контроллер? Миллисекунды!

И это не просто «быстрее». Это разница между тем, чтобы сотрудник мог найти коллегу до того, как забудет имя, и тем, чтобы он вышел из офиса с криком: «Где этот человек?! Я уже 10 минут жду!»

Есть множество других практических примеров, когда без GC определённые задачи выполнить было бы либо невозможно, либо крайне затруднительно и/или медленно. Но думаю, что этих двух примеров уже достаточно для понимания важности поддержки этой функциональности контроллерами Active Directory.

С чего всё начиналось

C самого начала развития продукта «Эллес» было понятно, что без полноценной реализации глобального каталога путь в большие домены нам закрыт. Казалось бы, Microsoft уже давным‑давно открыла спецификации и документацию по Active Directory — бери и реализуй, чего там сложного‑то? Но, как это часто бывает, «гладко было на бумаге», а по факту очень многого в документации нет, какие‑то части устарели, где‑то есть неточности, или вовсе не описаны целые протоколы.

В 2023 году продукт представлял из себя, по сути, ответвление оригинальной Samba с небольшими точечными доработками и исправлениями некоторых ошибок. Потому реализация GC была, скажем так, аналогична «ванильной» Samba: её не было. Ну, как «не было», — была, но только в каком‑то зачаточном представлении, если можно так выразиться:

  1. Во время введения в однодоменный лес в роли контроллера Samba могла реплицировать необходимые данные всех партиций.

  2. «Поднимали» GC‑порты — 3268 и 3269, но запросы на них лучше было не отправлять.

  3. Были зачатки функциональности, отвечающей за проверку «GC мы или нет» там, где это требовалось.

  4. KCC иногда понимал, когда не хватает некоторых NC для построения связей.

  5. и… всё 🙂

Историю всего пути сейчас трудно вспомнить, но, скорее всего, на тот момент мы посчитали вполне реалистичным реализовать за какие‑нибудь 3–4 месяца поддержку роли GC с достаточной функциональностью для работы в гетерогенном лесу рядом с продуктами Microsoft. Как молоды мы были же мы ошибались…

Первые шаги. Partial Attribute Set

Первое, с чего решили начать — это реализовать получение и хранение ссылок на данные частичных реплик контекстов именований и их проверку. В частности, ссылки на основной раздел (root), такие как serverReference, пользователи, группы и так далее.

Но тут же наружу вылезла другая «беда» оригинальной Samba: невозможность работать в многоуровневом лесу Active Directory. То есть Samba не понимала, что такое дочерние и родительские домены. Эту функциональность решили развивать параллельно, потому что по своей сути она не зависела от глобального каталога. Но об этих работах нужно писать отдельную статью (ждите, мы вернёмся), потому сейчас я ограничусь лишь упоминанием существенных ограничений функциональности в начале нашего пути.

Сначала взялись за реализацию частичной репликации родительского (root) раздела при вводе «Эллес» в роли контроллера в дочерний (child) домен. Для этого нужно было научить контроллер понимать, что такое «частичная реплика» (Partial replica) и «набор атрибутов частичной реплики» (Partial Attribute Set).

Partial Attribute Set (PAS) — это набор атрибутов объектов определённого NC, которые должны быть реплицированы на все серверы глобального каталога. Это тот самый минимальный и необходимый состав атрибутов объектов частичной реплики партиции, который должен содержаться на GC для поиска по всему лесу Active Directory. Microsoft определяет базовый набор атрибутов, которые автоматически включаются в PAS. Они необходимы для аутентификации и часто используются при поиске. Но этот набор можно изменять, для этого необходимо изменить свойство isMemberOfPartialAttributeSet в определении атрибута в схеме Active Direсtory.

И тут, вроде, всё было даже не сложно: реализовали поддержку необходимых атрибутов и флагов (в соответствии с MS‑ADTS), доработали LDAP‑сервер и сервис репликации, реализовав следующую логику:

  1. При запуске сервиса «Эллес», как только будет завершена репликация конфигурации (CN=Configuration), отправляется сигнал в подсистему GC.

  2. GC проверяет список NC в CN=Partitions, нет ли там чего‑то нового.

  3. Если есть новый NC, то для него создаётся свой PAS и информация об этом отправляется в сервис репликации.

  4. Сервис репликации видя, что есть новый PAS, обращается к другому контроллеру для начала репликации необходимых данных.

  5. Спустя какое‑то время у нас есть частичная реплика всех NC этого леса.

  6. Как только все данные были получены, «поднимался» порт GC и выставлялся флаг готовности isGlobalCatalogReady (находится в RootDSE).

Это позволило локально разрешать ссылки на объекты всего леса и корректно отображать статус глобального каталога для других контроллеров и клиентов. Соответственно, с этих пор контроллер «Эллес» научился отвечать на запросы как полноценный глобальный каталог.

Всё, можно в релиз? «Ну да, ну да, пошёл я чай пить…» — сказал сервис KCC.

Изменение леса «на лету»

Первые доработки лишь добавляли возможность синхронизации данных частичных партиций. Но что делать при изменении списка NC? А что там с топологией синхронизации данных? Тут тоже было не всё так гладко, как хотелось.

KCC и топология

Как я уже упоминал, в оригинальной Samba не было поддержки многоуровневого леса. Соответственно, она не умела выстраивать соответствующую топологию соединений. За построение топологии в Active Directory отвечает KCC (Knowledge Consistency Checker). В случае с GC‑контроллером сервис KCC должен выстраивать соединения с другими контроллерами таким образом, чтобы была возможность реплицировать данные по каждому из NC.

Разобрав небольшую кучу из граблей и ошибок, любезно разложенных в Python‑коде разработчиками KCC оригинальной Samba, мы смогли добиться построения объектов соединений (NTDSConnection) и, соответственно, входящих и исходящих связей (repsFrom/repsTo) для частичных партиций. Эта часть разработки была настолько существенной, что достойна отдельной статьи, потому здесь ограничусь лишь коротким упоминанием.

На начало 2024 года обеспечили полносвязность топологии. Теперь мы могли реплицировать необходимые данные независимо от того, в какую часть дерева леса будет помещён контроллер «Эллес» в роли глобального каталога.

Недоступность GC при обновлении данных

Само по себе изменение списка NC к каким‑либо сбоям не приводило, проблемы начинались после длительного отсутствия контроллера «в сети». Представим ситуацию:

  • контроллер вывели на обслуживание на несколько дней;

  • за время простоя в лес добавился дополнительный домен;

  • контроллер вывели на эксплуатацию;

  • прошла пара часов, а контроллер всё ещё «не поднял» флаг GC;

  • клиенты начинают жаловаться на сбои по своим запросам…

Как так вышло? Мы же, вроде, всё сделали, чтобы… ах, да. Точно. Флаг isGlobalCatalogReady не выставляется в TRUE до тех пор, пока не будут реплицированы данные со всех NC. А у нас тут появился новый домен, информации о котором совсем нет. В принципе, «Эллес» делал ровно то, что в нём было заложено: ждал, пока появится входящее соединение на репликацию по новому NC, а затем будут получены все данные, указанные в PAS. Но ведь у нас есть и другие NC, по которым все данные уже давно получены.

Эту проблему решили для начала, что называется, «в лоб»: флаг isGlobalCatalogReady выставляли в TRUE практически сразу после запуска сервиса, но контроллер отвечал на запросы только по тем партициям, чьи данные были помечены как «актуальные», то есть синхронизированы полностью и без ошибок. В первом приближении это сработало как надо.

Тонкая настройка и краевые случаи

Предыдущее решение с «поднятием» флага готовности GC было простым, но в некоторых случаях требовалась более тонкая настройка.

Global Catalog Delay Advertisement

Предположим, опять же, что контроллер «Эллес» был выведен на обслуживание на очень длительное время, а после его ввода в эксплуатацию оказалось, что есть большие изменения во всех частичных партициях, которые у него есть в списке. «Что страшного может произойти?» — флаг готовности GC будет поднят, но доступных частичных партиций для исполнения запросов от клиентов может не оказаться. Так быть не должно.

На помощь пришла конфигурация «Global Catalog Delay Advertisement», описанная где‑то в дебрях документации по Active Directory. По своей сути, этот параметр выставляет задержку, которая необходима для подготовки разделов к работе в режиме глобального каталога. Сказано — сделано, в параметрах конфигурации «Эллес» появился ldap_server:gc_delay_advertisement, который отвечает именно за то, о чём сказано в документации.

Теперь флаг готовности GC вёл себя следующим образом:

  • Если gc_delay_advertisement не выставлен или равен 0, то работаем, как было указано выше: устанавливаем TRUE практически сразу после запуска сервиса.

  • Если gc_delay_advertisement больше 0, то это значение воспринимается как задержка в секундах, пока не пройдёт указанное время, GC остаётся недоступен, продолжая синхронизацию, а когда время истекает, устанавливаем флаг готовности в TRUE и работаем, как указано в предыдущем пункте.

К слову, продукты Microsoft в плане «поднятия» флага isGlobalCatalogReady работают точно так же, как и «Эллес». Так что для администраторов такое поведение должно быть знакомым и очевидным.

Изменение набора атрибутов в PAS

Ранее я уже упоминал, что набор атрибутов в PAS можно изменить. И вот тут скрывались интересные нюансы, которые мы не учли сразу.

Если набор атрибутов будет сокращён (администратор исключит какой‑то из атрибутов), то ничего сложного нет: просто выводим указанный NC из работы (а мы уже умеем не отвечать на запросы в таких случаях) и удаляем из всех объектов указанный в нём атрибут, выполнив попутно индексацию. Тут даже тяжёлых синхронизаций не требуется.

А если набор атрибутов будет расширен? На первый взгляд, это тоже не проблема: получаем сведения об изменении PAS в конфигурации, выводим из работы NC и начинаем синхронизацию с другим GC, а потом вернём NC в работу. Это сработает ровно до тех пор, пока мы не окажемся в такой топологии соединений:

Рисунок 2. Линейная топология.

Рисунок 2. Линейная топология.

Что мы тут видим? Есть три сайта с GC‑контроллерами, которые выстроены в «линейную» топологию связи. Теперь давайте посмотрим на диаграмме (сильно упрощённой для наглядности) процесс распространения изменений:

Рисунок 3. Обновление PAS.

Рисунок 3. Обновление PAS.

Сначала распространяются изменения конфигурации и, на самом деле независимо от них, после этого инициируется обновление частичных партиций. Как видно на диаграмме, могли происходить ситуации, когда DC2 ещё не получил обновления NC от DC1, но DC3 уже запросил изменения этого же NC от DC2, и это выглядит логичным — он ведь уже знает о том, что PAS обновился, и ему уже нужны эти изменения. Что происходит дальше? DC3 получает неполные данные по обновлённому NC, либо вовсе пустые обновления, после чего считает, что у него уже актуальная БД и смело вводит частичную партицию в работу на GC. «Лёгким движением руки» мы получили некорректные ответы на запросы клиентов и неактуальную базу данных на одном из контроллеров. Если кто‑то скажет: «так NC должен ведь становиться недоступным при обновлении данных!» — всё верно, но это работает только для клиентов, а для других контроллеров репликация продолжает работать, причём по всем разделам глобального каталога.

Поиск документации по Active Directory на этот счёт никаких результатов не дал. Первичный разбор «а что делает Windows» в таких ситуациях тоже ситуацию не прояснил. После некоторых раздумий разработчик решил сделать так, «как он сам бы сделал, если реализовывал такую функциональность в продуктах Microsoft»:

  • После получения конфигурации PAS выводим соответствующий NC — перестаём отвечать на запросы по нему.

  • При запросе репликации этого NC другим контроллером отвечаем ему специальной служебной ошибкой, говорящей «этот NC сейчас недоступен».

  • Как только синхронизация завершится, можем начинать отвечать на все запросы.

Звучит логично, да, но только после того, как столкнёшься с ситуацией, разберешь её первопричины и докопаешься до сути.

После проверки и сравнения поведения с Windows, а также глубокого анализа сетевого трафика, оказалось, что выбранное поведение оказалось точно таким же, как в продуктах Microsoft! Логические рассуждения привели к единственно верному решению.

Итоговая реализация

На данный момент глобальный каталог в службе каталогов «Эллес» полноценно работает в гетерогенном лесу Active Directory. Он может спокойно работать совместно с продуктами от Microsoft, реплицируя данные в обе стороны. Клиенты могут с различных операционных систем отправлять запросы в глобальный каталог «Эллес», даже не подозревая о том, кто им отвечает — та самая «бесшовная интеграция».

Конфигурация (включение и отключение) возможна как через оснастки Windows, так и через инструменты, входящие в комплект поставки «Эллес».

Статус готовности глобального каталога корректно отображается в разделе RootDSE. Доступна конфигурация некоторых дополнительных параметров.

На сколько всё это затянулось? Выводы

Последние исправления по GC были сделаны в конце лета 2025 года. Практически два полных года прошло с момента начала разработки этой, как оказалось, действительно глобальной функциональности. Кто‑то, вероятно, скажет, что всему виной неполная изначальная аналитика, общо сформулированная задача, а другие ответят что‑то вроде: «да вы документацию на Samba и Active Directory вообще видели, читали?!». И со своей стороны я скажу, что правы и те, и другие. Но давайте честно: многие ли из вас читают инструкцию перед использованием? Да и, в конце концов, какое это сейчас имеет значение? Ведь самое главное, что работа сделана, и сделана она хорошо. Руководство ликует, все клиенты получили фичу, отделу продаж есть, что предложить новым клиентам — все счастливы.

Но всё же, какие выводы мы из этого сделали внутри команды? Что изменили в организации планирования?

Первое: всегда брать время на аналитику, как бы ни «горели сроки» у клиента. Ведь даже если мы побежим делать прямо сейчас, всегда остаётся (и даже множится) риск где‑то упустить сроки или допустить критическую ошибку в функциональности. Качественная аналитика по задаче позволяет оценить и увидеть множество «подводных камней» ещё до начала реализации.

Второе: всем всегда нужно «вчера», но задача инженерной команды и их руководителей — отстаивать подходы, способствующие качественной разработке с минимизацией рисков. Честно говоря, мы ещё раз убедились в необходимости соблюдения внутренних бизнес‑процессов ради получения качественного результата и продукта.

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

Послесловие

Разработка функциональности глобального каталога не происходила непрерывно, скорее, наоборот: она постоянно прерывалась, потому говорить «на разработку потрачено два года», конечно, не совсем корректно. Мы постоянно сталкивались с недоработками, ошибками, отсутствием чего‑либо в смежных модулях и функциональностях. За всё это время был сделан огромный пласт прочей работы.

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

А к нам тут недавно снова пришли с очередной «идеей на миллион» и… знаете что? С нетерпением ждём начала разработки, ведь в этом суть нашей работы: творить, воплощать идеи в жизнь, удовлетворять потребности бизнеса и клиентов.

На начало 2024 года GC ещё не был окончательно готов, а нас уже ждали на внедрениях. Нужно было дать клиентам что‑то полезное, но страх положить домен клиента вводил в паралич, и мы решили начать с «малого»: с контроллеров только для чтения. Там ведь ничего не сломаешь, правда? Так думали мы, еще не зная, что аналог Microsoft RODC скрывает в себе массу подводных камней. Особенно в гетерогенной среде. Хотите узнать, как мы их обходили? В таком случае, до скорой встречи ;‑)

ссылка на оригинал статьи https://habr.com/ru/articles/1066912/