Федерация в Keycloak с AD: как это устроено внутри
Прошлый пост был про то, что попадает в токен. Этот — про то, откуда данные берутся до этого, и что происходит в момент логина. Потому что федерация — не «настроил и забыл», а конвейер из пяти стадий, и ломается он всегда на одной из них.
Две модели, которые постоянно путают
Прежде чем разбирать поток нужно развести два разных механизма:
- User Federation — AD выступает источником пользователей. Идентичность создаётся там, Keycloak её подтягивает: аутентификация идёт по паролю источника, атрибуты читаются оттуда.
- Identity Brokering — AD выступает внешним провайдером входа. Keycloak не видит пароль вообще, получает assertion/токен и на его основе заводит или связывает локального пользователя.
Разница принципиальная: в первом случае Keycloak знает секрет (или проверяет его через bind), во втором — доверяет подписи и заявлению. Все дальнейшие вопросы «почему нет атрибута» и «почему не обновилось» решаются в разных местах в зависимости от того, какой из двух механизмов у нас применяется.
Что происходит в момент логина (User Federation)
Пошагово:
- Пользователь вводит логин. Keycloak ищет его в кэше провайдера.
- Промах кэша → lookup в AD по настроенному атрибуту (username, sAMAccountName, UPN, mail — что выбрано при создании провайдера).
- Найден → проверка пароля: bind сервисной учёткой, затем bind от имени пользователя. Пароль в Keycloak не сохраняется, он уходит в источник.
- Успех → атрибуты прогоняются через mappers и складываются в локальную модель пользователя (в зависимости от Edit Mode).
- Только теперь в игру вступают client scopes и protocol mappers из второго моего поста про Keycloak: они берут уже готовую модель и формируют токен.
Стадии 1–4 — это федерация, стадия 5 — выдача. Пустой claim почти всегда означает, что сломалась одна из первых четырёх, а не пятая.
Три режима, определяющие актуальность данных
- Imported — данные копируются в БД Keycloak при первом входе и живут там. Быстро, но между синхронизациями — устаревшие.
- Always read from source — атрибуты читаются при каждом обращении. Всегда свежие, но есть задержка при опросе самой AD, зависимость от доступности AD в момент логина — жёсткая.
- Write-back — изменения из Keycloak уезжают в AD. Требует прав на запись и аккуратности.
Выбор режима — это не техническая мелочь, а решение о том, где истина. Если отдел меняют в AD, а Keycloak держит копию — кто-то обязательно увидит старую должность в токене и придёт с вопросом.
Синхронизация: что и когда подтягивается
- Полная — обходит всех пользователей источника. Долго, нагружает каталог, нужна для первичного наполнения и после больших изменений.
- Инкрементальная — по метке изменения в источнике. Быстрее, но членство в группах и некоторые атрибуты подхватываются не так охотно, как хотелось бы.
- On-demand — при первом входе конкретного пользователя.
- Событийная — источник сам сообщает об изменениях. Самое свежее и самое хрупкое: ломается тихо.
Синхронизация обновляет модель пользователя. Она НЕ отзывает выданные токены. Старый access token живёт до истечения, refresh — до своего срока.
Маппинг: где чаще всего рассыпается цепочка
Данные идут в два прыжка:
- Mapper федерации: атрибут AD → атрибут модели Keycloak
- Protocol mapper в client scope: атрибут модели Keycloak → claim в токене
Правило, снимающее половину инцидентов: нет значения в модели — бесполезно крутить protocol mapper; значение в модели есть, но mapper не привязан к нужному scope — в токене ничего не появится.
Имя атрибута на обоих прыжках должно совпадать посимвольно.
Проблемы и как с ними бороться
1. Пользователь не находится при логине
→ Проверить атрибут поиска и его уникальность: искать по mail — плохая идея, у людей бывает несколько адресов и дубли. Логин должен быть однозначным.
→ Проверить фильтр поиска: слишком узкий — часть пользователей не видна.
→ Проверить, что сервисная учётка реально видит нужную OU.
2. Логин работает, атрибуты пустые
→ Смотреть модель пользователя, а не токен. Если там пусто — виноват mapper федерации, режим чтения или права учётки. Если там есть — идти в client scope.
3. Атрибут изменился в AD, а в Keycloak старый
→ Проверить режим: копия или чтение из источника.
→ Прогнать синхронизацию и убедиться, что она вообще отработала, а не завершилась с ошибкой в логах.
→ Пользователю всё равно нужен перелогин, старый токен не обновится сам.
4. Вложенность групп не видна
→ Источник отдаёт только прямые членства. Либо рекурсивный запрос в фильтре группы, либо разворачивать иерархию, либо не строить авторизацию на глубокой вложенности.
5. Дубли пользователей
→ Внешний идентификатор должен быть стабильным и неизменным. Пересоздание провайдера или смена атрибута-идентификатора — и вы получаете дубликат юзера с другим внутренним ID. Лечится только разбором и склейкой вручную.
6. Лимит выдачи при полной синхронизации
→ Источник по умолчанию отдаёт ограниченное число записей за раз. Включить пажинацию — для крупных каталогов это обязательный пункт.
7. Федерация падает целиком
→ Истёк пароль сервисной учётки, отвалился TLS, сеть до источника — и логина нет ни у кого, включая админов. Единая точка отказа.
→ Отдельная read-only учётка с ротацией пароля, шифрованный канал, таймауты, пул соединений, алерт на сам факт коннекта — не на количество ошибок, а на отсутствие успешных проверок.
→ Держать локального аварийного администратора, который не зависит от федерации.
8. Несколько источников, одинаковые логины
→ Два провайдера с пересекающимися username дают неоднозначность. Либо различать логины суффиксом, либо явно разделять пространства имён, либо не заводить второй провайдер без реальной необходимости.
9. Медленный логин
→ Частая причина — чтение из источника на каждый запрос плюс кэш, выставленный в ноль. Ищем баланс: короткий TTL кэша обычно приемлем, а вот полный отказ от кэша кладёт каталог под нагрузкой.
10. Синхронизация есть, а поведение не меняется
→ Кэш. Пользовательский кэш, кэш провайдера, кэш сессий — три разных слоя, и после правок групп/ролей чистить надо все три. Плюс реальный токен: декодировать и посмотреть, а не верить настройкам.
По теме можно еще очень много писать, есть куча дополнительных подводных камней, но пока на этом закончу, так как всю базу, в основном, расписал.