Как стать автором
Обновить
160.86

История одного SSO: как мы подружили два независимых каталога пользователей через Keycloak

Время на прочтение8 мин
Количество просмотров1.5K

Привет, Хабр! Меня зовут Аскар Добряков, я ведущий эксперт направления защиты бизнес-приложений в К2 Кибербезопасность. При внедрении Single Sign-On, на первый взгляд, все должно идти по накатанной схеме: настраиваем федерацию со службой каталогов, связываем identity provider с service provider, проверяем работу токенов — и готово. Но дьявол, как всегда, кроется в деталях. Зачастую приходится решать нестандартные задачи. Например, как быть, если у вас две равноправных организации-партнера со своими требованиями к безопасности, и каждый хочет контролировать аутентификацию пользователей, но при этом пользователи одного партнера должны ходить в систему другого?

В этой статье я расскажу, как мы с Даниилом Золотаревым, инженером нашей практики защиты приложений, проводили разработку кастомного аутентификатора для Keycloak, который позволил элегантно решить техническую задачу, стоявшую перед головной и дочерней организациями. Покажу, как мы обошли ограничения штатной функциональности, не нарушая требований безопасности обеих сторон. В процессе вы увидите технические детали реализации, примеры кода и практические советы по созданию собственных аутентификаторов.

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

Противоречивые требования

Тезис: наш заказчик является дочерней организацией крупной компании. Ему требовался доступ к корпоративным системам головной организации.

Антитезис: у головной организации есть собственный каталог пользователей, в котором хранятся пользователи нескольких дочерних организаций и  франчайзи, в том числе и нашего заказчика. 

Тезис: однако в этом случае пользователям заказчика пришлось бы запоминать две пары логин-пароль, что неминуемо  вызвало бы путаницу и связанные с этим проблемы. Поэтому заказчик настаивал на использовании собственного каталога.

Антитезис: но такое решение не подходит головной организации. Безопасники не хотят интегрировать систему с чужим каталогом (что можно было бы легко сделать в Keycloak с помощью механизма User Federation - LDAP), потому что мало ли кто и каких пользователей туда добавит; головная организация хочет сама контролировать состав пользователей.

Тезис: у Keycloak для таких случаев предусмотрен механизм Identity Broker.

Identity Broker подразумевает, что Keycloak в головной организации при нажатии на определенную кнопку на форме аутентификации (например, «авторизоваться с учетной записью дочерней компании») переадресовывает пользователя на другой Keycloak, выступая при этом как client, а после успешной авторизации —- предоставляет доступ к целевой системе. При этом Keycloak в головной организации делает запись у себя, добавляя связь между пользователем во внешнем Keycloak и в своей базе.

Антитезис: однако в рамках данного механизма в рамках Keycloak головной организации при первичном входе пользователя дочерней организации создается связь между учетными записями в двух разных серверах Keycloak. Чтобы подтвердить свою подлинность и создать такую связь, пользователь для проверки должен ввести пароль от каталога дочерней организации и от каталога головной организации. То есть мы возвращаемся к проблеме, возникшей в самом начале с двумя комплектам пар логин-пароль.

Тезис: процесс создания связи определяется с помощью потока аутентификации (flow) в Keycloak головной организации. Мы можем изменить его и убрать проверку с вводом логина-пароля.

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

В поисках решения

Итого, суть конфликта требований:

1.    Дочерняя организации хочет использовать данные аутентификации из своего каталога.

2.    Головная организация хочет контролировать доступ к своей системе.

Заказчик был категоричен: пользователи должны аутентифицироваться на портале только один раз со своими учетными данными дочерней организации. Головная компания же хотела сохранить полный контроль над доступом через собственный каталог пользователей и в каком-то виде сохранить проверку пользователей.

Что нам предстояло сделать?

Разработать концепцию доверия между брокером аутентификации в доверенной организации и провайдером у заказчика, а затем поток аутентификации на основе этой концепции.

Нам пришлось основательно поломать голову, чтобы найти компромисс. Сначала заказчик предлагал разработать скрипт для синхронизации учетных данных между Active Directory и Keycloak. Правда, каким образом забирать пароли, проводить аутентификацию и реализовывать линки, мы так и не смогли решить. 

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

Решение нашлось в архитектуре Keycloak: система предоставляет настраиваемые потоки аутентификации (authentication flow) с возможностью определения последовательности проверок – пароля, cookies, UTP и других параметров и проверок. Опираясь на наш предыдущий опыт разработки кастомных аутентификаторов, мы приняли решение реализовать проверку пользователей через кастомные пользовательские атрибуты.

Как выглядит концепция доверия, согласованная в итоге всеми сторонами?

У каждого пользователя в каталоге заказчика создается дополнительное поле — идентификатор (username) в каталоге головной организации.

У каждого пользователя в каталоге головной организации есть также дополнительное поле — идентификатор организации, к которой этот пользователь относится.

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

Мы написали кастомный блок с валидацией дополнительного атрибута «Идентификатор организации», уникального идентификатора каждой франшизы в системе, а также сверки логинов пользователей в двух системах

Для демонстрации концепции развернули тестовый стенд Keycloak. Заказчик посчитал такое решение наиболее удачным, потому что в целом оно штатное — используется стандартный функционал identity broker, немного доработанный в части flow создания учетных записей. То есть вся база построена на уже отлаженных возможностях системы, а кастом только там, где его предполагает Keycloak. В общем, все по best practice. 

Партнеры проанализировали наше предложение, и наконец утвердили проект и согласовали начало работ по внедрению.

