Как мы ускорили ванильную FreeIPA в 20 раз!!! (почти)
Мы — это кто?

Мы — команда внедрения продуктов «Группы Астра». Каждый день имеем дело с такими решениями, как СУБД Tantor, ПК СВ «Брест», Termidesk, RuPost, WorksPad, RuBackup, VMmanager и другие.
Как инженеры внедрения (Integration Team), мы обеспечиваем интеграцию всех продуктов компании, участвуем в составлении технической документации, проведении приёмо-сдаточных испытаний, плотно взаимодействуем с представителями заказчика по технической части, но самое важное — «наживую» внедряем собственные российские продукты в ИТ-среду наших заказчиков.
А еще мы умеем стыковать наши продукты с уже имеющимся у заказчика импортным или отечественным ПО.
Сложно? — Да.
Интересно? — Безумно!
Помимо наших продуктов (например, Astra Automation), для автоматизации и обеспечения подхода Infrastructure as Code (IaC) мы используем в своей работе:
— Ansible, Salt
— Terraform, OpenTofu
— Python, Bash
— Docker, Podman
— Git
Но в статье речь пойдет об ALD Pro (Astra Linux Domain Pro).
ALD Pro
ALD Pro — это набор сетевых служб, которыми можно управлять через единый веб-портал. В технологический стек входят следующие компоненты:
FreeIPA
Bind9
MIT KDC
389 Directory Server
Reprepo
ISC DHCP
TFTP + PXE
Zabbix, Grafana
CUPS
Samba
Syslog-ng
Так что же не так с FreeIPA?
Один заказчик попросил предоставить инструмент нагрузки LDAP-запросов, да не простой, а с GUI и графиками.
Наша команда в своей работеактивно использует open source инструмент нагрузочного тестирования Locust (англ. Саранча)
Сам по себе Locust является ядром нагрузки с минимальным функционалом из коробки, но этот функционал расширяется за счет использования Locustfiles, которые пишутся на чистом Python, что позволяет не ограничиваться набором инструкций, как, например, в Dockerfile/Containerfile/Vagrantfile, а писать отдельные Python-модули.
Таким образом, «Кузнечик» состоит из двух частей: Python-модуля для Locust и изменяемой инфраструктуры служб нагрузки в docker-compose файле.
Мы добавили требуемый функционал (нагрузка LDAP, DNS и Kerberos), позволяя при этом пользоваться встроенными в Locust возможностями.

Но за LDAP в FreeIPA отвечает компонент 389 Directory Server.
Так и есть.
Дальше по тексту все упоминания про LDAP подразумевают работу именно с 389-ds.
Инструмент нагрузки предоставлен — можно расходиться?
На создании инструмента нагрузки ничего не закончилось, а все только началось.
Мы нагрузили ALD Pro, получили графики и…
Как ранее, а точнее в 2023 году (а может, и раньше, но это не важно), было сказано на Хабре:

Разработчики нашего «Кузнечика» приняли решение сравнить производительность MS AD и ALD Pro. Тем более наши предположения заказчик подтвердил на следующей встрече, изъявив желание провести подобное сравнение. Но мы уже были готовы к подобному вопросу.
Горизонтальная ось — количество пользователей (единица нагрузки, а не пользователь в LDAP):

Да, здесь мы сделали так:

Ну, и другой срез:

Подождите!!! Это же катастрофа!!!
Как мы можем пойти к заказчику с такими графиками? Нужно что-то делать!
Сравнительный анализ
Мы судорожно стали делать различные сборки и сравнивать нагрузку.
Во-первых, нам предстояло исключить влияние собственных наработок ALD Pro и ОС Astra Linux:

Как видно из графика, ALD Pro на ОС Astra Linux уступает в производительности FreeIPA на Fedora в 2 раза, но все эти сборки существенно проигрывают MS AD.

График сравнения числа ответов в секунду подтверждает разницу. То есть разница в производительности между FreeIPA на Fedora и ALD Pro на Astra Linux есть, но незначительная по сравнению с разницей между ванильной FreeIPA и MS AD.
Если мы, как команда внедрения, не можем повлиять на ускорение запросов в операционной системе Astra Linux и продукте ALD Pro, то, быть может, стоит рассмотреть способы ускорения LDAP во FreeIPA?
— Нам это под силу?
— Пока не попробуешь, не узнаешь.
Создаем тикеты по данной проблеме в продуктовые команды и идем погружаться в 389-ds.
Нагрузка и бинарный поиск

