SSO на микросервисной архитектуре. Используем Keycloak. Часть №1

    В любой крупной компании, и X5 Retail Group не исключение, по мере развития возрастает количество проектов, где требуется авторизация пользователей. С течением времени требуется бесшовный переход пользователей из одного приложения в другой и тогда возникает необходимость использования единого сервера Single-Sing-On (SSO). Но как быть, когда такие идентификационные провайдеры как AD или иные, не обладающие дополнительными атрибутами, уже используются в различных проектах. На помощь придет класс систем под названием «идентификационные брокеры». Наиболее функциональными являются его представители, такие как Keycloak, Gravitee Access management и пр. Чаще всего сценарии использования могут быть различны: машинное взаимодействие, участие пользователей и пр. Решение должно поддерживать гибкий и масштабируемый функционал, способный объединить все требования в одном, и такие решением в нашей компании сейчас является индикационный брокер – Keycloak.



    Keycloak – это продукт с открытым исходным кодом, предназначенный для идентификации и контроля доступа и поддерживаемый компанией RedHat. Он является основой для продуктов компании использующих SSO – RH-SSO.

    Основные понятия


    Прежде чем начать разбираться с решениями и подходами следует определиться в терминах и последовательности процессов:



    Идентификация — это процедура распознавания субъекта по его идентификатору (проще говоря, это определение имени, логина или номера).

    Аутентификация – это процедура проверки подлинности (пользователя проверяют с помощью пароля, письмо проверяют по электронной подписи и т.д.)

    Авторизация – это предоставление доступа к какому-либо ресурсу (например, к электронной почте).

    Идентификационный брокер Keycloak


    Keycloak — это решение для управления идентификацией и доступом с открытым исходным кодом, предназначенное для использования в ИС где могут использоваться паттерны микросервисной архитектуры.

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

    Базовый функционал, поддерживаемый в Keycloak:

    • Single-Sign On and Single-Sign Out для браузерных приложений.
    • Поддержка OpenID/OAuth 2.0/SAML.
    • Identity Brokering – аутентификация с помощью внешних OpenID Connect или SAML идентификационных провайдеров.
    • Social Login – поддержка Google, GitHub, Facebook, Twitter для идентификации пользователей.
    • User Federation – синхронизация пользователей из LDAP и Active Directory серверов и других идентификационных провайдеров.
    • Kerberos bridge – использование Kerberos сервера для автоматической аутентификации пользователей.
    • Admin Console — для единого управления настройками и параметрами решения через Web.
    • Account Management Console – для самостоятельного управления профилем пользователей.
    • Кастомизация решения на основе фирменного стиля компании.
    • 2FA Authentication – поддержка TOTP/HOTP с помощью Google Authenticator или FreeOTP.
    • Login Flows – возможна саморегистрация пользователей, восстановление и сброс пароля и прочие.
    • Session Management – администраторы могут управлять из единой точки сессиями пользователей.
    • Token Mappers – привязка атрибутов пользователей, ролей и иных требуемых атрибутов в токены.
    • Гибкое управление политиками через realm, application и пользователей.
    • CORS Support – клиентские адаптеры имеют встроенную поддержку CORS.
    • Service Provider Interfaces (SPI) – большое количество SPI, позволяющих настраивать различные аспекты работы сервера: потоки аутентификации, идентификационных провайдеров, сопоставление протоколов и многое другое.
    • Клиентские адаптеры для JavaScript applications, WildFly, JBoss EAP, Fuse, Tomcat, Jetty, Spring.
    • Поддержка работы с различными приложениями, поддерживающими OpenID Connect Relying Party library или SAML 2.0 Service Provider Library.
    • Возможность расширения с использованием plugins.

    Для процессов CI/CD, а так же автоматизации процессов управления в Keycloak, может использоваться REST API/ JAVA API. Документация доступна в электронном виде:

    REST API https://www.keycloak.org/docs-api/8.0/rest-api/index.html
    JAVA API https://www.keycloak.org/docs-api/8.0/javadocs/index.html

    Идентификационные провайдеры уровня предприятия (On-Premise)


    Возможность аутентификации пользователей через User Federation сервисы.



    Также может быть использована сквозная аутентификация — если пользователи проходят аутентификацию на рабочих станциях с Kerberos (LDAP или AD), то они могут быть автоматически аутентифицированы на Keycloak без необходимости снова указывать свое имя пользователя и пароль.

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

    Список поддерживаемых СУБД обширен и включает в себя: MS SQL, Oracle, PostgreSQL, MariaDB, Oracle и другие. Наиболее протестированными на данный момент являются Oracle 12C Release1 RAC и Galera 3.12 cluster для MariaDB 10.1.19.

    Идентификационные провайдеры — social login


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



    Для аутентификации пользователей возможно использование OpenID/SAML Identity провайдеров.

    Типовые сценарии авторизации с использование OAuth2 в Keycloak


    Authorization Code Flow — используется с серверными приложениями (server-side applications). Один из наиболее распространенных типов разрешения на авторизацию, поскольку он хорошо подходит для серверных приложений, в которых исходный код приложения и даные клиента не доступны посторонним. Процесс в данном случае строится на перенаправлении (redirection). Приложение должно быть в состоянии взаимодействовать с пользовательским агентом (user-agent), таким как веб-браузер — получать коды авторизации API перенаправляемые через пользовательский агент.

    Implicit Flow — используется мобильными или веб-приложениями (приложения, работающие на устройстве пользователя).

    Неявный тип разрешения на авторизацию используется мобильными и веб-приложениями, где конфиденциальность клиента не может быть гарантирована. Неявный тип разрешения также использует перенаправление пользовательского агента, при этом токен доступа передается пользовательскому агенту для дальнейшего использовании в приложении. Это делает токен доступным пользователю и другим приложениям на устройстве пользователя. При этом типе разрешения на авторизацию не осуществляется аутентификация подлинности приложения, а сам процесс полагается на URL перенаправления (зарегистрированный ранее в сервисе).

    Implicit Flow не поддерживает токены обновления токена доступа (refresh tokens).
    Client Credentials Grant Flow — используются при доступе приложения к API. Этот тип разрешения на авторизацию обычно используется для взаимодействий «сервер-сервер», которые должны выполняться в фоновом режиме без немедленного взаимодействия с пользователем. Поток предоставления учетных данных клиента позволяет веб-службе (конфиденциальному клиенту) использовать собственные учетные данные вместо олицетворения пользователя для проверки подлинности при вызове другой веб-службы. Для более высокого уровня безопасности возможно вызывающей службе использовать сертификат (вместо общего секрета) в качестве учетных данных.

    Спецификация OAuth2 описана в
    RFC-6749
    RFC-8252
    RFC-6819

    JWT токен и его преимущества


    JWT (JSON Web Token) — открытый стандарт (https://tools.ietf.org/html/rfc7519), который определяет компактный и автономный способ для защищенной передачи информации между сторонами в виде JSON-объекта.

    Согласно стандарту, токен состоит из трех частей в base-64 формате, разделенных точками. Первая часть называется заголовком (header), в которой содержится тип токена и название хэш-алгоритма для получения цифровой подписи. Вторая часть хранит основную информацию (пользователь, атрибуты и т.д.). Третья часть – цифровая подпись.

    <encoded header>.<encoded payload>.<signature>
    Никогда не сохраняйте токен в вашей БД. Потому что действительный токен эквивалентен паролю, хранить токен– это все равно, что хранить пароль в открытом виде.
    Access-токен — это токен, который предоставляет доступ его владельцу к защищенным ресурсам сервера. Обычно он имеет короткий срок жизни и может нести в себе дополнительную информацию, такую как IP-адрес стороны, запрашивающей данный токен.

    Refresh-токен — это токен, позволяющий клиентам запрашивать новые access-токены по истечении их времени жизни. Данные токены обычно выдаются на длительный срок.

    Основные преимущества применения в микросервисной архитектуре:

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

    JWT токен — состав


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

    Тип токена хранится в ключе «typ». Ключ «typ» игнорируется в JWT. Если ключ «typ» присутствует, его значение должно быть JWT, чтобы указать, что этот объект является JSON Web Token.

    Второй ключ «alg» определяет алгоритм, используемый для шифрования токена. По умолчанию он должен быть установлен в HS256. Заголовок кодируется в base64.

    { "alg": "HS256", "typ": "JWT"}
    Payload (содержимое) — в полезной нагрузке хранится любая информация, которую нужно проверить. Каждый ключ в полезной нагрузке известен как «заявление». К примеру, в приложение можно войти только по приглашению (закрытое промо). Когда мы хотим пригласить кого-то поучаствовать, мы отправляем ему письмо с приглашением. Важно проверить, что адрес электронной почты принадлежит человеку, который принимает приглашение, поэтому мы включим этот адрес в полезную нагрузку, для этого сохраним его в ключе «e-mail»

    { "email": "example@x5.ru" }
    Ключи в payload могут быть произвольными. Тем не менее, есть несколько зарезервированных:

    • iss (Issuer) — определяет приложение, из которого отправляется токен.
    • sub (Subject) — определяет тему токена.
    • aud (Audience) – массив чувствительных к регистру строк или URI, являющийся списком получателей данного токена. Когда принимающая сторона получает JWT с данным ключом, она должна проверить наличие себя в получателях — иначе проигнорировать токен.
    • exp (Expiration Time) — указывает, когда истекает срок действия маркера. Стандарт JWT требует, чтобы во всех его реализациях маркеры с истекшим сроком действия отклонялись. Exp ключ должен быть отметкой времени в unix формате.
    • nbf (Not Before) — это время в unix формате, определяющее момент, когда токен станет валидным.
    • iat (Issued At) — этот ключ представляет собой время, когда маркер был выдан и может быть использован для определения возраста JWT. iat ключ должен быть отметкой времени в unix формате.
    • Jti (JWT ID) — строка, определяющая уникальный идентификатор данного токена c учетом регистра.

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

    Берутся закодированные в base64: заголовок и payload, они объединяются в строку через точку. Затем эта строка и секретный ключ поступает на вход алгоритма шифрования, указанного в заголовке (ключ «alg»). Ключом может быть любая строка. Более длинные строки будут наиболее предпочтительнее, поскольку потребуется больше времени на подбор.

    {"alg":"RSA1_5",“payload":"A128CBC-HS256"}

    Построение архитектуры отказоустойчивого кластера Keycloak


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

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

    Для работы в режиме Active/Active и Active/Passive кластера требуется обеспечивать консистентность данных в реляционной базе данных — оба узла базы данных должны синхронно реплицироваться между различными геораспределенными ЦОД.

    Самый простой пример отказоустойчивой инсталяции.



    Какие преимущества дает использование единого кластера:

    • Высокая доступность и производительность.
    • Поддержка режимов работы: Active/Active, Active/Passive.
    • Возможность динамического масштабирования — при использовании контейнерной виртуализации.
    • Возможность централизованного управления и мониторинга.
    • Единый подход для идентификации/аутентификации/авторизации пользователей в проектах.
    • Более прозрачное взаимодействие между различными проектами без участия пользователей.
    • Возможность переиспользования JWT токена в различных проектах.
    • Единая точка доверия.
    • Более быстрый запуск проектов с использованием микросервисов/контейнерной виртуализации (не требуется поднятие и настройка дополнительных компонентов).
    • Возможно приобретение коммерческой поддержки от вендора.

    На что стоит обратить внимание при планировании кластера


    СУБД


    Keycloak использует систему управления СУБД для сохранения: realms, clients, users и пр.
    Поддерживается большой спектр СУБД: MS SQL, Oracle, MySQL, PostgreSQL. Keycloak поставляется с собственной встроенной реляционной базой данных. Рекомендуется использование для ненагруженных сред – такие как среды разработки.

    Для работы в режиме Active/Active и Active/Passive кластера требуется обеспечивать консистентность данных в реляционной базе данных и оба узла кластера баз данных синхронно реплицируются между ЦОД.

    Распределенный кеш (Infinspan)


    Для корректной работы кластера требуется дополнительная синхронизация следующих типов кеша с использованием JBoss Data Grid:

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

    Action tokens — используются для сценариев, когда пользователю необходимо подтвердить действие асинхронно (по электронной почте). Например, во время потока forget password кэш actionTokens Infinispan используется для отслеживания метаданных о связанных маркерах действий, которые уже использовались, поэтому его нельзя использовать повторно.

    Caching and invalidation of persistent data – используется для кэширования постоянных данных, чтобы избежать лишних запросов к базе данных. Когда какой-либо сервер Keycloak обновляет данные, все остальные серверы Keycloak во всех центрах обработки данных должны знать об этом.

    Work — используется только для отправки сообщений о недействительности между узлами кластера и центрами обработки данных.

    User sessions — используются для сохранения данных о сеансах пользователя, которые действительны в течение сеанса браузера пользователя. Кэш должен обрабатывать HTTP-запросы от конечного пользователя и приложения.

    Brute force protection — используется для отслеживания данных о неудачных входах.

    Балансировка нагрузки


    Балансировщик нагрузки является единой точкой входа в keycloak и должен поддерживать sticky sessions.

    Сервера приложений


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

    На этом первая часть – теоретическая — закончена. В следующих циклах статей будут разобраны примеры интеграций с различными идентификационными провайдерами и примеры настроек.
    X5 Retail Group
    Все о цифровой трансформации ритейла

    Комментарии 26

      +1
      Очень интересная тема, надеюсь, будут и примеры кода
        +1
        А не было случайно проблемы, когда Кейклок держал кучу соединений в time_wait, как будто чего-то ждет от балансировщиков?
          +1
          Нет такая проблема не возникала, т.к при первичной настройке мы использовали кластеризацию настроек и выделяли опытным путем подобранное количество ядер и памяти, а так же ряд других настроек оптимизированных для нашей инфраструктуры.
          Мы используем два разных решения для балансировки нагрузки — для внутренних пользователей(локальная сеть) и внешних (доступ из публичных сетей). Это специализированные решения.
          Возможно стоит более детально изучить проблемы на предмет тюнинга сетевого стека при использовании haproxy или иных.

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

            0

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


            PS. Слышал, что у X5 высокая доля внедрения IPv6 в интранете, интересно было бы почитать.

          +1
          Используем в комбинации с KrakenD неплохо, но ошибки неудобно смотреть.
            0

            Keycloak — достаточно мощная система, но и достаточно "интересная" в плане интеграции, начиная с отсутствия библиотек под некоторые распространённые языки. А rest api позволяет не всё, например создавая scope'ы, он вам не отдаст их scope_id, и наоборот, получая scope_id, вы не получите название scope'а. Или я просто плохо искал?


            Через что в итоге вы внедряли, у вас композитный доступ по ресурсам или обычный по группам?

              0
              Хм, апи довольно мощный, про scope не смотрел, но по client дёргаю через апи всё, что нам нужно.
              Клиентами управляю так же через апи (точнее, через ансибл модуль).
                0
                Все так, каждый выбираем сам, что ему требуется на основе имеющегося стека технологий
                +1
                Мы стараемся использовать рекомендованные и опробованные библиотеки. Однако сталкивались с рядом трудностей на Angular. Часть кейсов мы решаем самостоятельно или используем преимущества поддержки вендора.
                По поводу scope и scope_id возможно стоит сделать два запроса (но это решение прямое и в лоб – без погружения в задачу)
                У нас используются различные вариации, а так же в ряде случаев для машинного взаимодействия

                  0

                  По моему опыту, использование собственных библиотек KC только усложняет жизнь.
                  Я предпочитаю брать любую другую реализацию OIDC/OAuth2 c поддержкой JWKS. Ну и в конце концов, библиотек по работе с JWT более чем достаточно на любой вкус, а написать специфичный для веб-фреймворка middleware, распаковывающий и валидирующий токены — не запредельно сложно.


                  API у него и вправду довольно жуткий. Меня убило уже то, что из него нельзя получить названия ролей пользователя по id пользователя в один запрос.

                    0
                    Мы сейчас тестируем вариант с использованием gatekeeper. Вообще ничего в коде можно не менять.
                      0

                      Мне настолько не нравится сам KC, что я избегаю решений, которые могут нас к нему сколько-то привязать.


                      Тем не менее, для низкого старта нет ничего лучше.

                        0
                        А чем именно не нравится? Альтернатив, на самом деле, достаточно. Auth0, допустим (у них, кстати, куча отличной документации по oidc и oauth2).
                          0

                          JBoss внутри мне не нравится. У меня абсолютно субъективная непереносимость Java EE и application servers.


                          Как только в плагине KC нужна интеграция с внешней системой чуть теснее HTTP API — начинается Java EE с ее маловменяемой конфигурацией датасорсов и очередей в XML.

                  +1

                  По моим субьективным ощущениям Кийклоак многовато делает. Ведь редко все это нужно в комбинации.
                  Какие альтернативы этому продукту существуют? Мозехт кто-то посоветует?
                  Самая интересная мне фича это раздача OAuth2 токенов (в JWT формате). База пользователей есть уже, к ней надо привязать.

                    0
                    Вы можете брать только тот фукнционал, который вам нужен. Можно просто выдавать токены и всё. Мы, например, целиком отказались от политик и ролей кейклока. Просто добавляем в токен членство в АД группах и по ним делаем авторизацию в приложении уже.
                      0
                      Несомненно, каждый проект или продукт в праве выбирать, что ему необходимо и требуется. Очень распространённый кейс, когда keycloak используется в связке с AD или ADFS и сервисом дообогащения данных из других ИС
                      0
                      Одна из ярких альтернатив при использовании API-GW — связка Gravitee APIM + Gravitee Access Management. В целом можно сказать это комплексное решение.
                      Так же возможна связка APIGEE + Keycloak — для высоконагруженных проектов, где есть монетизация.
                      Опять же каждый решает сам по применимости к своему стеку и всегда есть альтернатива 
                        0

                        Ага смотрю на нее

                        0
                        База пользователей есть уже

                        тут два варианта навскидку


                        • написать user storage provider для KC(довольно несложно пишутся, но IMO неудобно отлаживать)
                        • взять опенсорсный OIDC или OAuth2 провайдер по вкусу и поменять источник пользователей в нем
                          0
                          взять опенсорсный OIDC или OAuth2 провайдер по вкусу

                          Так вот какие советуете то?

                            0

                            Честно говоря — я пока не присматривался всерьез, мы пока плагинами в KC обходимся.


                            Из того, что попадалось на глаза и вызывало некоторый интерес
                            https://github.com/cloudfoundry/uaa
                            https://github.com/mitreid-connect/OpenID-Connect-Java-Spring-Server
                            https://github.com/mbuczko/cerber-oauth2-provider


                            На офсайте OIDC есть целый список реализаций, смотреть те, в которых есть identity provider.
                            https://openid.net/developers/uncertified/

                              0

                              Выглядит все как-то слабо на фоне КС. Хм… видимо КС это немного старый принцип. Все в одном флаконе…
                              Интересным показался вот этот подход. Отдельбые тулы выполняющие свои задачи. https://www.ory.sh/docs/ecosystem/projects


                              Смотрю на них теперь. Так же на КС и амазоновский Cognito.
                              И наверно нам, как новичкам в этой теме, неплохо все иметь из коробки как у КС.

                                0

                                Да, на ORY Hydra я тоже поглядывал, он выглядит как минимум занятно.


                                Но все же для низкого старта, когда нужен полноценный OAuth2/OIDC, лучше KC по-моему ничего нет. Интегрировать его в систему несложно, гибкость настройки более чем достаточная, работает достаточно прилично. За пару лет не припоминаю сбоев, когда проблема была в самом KC.

                                  0

                                  Ок, спасибо.

                        0
                        А RHSSO кто-нибудь вообще пользуется?
                        Там есть какие-то отличия от Keycloak?
                        Или это просто некая стабильная и поддерживаемая версия?
                        Вижу, что последний RHSSO использует keycloak 4.x под капотом, хотя недавно вышел уже 8.0.2

                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                        Самое читаемое