Разработка кастомного аутентификатора и принцип его работы

Архитектура решения базируется на организации защищенной связи между двумя автономными каталогами пользователей. В каталоге головной организации уже был атрибут, отвечающий за организацию дочерней организацией. Параллельно в каталоге дочерней организации для каждого пользователя сохраняется соответствующий ему идентификатор из каталога — логин из системы головной организации. 

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

  1. Есть ли пользователь с таким логином, который указан в дополнительном атрибуте, в каталоге головной организации?

  2. Если есть — какая дочерняя организация прописана для него, соответствует ли это нашей организации?

Такая система перекрестных ссылок обеспечивает надежную идентификацию пользователей и позволяет однозначно сопоставлять учетные записи между разными доменами.

Для реализации мы разработали кастомный аутентификатор. О том, как добавить его в систему аутентификации, мы уже рассказывали в отдельной статье.Технически это плагин, наследующий базовый класс аутентификаторов Keycloak. На его основе мы создали и скомпилировали собственный модуль, который интегрируется в поток аутентификации Keycloak “First broker login”.

При первой попытке пользователя подключиться к системе головной организации через Keycloak с учетной записью заказчика система выполняет двухэтапную проверку:

  • проверяется существование пользователя в системе головной организации и соответствие его данных с данными подключающегося пользователя;

  • система анализирует наличие пользователя в каталоге дочерней организации и сверяет его идентификатор с записью у головной.

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

Создание потока аутентификации и его интеграция в Keycloak

Наше решение потребовало создания потока аутентификации. 

  1. Пользователь идет на портал (целевой системы) → Портал переадресовывает его на Keycloak головной компании. 

  2. В форме аутентификации пользователь может выбрать способ авторизации: с учетной записью головной организации или данными из каталога дочерней. Наш сценарий — из каталога дочерней. 

  3. При выборе этого варианта Keycloak головной компании начинает работать в режиме Identity Broker и переадресовывает пользователя на Keycloak, который находится в контуре заказчика, выступая при этом как client.

  4. После успешной авторизации на Keycloak в контуре заказчика пользователь переадресовывается обратно на Keycloak головной компании (Identity Broker).

  5. Если это не первичный вход → У пользователя уже есть учетная запись в Keycloak головной компании. Для нее не задан пароль, но указана связь с вышестоящим Keycloak.

  6. Первичный вход → Создаем нового пользователя.

  7. Помним про ограничения службы безопасности головной компании, поэтому перед созданием пользователя проходят дополнительные проверки:

    a. что в каталоге заказчика-дочерней компании для учетной записи указан username из каталога головной компании. 

    b. что в каталоге головной компании есть пользователь с таким username, и для него указана организация, соответствующая заказчику-дочерней компании.

    8. Если эти условия выполняются, в Keycloak головной компании (он же Identity Broker) создается учетная запись без пароля и со ссылкой на вышестоящий Keycloak. Далее авторизация этого пользователя проходит уже как постоянного посетителя портала. 

    Теперь рассмотрим стандартный поток аутентификации на брокере.

First broker login
First broker login

Create User If Unique — на этом шаге проверяется, существует ли учетная запись Keycloak с тем же адресом электронной почты или именем пользователя, что и у учетной записи поставщика удостоверений. Если учетная запись не существует, аутентификатор создает новую локальную учетную запись Keycloak, связывает ее с поставщиком удостоверений, и процесс завершается. В противном случае система переходит к следующему потоку обработки существующей учетной записи.

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

Handle existing account — подпоток, который обрабатывает существующие учетные записи с тем же адресом электронной почты или именем пользователя.

Account verification options — подпоток, который верифицирует учетные записи с одинаковым адресом электронной почты и именем пользователя.

Verify Existing Account by Re-authentication — предназначен для связывания пользователей в IdP и IdBroker.

Username Password Form For IdP — обеспечивает повторную аутентификацию с учетными данными пользователя из брокера.

Тестирование решения, поиск багов и проблема недостаточной документации Keycloak

Основная сложность была в извлечении атрибута пользователя Franchise ID из UserInfo. Проблема крылась в недостаточно подробной документации Keycloak по работе с пользовательскими объектами. Она дает общее понимание структуры данных, но точный механизм получения атрибутов и их формат толком не описывает. 

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

После ряда экспериментов, мы взяли класс IdpCreateUserIfUniqueAuthenticator за основу для кастомного аутентификатора. В его исходном коде есть комментарий, который говорит, что мы можем сами доработать логику и сопоставлять учетные записи не только по username или email, но и по другим атрибутам.

Основные сложности возникли при синхронизации каталогов организаций. В некоторых случаях аутентификатор был неверно привязан к каталогу, иногда некорректно указывался «Идентификатор организации». В таких ситуациях аутентификатор блокировал запросы, что подтверждало корректность его работы с точки зрения безопасности.

Мы последовательно устраняли выявленные недочеты, находя оптимальные или компромиссные решения. Например, дочерняя компания взяла на себя обязательство по регулярному обновлению идентификаторов в своих каталогах.


В заключение отмечу, что кастомизация Keycloak — действительно мощный инструмент для решения нестандартных задач аутентификации. Мы убедились, что даже при конфликтующих требованиях к безопасности c его помощью можно найти элегантное решение, удовлетворяющее все стороны.

Надеюсь, эта статья поможет вам в реализации собственных проектов на базе Keycloak.

Теги:
Хабы:
+3
Комментарии0

Публикации

Информация

Сайт
k2.tech
Дата регистрации
Численность
101–200 человек
Местоположение
Россия