Какая диагностика без информации, что происходит в ОС?
Проверяем, насколько грузит процессор сервис LDAP:

Служба Dirsrv грузит процессор по максимуму, независимо от операционной системы и версии FreeIPA.
Проверили на железе: результат такой же.

Ресурсов процессора не хватает в нескольких случаях.
Вариант с бэкдором (вспоминаем XZ backdoor) мы откинули практически сразу, но допускали возможность ошибки в исходном коде.
Под рукой была старая версия FreeIPA на Fedora 34, которая нас приятно удивила: версия 2.0.1 389-ds работает быстрее!
Мы запустили ряд тестирований различных версий 389-ds, руководствуясь бинарным поиском.
Всего было рассмотрено 49 версий, начиная с 2.0.1 и заканчивая 3.1.1.
Здесь нужно объяснить, что 2.0.х, 2.1.х, 2.2.х, 2.3.х — это версии под разные релизы операционной системы.
2.0.х — под Fedora 34 и 35.
2.1.х — под Fedora 36.
2.2.х — под Fedora 37.
В общей сложности нами сделано несколько десятков нагрузочных тестирований на разных ОС и различными нагрузчиками.
Так мы с каждым разом оттачивали навыки владения «Кузнечиком» (плюсы есть во всем :)).
Использование контейнеров сильно облегчило нам эту задачу.
Прикладываем код, чтобы можно было самостоятельно проверить скорости:
# пример использования freeipa в контейнере # «быстрая» freeipa |
Результаты в студию!
(представим только наиболее значимые)
OS | FreeIPA | 389 Directory Server | Результаты |
---|---|---|---|
Fedora 34 | FreeIPA 4.9.6 | 389-ds 2.0.15 | высокие показатели производительности |
Fedora 35 | FreeIPA 4.9.7 | 389-ds 2.0.11 | высокие показатели производительности |
Fedora 35 | FreeIPA 4.9.8 | 389-ds 2.0.15 | высокие показатели производительности |
Fedora 35 | FreeIPA 4.9.10 | 389-ds 2.0.17 | низкие показатели производительности |
Fedora 36 | FreeIPA 4.9.9 | 389-ds 2.1.1 | высокие показатели производительности |
Almalinux 8 | FreeIPA 4.9.9 | 389-ds 2.1.1 | высокие показатели производительности |
Rocky Linux 8 | FreeIPA 4.9.10 | 389-ds 1.4.3.34 | высокие показатели производительности |
Fedora 37 (rawhid) | FreeIPA 4.9.10 | 389-ds 2.2.1 | высокие показатели производительности |
Fedora 37 (rawhid) | FreeIPA 4.9.9 | 389-ds 2.1.1 | высокие показатели производительности |
Fedora 35 | FreeIPA 4.9.11 | 389-ds 2.0.16 (не в контейнере) | высокие показатели производительности |
Мы специально выделили жирным результаты, которые можно сравнить с производительностью, близкой к производительности MS AD.
Вы можете проверить наши испытания и убедиться в этом самостоятельно с помощью более простых инструментов тестирования, например ldclt от разработчиков 389-ds или jmeter (сравнение в 7 главе).
Цифры могут быть другими, но сравнительный анализ покажет подобную дельту.
Так мы пришли к нашей версии, но уже с другой стороны.
Шел 2022 год…
Странный заголовок, но таковы реалии.
Давайте посмотрим на дату выхода релизов 389-ds:

Своими тестами мы исключили влияние ОС на замедление 389-ds.
Все, что связано с производительностью, прячется в самом 389-ds, и это точно шифрование.
«Чуть-чуть терпения», — говорим мы себе.
На скрине выделены релизы, между которыми большая разница в производительности. Таким образом, нас интересует разница между 2.0.16 (выпущен в июле 2022 года) и 2.0.17 (выпущен в ноябре 2022 года).
Посмотрим на сравнение между версиями.
А чтобы долго не томиться, можно сразу проваливаться в issue 5356.
С ноября 2022-го разработчики 389-ds внедрили RUST как дефолтную схему хранения паролей. А-ха!
For RUST build update the default password storage scheme
4. RUST vs C
RUST не уступает в скорости, говорили они и они, значит, и код должен быть быстрым?
Так ли это в нашем случае?
Для ответа на данный вопрос мы даже собрали свои пакеты из исходников, исключив данный коммит, чтобы получить более свежую версию 389-ds, но оставить дефолтную схему на С (хотя этих трудозатрат можно было избежать — ниже опишем).
На картинке ниже выделили новую схему на RUST.


Заметили разницу в названии между старым и вновь установленным дефолтным методом?
Запомните эту разницу: дальше по тексту это будет играть большую роль.
Погнали тесты!



На графиках нас интересуют красный и фиолетовый столбцы.

Новая схема на быстром RUST грузит процессор, число ответов выдает ниже в 3 раза, а время отклика в 20 раз больше старой схемы на медленном С.
Да, с 2022 года все продукты, базирующиеся на FreeIPA, стали в 3 раза медленнее безопаснее просто потому, что в 389-ds был внесен коммит со схемой на RUST.
Но FreeIPA — это open sourse продукт, которым пользуется и Red Hat.

Конечно, очень интересно сравнить производительность FreeIPA на Fedora и RHEL, так как RHEL тоже использует схему на RUST.
Добавляем:



Разницы в нагрузке нет, да и число запросов не многим больше. Это обнадеживает.
Криптография
Как уже было сказано выше, на скорость повлияли коммиты, связанные с ISSUE-5356, в которых был изменен бэкенд шифрования с PBKDF2_SHA256 (написанный на языке C) на PBKDF2-SHA512 (написанный на языке Rust).
Изменение должно было ускорить алгоритм шифрования + хеширования, но, напротив, привело к снижению скорости.
Существенная разница в скорости также сохраняется между PBKDF2_SHA256 (на C) и PBKDF2-SHA256 (на RUST):
У Rust число итераций для алгоритма RBKDF2 на языке Rust было захардкожено на 10 000, совсем недавно добавили возможность менять это значение в промежутке с 10 000 до 10 000 000 (ссылка на коммит и статью) со значением, по умолчанию равным 100 000, но все тесты проводились до появления этой функции.
У C число итераций для алгоритма PBKDF2 подбирается автоматически на основе производительности CPU (в нашем случае получилось 2 048).

В связи с разницей в числе итераций был проведен тест с 2 048 итерациями для алгоритма на RUST (изменено значение переменной с перекомпиляцией в новые пакеты).
Результат: PBKDF2_SHA256 (на C) кратно быстрее, чем PBKDF2-SHA256 (на RUST).

Пост автора этих двух алгоритмов, в котором упоминаются проблемы с производительностью кода, написанного на языке C в связи с ошибкой в библиотеке NSS.
Однако результаты тестирования демонстрируют обратное поведение.
Тесты показывают, что ошибка так и не устранена.
Коммиты с ISSUE-5356:
https://github.com/389ds/389-ds-base/commit/7d381b9a8b000f9a9facaf45b5e09f40da6bd318
https://github.com/389ds/389-ds-base/commit/41c8e4101b23a0cc2a630a8040bd58d96dbb60f3
Решение
Скорость simple bind во многом определяется используемым алгоритмом шифрования, потому что при передаче пароля серверу LDAP нужно произвести шифрование переданного пароля и сравнить с паролем из базы, что может являться узким горлышком при высокой нагрузке.
Чтобы выяснить, какие схемы доступны в 389-ds, используем команду (ALD-PRO — домен) :
# Посмотреть текущие значения |
Доступные схемы шифрования:
CLEAR, CRYPT, CRYPT-MD5, CRYPT-SHA256, CRYPT-SHA512, GOST_YESCRYPT, MD5, PBKDF2, PBKDF2-SHA1, PBKDF2-SHA256, PBKDF2-SHA512, PBKDF2_SHA256, SHA, SHA256, SHA384, SHA512, SMD5, SSHA, SSHA256, SSHA384, SSHA512
Мы подробно рассмотрели каждую из них. Вот наши выводы (постарались расставить по убыванию производительности):
CLEAR, CRYPT, CRYPT-MD5, CRYPT-SHA256, MD5, PBKDF2, PBKDF2-SHA1, SHA, SHA256, SHA384, SHA512, SMD5, SSHA, SSHA256, SSHA384 — не рекомендуются к использованию.
SSHA512 — менее безопасный, чем PBKDF2-SHA512 и PBKDF2_SHA256, но очень быстрый.
PBKDF2_SHA256 — менее безопасный, чем PBKDF2-SHA512, однако быстрый.
CRYPT-SHA512 — альтернатива PBKDF2-SHA512, точно сказать, какой из них безопаснее, нельзя, но CRYPT-SHA512 быстрее в несколько раз. Единственная информация, которую можно найти по этому вопросу.
PBKDF2-SHA256 — менее безопасный, чем PBKDF2-SHA512, медленный.
PBKDF2-SHA512 — алгоритм по умолчанию (с ноября 2022 года), безопасный, но медленный.
GOST_YESCRYPT — алгоритм ГОСТ Р 34.11–2012, самый медленный алгоритм из всех представленных, поддерживается только в Astra Linux 1.8.
Стоит учитывать, что все алгоритмы, представленные выше, небезопасные (кроме, возможно, GOST_YESCRYPT) согласно рекомендациям OWASP.
Для каждого из алгоритмов требуется гораздо более высокое значение итераций.
Сейчас открыт pull request на возможность параметризации числа итераций, но это будет относиться к новейшим версиям. Стоит понимать, что увеличение числа итераций очень негативно повлияет на производительность.
На данный момент для хранения паролей рекомендуются такие алгоритмы, как bcrypt, argon2i, scrypt, однако они не реализованы в 389-ds-base.
Важно исходить из требований к безопасности и не использовать алгоритмы шифрования как основной метод защиты паролей.
Например, если домен находится в закрытом контуре, защищенном другими СЗИ (средства защиты информации), можно использовать более быстрый метод шифрования. В любом случае нужно отталкиваться от конкретной архитектуры ИТ-инфраструктуры.
Для решения проблемы скорости simple bind нужно поменять алгоритм шифрования паролей в БД 389-ds-base на более быстрый и перезадать пароль пользователей.
Перезадать пароли важно, поскольку после смены алгоритма пароли останутся с предыдущим шифрованием.
Приводим команды:
# Посмотреть текущие значения # Заменить PBKDF2-SHA512 на другой алгоритм (в примере CRYPT-SHA512) # Поменять пароль Directory Manager # Поменять пароль пользователей ALD Pro/FreeIPA # Перезагрузить dirsrv (любой из 3 вариантов) |
Бонус
Еще мы выяснили, что есть возможность использования различных схем шифрования для разных пользователей.
Сейчас наши коллеги работают над реализацией данной функциональности в веб-интерфейсе ALD Pro, а пока мы это делаем через cli.
Например, можно назначить администраторам пароли, используя наиболее безопасный алгоритм, а пользователям — попроще, но более быстрый:
для администраторов в Astra Linux 1.8 — GOST_YESCRYPT,
для администраторов в Astra Linux 1.7 — PBKDF2-SHA512 / CRYPT-SHA512,
для пользователей — менее ресурсоемкий алгоритм SSHA512 / PBKDF2_SHA256.
MS AD
Со схемами шифрования во FreeIPA разобрались, но нужно разобраться и с MS AD.
Определимся с методом шифрования в MS AD — это даст понимание, почему такая высокая скорость.
Но по MS AD мало информации, MD4 или MD5.
Нашли это и это


и это

Если обратили внимание, мы проводили сравнение FreeIPA с MS AD на базе Windows 2012R2. Но для себя провели сравнение и с MS AD на базе Windows 2019.
Таким образом, мы сравнивали разные по скорости и безопасности методы шифрования. Отсюда такая разница в производительности.
Сертификация ФСТЭК
Раз мы выявили обратную зависимость «производительность — безопасность», стоит рассказать и про безопасность.
А что может быть нагляднее, чем сертификация ФСТЭК?
ALD Pro соответствует требованиям безопасности информации, установленным в документе «Требования по безопасности информации, устанавливающие уровни доверия к средствами технической защиты информации и средствам обеспечения безопасности информационных технологий» (ФСТЭК России, 2022) (далее — Требования), по 4 уровню доверия, что удостоверяется сертификатом соответствия № 4830, выданным ФСТЭК России 26 июля 2024 г., а также соответствует декларируемым в технических условиях функциональным возможностям.
Сертификат ФСТЭК России подтверждает, что ALD Pro может применяться в ИСОП (информационные системы общего пользования) II класса, а также в информационных системах, обрабатывающих информацию ограниченного доступа, не содержащую сведения, составляющие государственную тайну, в том числе:
в ГИС (государственные информационные системы) до I класса защищенности включительно;
в ИСПДн (информационные системы персональных данных) до I уровня защищенности включительно;
в АСУ ТП (автоматизированные системы управления производственными и технологическими процессами) до I класса защищенности включительно;
в составе ЗО КИИ (значимые объекты критической информационной инфраструктуры) до I категории включительно.
MS AD
Сертификаты ФСТЭК России отсутствуют, срок действия части сертификатов закончился в 2019 году (MS Server 2008, 2012), 2021 году (MS Server 2012R2), часть сертификатов приостановили/отозвали в 2022 году (MS Server 2016, MS 10).
FreeIPA
Свободное ПО, не сертифицировано во ФСТЭК России.
У тех заказчиков, где сертификация ПО является одним из ключевых требований, выбор очевиден. А теперь еще и скорость работы ALD Pro можно получить высокую.
SambaDC
Но мы не мы, если не сравним производительность FreeIPA и SambaDC.
В SambaDC же нет 389-ds с его новой дефолтной схемой на RUST.
На удивление SambaDC не грузит процессор, но и производительностью похвастаться не может (от слова «совсем»).
Вот эти зеленые пики в космос, они же время отклика, — это SambaDC:



Заметили, у нас изменились названия на ALD Pro Patched (применение более быстрой схемы шифрования)?
В процессе тестов мы использовали различные инструменты нагрузки (ldclt, jmeter) , чтобы проверить, нет ли узкого места на стороне самого «Кузнечика».
Тесты демонстрируют чуть более низкие результаты для Astra Linux 1.7.6uu1 + ALD Pro 2.4.0 по сравнению с Fedora 41 + FreeIPA 4.12.2 (1 и 2 тест соответственно).
В тестах на MSAD 2012R2 ldclt позволил нагрузить до 7000–7500 RPS, что в 5–6 раз выше значений, полученных при помощи нашего«Кузнечика».Причина меньшей производительности «Кузнечика» была в малом числе Worker-контейнеров. С увеличением числа Worker’ов добились производительности, сравнимой с ldclt или jmeter. Однако после оптимизации LDAP на базе 389-ds и проведения нагрузки на уже высоких значениях RPS мы выявили еще одну проблему. Рассказываем.
База, но не данных
Идем в базу TCP, так как LDAP — это TCP.
Проблема
В чем проблема? Проблема в том, что мы эмулируем клиентскую нагрузку с одного хоста, поэтому столкнулись с нехваткой портов для TCP-соединений при нагрузке уже «затюненного» 389-ds.
Почему не сталкивались с проблемой ранее? До «тюнинга» 389-ds не выдерживал нагрузку, которая могла бы привести к такой проблеме.
Почему этого не было в MS AD? 389ds и MS AD по-разному отрабатывают операцию unbind на транспортном уровне OSI, об этом ниже.
Вот как это выглядит на практике. Возьмем для наглядности инструмент ldclt и проведем нагрузку на уже затюненный 389-ds.

Что получаем? Через несколько десятков секунд нагрузка полностью приостанавливается ввиду того, что нет свободных портов.
В Jmeter, или «Кузнечике», без контейнеров произойдет аналогичная ситуация, так уж работает TCP.
Анализ
Начнем с того, что число соединений ограничивается 5 значениями: протокол, порт источника, порт назначения, IP-источника, IP-назначения.
В нашем случае протокол всегда TCP.
IP-назначения и порт назначения тоже постоянные.
Остаются два значения — порт источника и IP-источника. В случае использования инструментов нагрузочного тестирования специфика заключается в том, что IP-источника один и тот же, поэтому количество соединений получается сильно лимитированным.
Начнем с порта источника:
Если установить число свободных портов до максимальных 1024–65535 (в ядре linux это значение определяется параметром net.ipv4.ip_local_port_range), то получим 64511 доступных портов в одну единицу времени (порты 1–1023 служебные и требуют прав суперпользователя либо повышения прав).
Соединение TCP при переходе в статус TIME_WAIT не позволяет использовать занятый порт в течение последующих 60 секунд (время длительности TIME_WAIT) — делим 64511 портов на 60 секунд и получаем значение 1075.18 pps (Port per Second).
Округляем до целых значений и получаем 1075 — это максимальное число RPS, которое можно получить для одного хоста (не говоря о том, что некоторые порты могут быть заняты).
Принимая во внимание тот факт, что после оптимизации один экземпляр 389-ds сервер способен выдерживать нагрузку более чем в 3 000 RPS, для того чтобы нагрузить его на максимум одним клиентом нагрузочного тестирования, у этого клиента должно быть не менее трех IP-адресов сразу.
Мы помним, что один хост может использовать несколько разных IP-адресов (IP-alias/network namespaces/bridge). Для понимания принципа работы TCP-соединений прикладываем:
389 Directory Server
В системе Linux время задержки захардкожено, и изменить его в меньшую сторону не представляется возможным.


Вот что нам показывает Wireshark:
# TCP handshake LDAP (bindRequest) --> # Соединение со стороны клиента попадает в статус TIME_WAIT и ожидает своего завершения 60 секунд (значение захардкожено и равно 2 * MSL) |

MS AD
В это время у MS AD:
# TCP handshake LDAP (bindRequest) --> |
MS AD намеренно отправляет флаг RST при закрытии соединения, аналогичный механизм использует HAProxy при отправке Healthcheck’ов для экономии времени и ресурсов:

Получается:

Решение
Повлиять на количество портов со стороны нагрузчика мы не можем, уменьшить время ожидания TIME_WAIT мы тоже не способны.
Так почему бы нам не создать бОльшее количество нагрузчиков (в терминологии Locust — workers)?
Мы так и сделали — все наши воркеры крутятся в docker-контейнерах с использованием типа сети ipvlan, что позволяет каждому воркеру иметь свою пару IP: Порт.
Но есть нюанс! Использование простого bridge в Docker не поможет, и для решения мы использовали ipvlan, который позволяет каждому контейнеру иметь IP-адрес из подсети хоста (аналог bridge в qemu/kvm). Ниже вывод DST NAT таблицы на хосте при использовании простого docker bridge, и мы видим, что в итоге все контейнеры будут использовать адрес хоста, и, как следствие, число соединений мы не увеличим.

Подведем итоги

Здесь можно написать что-то подобное:
Никогда не сдавайся!
Не доверяй open-source решениям
Тестирование продуктов — один из важных процессов. При заимствовании разработок необходимо тестировать и эти разработки.
Безопасность MS AD под вопросом.
Но мы хотели бы обратить внимание на то, что российские продукты, построенные на базе открытых компонентов, совершенно сопоставимы по скорости, а кажущиеся преимущества MS AD в этом вопросе являются больше недостатком данной системы, который связан с использованием устаревших алгоритмов шифрования.
Как лучше действовать: накинуть контроллерам домена вычислительных ресурсов или упросить алгоритм шифрования, будет зависеть, конечно же, от потребностей конкретного проекта. Но смеем заметить, что до тех пор, пока в ИТ-инфраструктуре будет оставаться MS AD с ее NTLM-хешами, вариант с упрощением алгоритма шифрования видится вполне оправданным решением. Будем рады, если материалы нашего исследования окажутся вам полезны, тогда с вас лайки, комментарии, подписка.
В свою очередь хотели бы поблагодарить открытое сообщество, разработчиков ALD Pro, инженеров внедрения «Группы Астра» и всех, кто принимает активное участие в реализации проектов импортозамещения, чтобы они стали былью.