Начальная авторизация
После данных действий вы уже будете иметь Kubernetes-кластер с настроенной OIDC-авторизацией. Единственный момент, что ваши пользователи пока что не имеют настроенного клиента как и собственного kubeconfig. Что бы эту проблему решить нужно настроить автоматическую выдачу kubeconfig пользователям после успешной авторизации.
Описание
Хочу описать логику как с использованием сервиса авторизации Keycloak настроить авторизацию при этом получая token и refreshToken , а так-же обменивать refreshToken на новый token.
Мы такую логику использовали при работе с фронтом. Выставляли срок действия token 15 минут и когда он был просрочен, можно было обновить его при помощи refreshToken.
Введение
Пару лет назад мы уже затрагивали тему безопасности в веб-приложениях. Тогда в рамках исследовательских работ был реализован собственный Service Provider для интеграции с продуктом Shibboleth по протоколу SAML 2.0.
В сегодняшней статье речь снова пойдет о безопасности веб-приложений. Мы сделаем небольшой обзор продукта KeyCloak (доселе оставленного без внимания сообществом Habr).
В качестве практической ценности будет разобран пример, как защитить простое JEE приложение средствами KeyCloak, а также как осуществить взаимодействие между двумя защищенными приложениями.
KeyCloak и PicketLink – два продукта под одной крышей
В настоящее время JBoss ведет разработку двух продуктов в области безопасности веб-приложений: KeyCloak и PicketLink. Вероятно, в ближайшем будущем оба продукта объединят в один, о чем уже давненько ходят разговоры:
Сегодня мы не будем останавливаться на проведении детального сравнения двух продуктов, хотя кое-что на эту тему можно посмотреть перейдя по ссылке: planet.jboss.org/post/what_is_the_difference_between_picketlink_and_keycloak.
Буквально в двух словах хочется отметить, что PicketLink защищает приложения, используя программную модель конфигурации. PicketLink предоставляет набор библиотек, хорошо документированный API и широкий набор примеров, оформленных в виде quick start приложений.
Все это вы найдете на официальном сайте picketlink.org. Придется потратить кое-какое время, чтобы разобраться с API и научиться правильно его использовать. Около года назад у нас был опыт применения PicketLink в качестве idP сервера для построения SSO на протоколе SAML 2.0.
KeyCloak – берем все из коробки…
Keycloak (
1 Как работает аутентификация в KeyCloak?
После этапа настройки приложения и KeyCloak сервера схема авторизации выглядит так:
2 Немного о JWT
JWT (JSON Web Token) — молодой открытый стандарт (
3 Настройка SSO
Получив JWT токен, мы по сути дела уже имеем готовый SSO. Несколько простых конфигурационных шагов и мы сможем использовать защищенные данные другого приложения без необходимости повторной аутентификации.
Практическая часть
Знакомство с KeyCloak будем продолжать в рамках практической части, в ходе которой будут выполнены следующие шаги:
- Сначала создадим простое веб-приложение (video-app), которое отображает простой список видео-объектов.
- Далее установим и настроим KeyCloak.
- Защитим приложение средствами KeyCloak.
- Реализуем REST сервис в виде отдельного приложения (video-rest).
- Защитим REST сервис средствами KeyCloak. Будем использовать bearer токен, для доступа к REST сервису.
- Построим SSO: обновим приложение video-app, чтобы в качестве данных использовались данные из приложения video-rest.
Мы будем строить приложения с нуля простыми итерациями. Уже разработанный код можно взять из публичного репозитория:
2 Родительский модуль – keycloak-demo
Родительский модуль хранит версии общих библиотек, версию JVM.
3 Модуль общих ресурсов (common)
В данном модуле определим общие интерфейсы, которые будут использоваться в остальных модулях/приложениях нашего практического примера.
4 Приложение для отображения списка видео (video-app)
Структура готового приложения будет выглядеть так.
4.3 Сервис для работы с видео объектами – VideoService.java
Сервис построен на коллекции объектов. Для простоты создаем коллекцию прямо в сервисе. Понятно, что в реальном приложении будет промежуточный DAO слой для работы с БД или другим источником данных.
4.4 Сервлет для отображения списка видео
Примечание: Аннотация @WebServlet появилась в спецификации Servlet API 3.0 и позволяет конфигурировать сервлеты прямо в Java коде.
4.9 Сборка и деплой
Собираем проект через maven: mvn clean install.
Деплоим собранное приложение в сервер приложений WildFly. Для этого копируем собранный video-app.war в <WILDFLY_HOME>/standalone/deployments.
5.1 Установка KeyCloak в WildFly
Шаги взяты из инструкции (
5.2 Создаем Realm
Realm – область безопасности, которую мы определяем и настраиваем в KeyCloak. Realm’ы в KeyCloak позволяют создавать несколько различных конфигураций безопасности.
Все настройки будут производится в рамках созданного Realm: как будет проходить аутентификация, какие приложения будут ее использовать, какие пользователи могут проходить аутентификацию, какие атрибуты будут возвращаться и т.д.
Шаги для создания Realm:
6 Защита приложения video-app через KeyCloak
1. Идем в административную панель Keycloak:
6.3 Определяем приложение video-app в KeyCloak
Приложения, которые мы хотим защитить, описываются в секции Clients для выбранного Realm’а. Здесь мы будем описывать как будет проходить аутентификация в данное приложение, куда и какую информацию отправлять приложению после успешной аутентификации и т.д.
- В секции Configure кликаем на пункт Clients.
- Отображается список приложений. В правом верхнем углу нажимаем кнопку Create.
- Определяем свойства клиента. На текущий момент нужно задать ID приложения и URL для возврата после аутентификации.
Поле Valid Redirect URIs нужно для идентификации тех точек приложения куда допускается отправка результата после аутентификации (JWT токеном). Если у вас несколько таких точек, то все их нужно перечислить тут (используя кнопку ” “). Также можно задать маску: /video-app/*. Это дает право KeyCloak делать редирект на любой ресурс, соответствующий данной маске.
6.4 Добавляем слой безопасности в приложение video-app
4.6.4.1 Защищаем Сервлет VideoListServlet
Для этого просто добавляем на класс VideoListServlet аннотации DeclaredRoles, ServletSecurity.
6.5 Сборка и деплой
Снова собираем и деплоим приложение.
7 REST сервис, как источник видео – (video-rest)
Создадим простой REST сервис, который будет возвращать список видео.
Структура приложения будет выглядеть так.
7.2 Имплементация модели — VideoImpl.java
Аналогично имплементации в приложении video-app (с точности до имени пакета).
7.3 Сервис для работы с видео объектами — VideoService.java
Аналогично имплементации в приложении video-app.
7.6 Включаем поддержку CDI – beans.xml
В точности файл из приложения video-app.
7.8 Сборка и деплой
Собираем приложение через maven и деплоим в сервер в WildFly.
8.3 Определяем приложение video-rest в KeyCloak
Определяем video-rest в KeyCloak аналогично пункту «Определяем приложение video-app в KeyCloak» с одним замечанием.
В AccessType выбираем значение bearer-only.
Это означает, что приложение не будет инициировать процесс аутентификации в KeyCloak и ожидает получения пользователя и его атрибутов из JWT токена.
8.4 Добавляем слой безопасности в приложение video-rest
4.8.4.1 Защищаем сервис возврата списка видео объектов – VideoRest.java
Помечаем метод аннотацией @RolesAllowed. Сам класс сервиса помечаем аннотацией @Stateless, чтобы сделать его EJB (аннотации из пакета javax.annotation.security работают только в EJB 3.0 бинах).
В итоге получаем такой сервис:
4.8.4.2 Конфигурируем способ аутентификацииАналогично конфигурации приложения video-app.
4.8.4.3 Интеграция с KeyCloakПовторить те же шаги для приложения video-rest, что и для конфигурации с KeyCloak для приложения video-app.
Обратите внимание, что в файле конфигурации параметр «bearer-only» должен быть установлен в значение «true».
8.5 Сборка и деплой
Собираем и деплоим приложение.
9 SSO – интеграция video-app с video-rest
Изменяем логику сервлета VideoListServlet в приложении video-app для получения данных из REST сервиса. Будем выводить общий список видео, построенный на основе двух источников.
9.1 Модифицируем сервлет VideoListServlet.java
Во фрагменте кода, получаем контекст безопасности, созданный в процессе авторизации средствами KeyCloak.
KeycloakSecurityContext ksc = (KeycloakSecurityContext) req.getAttribute(KeycloakSecurityContext.class.getName());
Далее обращаемся к защищенному ресурсу, используя JWT токен:
List<VideoImpl> list = target.request().header("Authorization", "Bearer " ksc.getTokenString()).get(listGenericType);
9.2 Сборка и деплой
Собираем и деплоим приложение.
Открываем страницу списка видео
Adding client roles to a group
Use the add-roles command to add realm roles and client roles.
Adding client roles to a realm role
Keycloak provides an add-roles command for adding realm roles and client roles.
Adding configuration to an execution
For example:
$ kcadm create "authentication/executions/a3147129-c402-4760-86d9-3f2345e401c7/config" -r examplerealm -b '{"config":{"x509-cert-auth.mapping-source-selection":"Match SubjectDN using regular expression","x509-cert-auth.regular-expression":"(.*?)(?:$)","x509-cert-auth.mapper-selection":"Custom Attribute Mapper","x509-cert-auth.mapper-selection.user-attribute-name":"usercertificate","x509-cert-auth.crl-checking-enabled":"","x509-cert-auth.crldp-checking-enabled":false,"x509-cert-auth.crl-relative-path":"crl.pem","x509-cert-auth.ocsp-checking-enabled":"","x509-cert-auth.ocsp-responder-uri":"","x509-cert-auth.keyusage":"","x509-cert-auth.extendedkeyusage":"","x509-cert-auth.confirmation-page-disallowed":""},"alias":"my_otp_config"}'
Adding realm roles to a composite role
Keycloak provides an add-roles command for adding realm roles and client roles.
Claim information point
A Claim Information Point (CIP) is responsible for resolving claims and pushing these claims to the Keycloak server
in order to provide more information about the access context to policies. They can be defined as a configuration option
to the policy-enforcer in order to resolve claims from different sources, such as:
Claim information provider spi
The Claim Information Provider SPI can be used by developers to support different claim information points in case none of the
built-ins providers are enough to address their requirements.
For example, to implement a new CIP provider you need to implement org.keycloak.adapters.authorization.ClaimInformationPointProviderFactory
and ClaimInformationPointProvider and also provide the file META-INF/services/org.keycloak.adapters.authorization.ClaimInformationPointProviderFactory
in your application`s classpath.
Example of org.keycloak.adapters.authorization.ClaimInformationPointProviderFactory:
Every CIP provider must be associated with a name, as defined above in the MyClaimInformationPointProviderFactory.getName method. The name
will be used to map the configuration from the claim-information-point section in the policy-enforcer configuration to the implementation.
When processing requests, the policy enforcer will call the MyClaimInformationPointProviderFactory.create method in order to obtain an
instance of MyClaimInformationPointProvider. When called, any configuration defined for this particular CIP provider
(via claim-information-point) is passed as a map.
Example of ClaimInformationPointProvider:
Configuration
To enable policy enforcement for your application, add the following property to your keycloak.json file:
Or a little more verbose if you want to manually define the resources being protected:
{
"policy-enforcer": {
"user-managed-access" : {},
"enforcement-mode" : "ENFORCING",
"paths": [
{
"path" : "/someUri/*",
"methods" : [
{
"method": "GET",
"scopes" : ["urn:app.com:scopes:view"]
},
{
"method": "POST",
"scopes" : ["urn:app.com:scopes:create"]
}
]
},
{
"name" : "Some Resource",
"path" : "/usingPattern/{id}",
"methods" : [
{
"method": "DELETE",
"scopes" : ["urn:app.com:scopes:delete"]
}
]
},
{
"path" : "/exactMatch"
},
{
"name" : "Admin Resources",
"path" : "/usingWildCards/*"
}
]
}
}
Here is a description of each configuration option:
policy-enforcer
Specifies the configuration options that define how policies are actually enforced and optionally the paths you want to protect. If not specified, the policy enforcer queries the server
for all resources associated with the resource server being protected. In this case, you need to ensure the resources are properly configured with a URIS property that matches the paths you want to protect.user-managed-access
Specifies that the adapter uses the UMA protocol. If specified, the adapter queries the server for permission tickets and returns them to clients according to the UMA specification. If not specified, the policy enforcer will be able to enforce permissions based on regular access tokens or RPTs. In this case,
before denying access to the resource when the token lacks permission, the policy enforcer will try to obtain permissions directly from the server.enforcement-mode
Specifies how policies are enforced.
on-deny-redirect-to
Defines a URL where a client request is redirected when an “access denied” message is obtained from the server. By default, the adapter responds with a 403 HTTP status code.
path-cache
Defines how the policy enforcer should track associations between paths in your application and resources defined in Keycloak. The cache is needed to avoid
unnecessary requests to a Keycloak server by caching associations between paths and protected resources.lifespan
Defines the time in milliseconds when the entry should be expired. If not provided, default value is 30000. A value equal to 0 can be set to completely disable the cache. A value equal to -1 can be set to disable the expiry of the cache.
max-entries
Defines the limit of entries that should be kept in the cache. If not provided, default value is 1000.
paths
Specifies the paths to protect. This configuration is optional. If not defined, the policy enforcer will discover all paths by fetching the resources you defined to your application in Keycloak, where these resources are defined with
URIS
representing some paths in your application.lazy-load-paths
Specifies how the adapter should fetch the server for resources associated with paths in your application. If true, the policy
enforcer is going to fetch resources on-demand accordingly with the path being requested. This configuration is specially useful
when you don’t want to fetch all resources from the server during deployment (in case you have provided nopaths
) or in case
you have defined only a sub set ofpaths
and want to fetch others on-demand.http-method-as-scope
Specifies how scopes should be mapped to HTTP methods. If set to true, the policy enforcer will use the HTTP method from the current request to
check whether or not access should be granted. When enabled, make sure your resources in Keycloak are associated with scopes representing each HTTP method you are protecting.claim-information-point
Defines a set of one or more global claims that must be resolved and pushed to the Keycloak server in order to make these claims available to policies. See Claim Information Point for more details.
Configuring an openid connect identity provider
Configure the generic OpenID Connect provider the same way you configure the Keycloak OpenID Connect provider, except you set the providerId attribute value to oidc.
Configuring event logging for a realm
Use the update command on the events/config endpoint.
Creating a group
Use the create command on the groups endpoint to create a new group.
For example:
Creating a new realm
Use the create command on the realms endpoint to create a new enabled realm. Set the attributes to realm and enabled.
Keycloak disables realms by default. You can use a realm immediately for authentication by enabling it.
A description for a new object can also be in JSON format.
You can send a JSON document with realm attributes directly from a file or pipe the document to standard input.
For example:
Creating a realm role
Use the roles endpoint to create a realm role.
Creating a subgroup
Find the ID of the parent group by listing groups. Use that ID to construct an endpoint URI, such as groups/GROUP_ID/children.
For example:
Deleting a client role
Use the delete command with the endpoint URI that you used to get a specific client role.
For example:
Deleting a group
Use the delete command with the same endpoint URI that you use to get a specific group.
For example:
Deleting a realm
Run the following command to delete a realm:
Deleting a realm role
Use the delete command with the endpoint URI that you used to get a specific realm role.
For example:
Getting a specific authentication flow
Run the get command on the authentication/flows/FLOW_ID endpoint.
For example:
Getting a specific client role
Use the get-roles command, passing it the clientId attribute (–cclientid option) or ID attribute (–cid option) to identify the client, and pass the role name (–rolename option) or the role ID attribute (–roleid) to identify a specific client role.
For example:
Getting a specific configured identity provider
Use the identity provider’s alias attribute to construct an endpoint URI, such as identity-provider/instances/ALIAS, to get a specific identity provider.
For example:
Getting a specific group
Use the group’s ID to construct an endpoint URI, such as groups/GROUP_ID.
For example:
Getting a specific realm
Append a realm name to a collection URI to get an individual realm.
Javascript integration
The Keycloak Server comes with a JavaScript library you can use to interact with a resource server protected by a policy enforcer.
This library is based on the Keycloak JavaScript adapter, which can be integrated to allow your client to obtain permissions from a Keycloak Server.
You can obtain this library from a running a Keycloak Server instance by including the following script tag in your web page:
Once you do that, you can create a KeycloakAuthorization instance as follows:
The keycloak-authz.js library provides two main features:
Obtain permissions from the server using a permission ticket, if you are accessing a UMA protected resource server.
Obtain permissions from the server by sending the resources and scopes the application wants to access.
Listing assigned, available, and effective client roles for a composite role
Use the get-roles command to list assigned, available, and effective client roles for a composite role.
Listing assigned, available, and effective client roles for a group
Use the get-roles command to list assigned, available, and effective client roles for a group.
Listing assigned, available, and effective realm roles for a composite role
Use the get-roles command to list assigned, available, and effective realm roles for a composite role.
Listing assigned, available, and effective realm roles for a group
Use a dedicated get-roles command to list assigned, available, and effective realm roles for a group.
Listing authentication flows
Run the get command on the authentication/flows endpoint.
For example:
Listing available identity providers
Use the serverinfo endpoint to list available identity providers.
For example:
Listing client roles
Keycloak has a dedicated get-roles command to simplify the listing of realm and client roles. The command is an extension of the get command and behaves the same as the get command but with additional semantics for listing roles.
Use the get-roles command by passing it the clientId (–cclientid) option or the id (–cid) option to identify the client to list client roles.
For example:
Listing configured identity providers
Use the identity-provider/instances endpoint.
For example:
Listing executions for a flow
Run the get command on the authentication/flows/FLOW_ALIAS/executions endpoint.
For example:
Listing existing realms
This command returns a list of all realms.
Listing groups
Use the get command on the groups endpoint to list groups.
For example:
Listing realm roles
Use the get command on the roles endpoint to list existing realm roles.
You can use the get-roles command also.
Listing the realm keys
Use the get operation on the keys endpoint of the target realm.
Moving a group under another group
For example:
Obtaining the current password policy
You can get the current realm configuration by filtering all output except for the passwordPolicy attribute.
For example, display passwordPolicy for demorealm.
Removing a specific configured identity provider
Use the delete command with the same endpoint URI that you use to get a specific configured identity provider to remove a specific configured identity provider.
For example:
Removing client roles from a composite role
Use the remove-roles command to remove client roles from a composite role.
Removing client roles from a group
Use the remove-roles command to remove client roles from a group.
Removing realm roles from a composite role
Keycloak provides a remove-roles command for removing realm roles and client roles.
Turning on all login page options for the realm
Set the attributes that control specific capabilities to true.
For example:
Updating a client role
Use the update command with the endpoint URI that you used to get a specific client role.
For example:
Updating a group
Use the update command with the same endpoint URI that you use to get a specific group.
For example:
Updating a realm role
Use the update command with the endpoint URI you used to get a specific realm role.
For example:
Updating configuration for an execution
For example:
$ kcadm update "authentication/config/dd91611a-d25c-421a-87e2-227c18421833" -r examplerealm -b '{"id":"dd91611a-d25c-421a-87e2-227c18421833","alias":"my_otp_config","config":{"x509-cert-auth.extendedkeyusage":"","x509-cert-auth.mapper-selection.user-attribute-name":"usercertificate","x509-cert-auth.ocsp-responder-uri":"","x509-cert-auth.regular-expression":"(.*?)(?:$)","x509-cert-auth.crl-checking-enabled":"true","x509-cert-auth.confirmation-page-disallowed":"","x509-cert-auth.keyusage":"","x509-cert-auth.mapper-selection":"Custom Attribute Mapper","x509-cert-auth.crl-relative-path":"crl.pem","x509-cert-auth.crldp-checking-enabled":"false","x509-cert-auth.mapping-source-selection":"Match SubjectDN using regular expression","x509-cert-auth.ocsp-checking-enabled":""}}'
Авторизация вызовов сервисов с использованием keycloak
При работе с микросервисной архитектурой иногда возникают требования авторизованных вызовов между сервисами. В случаях, когда инициатором взаимодействия является какой-то внутренний процесс или служба, нам где-то нужно брать токен доступа. В качестве решения данного вопроса мы можем использовать Client Credentials Flow, чтобы получить токен из keycloak (исходный код примера доступен по ссылке).
Для начала создадим нового клиента, под которым будут авторизоваться наши сервисы:
Для возможности авторизации сервиса нам нужно изменить тип доступа (“Access Type”) на “confidential” и включить флаг “Service accounts Enabled”. В остальном конфигурация не отличается от конфигурации по умолчанию:
Если нам необходимо, чтобы у сервисов, авторизованных под данным клиентом, была своя роль, добавим ее в роли:
Далее эту роль необходимо добавить клиенту. На вкладке “Service Account Roles” выбираем необходимую роль – в нашем случае роль “SERVICE”:
Сохраняем client_id и client_secret для дальнейшего использования в сервисах для авторизации:
Запускаем и настраиваем keycloak
Для запуска keycloak на машине разработчика удобно использовать docker-compose. В этом случае мы можем в разное время для разных приложений запускать свой сервис авторизации, тем самым избавляя себя от кучи проблем, связанных с конфигурацией под различные приложения. Ниже приведен один из вариантов конфигурации docker-compose для запуска standalone сервера с базой данных postgres:
docker-compose.yml
После успешного запуска необходимо произвести настройки realm, клиентов, ролей и пользователей.
Произведем некоторые первоначальные настройки. Создадим realm “my_realm”:
После этого создадим клиент “my_client”, через который будем производить авторизацию пользователей (оставим все настройки по-умолчанию):
Используем oauth2 client из spring-security
Использование keycloak адаптера избавляет нас от написания кучи boilerplate кода. Но в то же время наше приложение становится зависимым от реализации. В некоторых случаях не стоит завязываться на какой-то конкретный сервис авторизации, это даст нам больше гибкости в дальнейшей эксплуатации системы.
Одной из ключевых особенностей spring security 5 является поддержка протоколов OAuth2 и OIDC. Мы можем использовать OAuth2 клиент из пакета spring-security для интеграции с сервером keycloak.
Итак, для использования клиента подключим соответствующую библиотеку в зависимости от проекта (исходный код примера). Полный текст pom.xml:
pom.xml
Далее в application.yaml необходимо указать параметры подключения к сервису авторизации:
Как работают права в kubernetes.
Управлять правами пользователя/групп мы можем с помощью RBAC, об этом создано уже кучу статей, не буду подробно на этом останавливаться. Проблема в том что вы можете используя RBAC для того что бы ограничить права пользователя, но Kubernetes не чего не знает о пользователях.
Подготовка
Подробно останавливаться на том как создавать самоподписанные сертификат я не буду, надо создать 2 сертификата, это корневой(Центр сертификации) и wildcard клиентский для домена *.example.org
После того как вы получите/выпишите сертификаты, клиентский надо добавить в Kubernetes, для этого создаем для него secret:
kubectl create secret tls tls-keycloak --cert=example.org.crt --key=example.org.pem
Далее мы будет его использовать для нашего Ingress контроллера
Настройка kubernetes
Настройка Kubernetes для OIDC-авторизации достаточно тривиальна и не является чем-то очень сложным. Все что вам нужно это положить CA-сертификат вашего OIDC-сервера в /etc/kubernetes/pki/oidc-ca.pem и добавить необходимые опции для kube-apiserver.Для этого обновите /etc/kubernetes/manifests/kube-apiserver.yaml на всех ваших мастерах:
Настройка клиента
Надо создать клиента, в понятиях Keycloak это приложение которое будет у него авторизовываться. Важные пункты выделю на скриншоте красным.
Clients –> Create
Создадим scope для групп:
Client Scopes –> Create
И настроим mapper для них:
Client Scopes –> groups –> Mappers –> Create
Добавляем маппинг наших групп в Default Client Scopes:
Clients –> kubernetes –> Client Scopes –> Default Client ScopesВыбираем groups в Available Client Scopes, нажимаем Add selected
Получаем secret(и записываем его куда нить) который мы будем использовать для авторизации в Keycloak:
Clients –> kubernetes –> Credentials –> SecretНа этом настройка окончена, но у меня возникла ошибка когда после успешной авторизации я получал ошибку 403. Баг репорт.
Фикс:
Client Scopes –> roles –> Mappers –> Create
Немного о keycloak
Это реализация SSO (Single sign on) с открытым исходным кодом для управления идентификацией и доступом пользователей.
Основной функционал, поддерживаемый в Keycloak:
Подключаем keycloak при помощи адаптера
В официальной документации к keycloak для использования в приложениях рекомендуют использовать готовые библиотеки – адаптеры, которые дают возможность избавиться от boilerplate кода и излишнего конфигурирования. Есть реализация для большинства популярных языков и фреймворков (supported-platforms). Мы будем использовать Spring Boot Adapter.
Создадим небольшое демонстрационное, приложение на spring-boot (исходники можно найти здесь) и подключим к нему Keycloak Spring Boot адаптер. Конфигурационный файл maven будет выглядеть так:
pom.xml
Для целей проверки добавим контроллер, который будет выставлять методы для различных ролей пользователей и информацию о текущем пользователе (этот же контроллер мы будем использовать в других примерах ниже):
Подключаем приложение как resourceservice
Довольно часто не нужно, чтобы наше приложение инициировало аутентификацию пользователя. Достаточно лишь проверки авторизации пользователя по предоставляемому токену доступа. Вариантом подключения авторизации с keycloak без использования адаптера является настройка приложения как resource server.
В этом случае приложение не может инициировать аутентификацию пользователя, а только авторизует пользователя и проверяет подпись токена доступа. Подключим соответствующие библиотеки: spring-security-oauth2-resource-server и spring-security-oauth2-jose (исходный код). Полный файл pom.xml будет выглядеть так:
pom.xml
Установка gangway
Для удобства можно добавить gangway который будет генерировать файл конфига для kubectl, с помощью которого мы уже под нашим пользователем попадем в Kubernetes.
helm install --name gangway stable/gangway -f values_gangway.yaml
Установка keycloak
Предположим что у вас уже есть LDAP-сервер. Это может быть Active Directory, FreeIPA, OpenLDAP или что-либо еще. Если LDAP-сервера у вас нет, то в принципе вы можете создавать пользователей прямо в интерфейсе Keycloak, либо использовать публичные oidc-провайдеры (Google, Github, Gitlab), результат получится почти такой же.
Первым делом установим сам Keycloak, установка может выполняться отдельно, так и сразу в Kubernetes-кластер, как правило если у вас имеется несколько Kubernetes-кластеров, было бы проще установить его отдельно. С другой стороны вы всегда можете использовать официальный helm-чарт и установить его прямо в ваш кластер.
Для хранения данных Keycloak вам понадобится база данных. По умолчанию используется h2 (все данные хранятся локально), но возможно также использовать postgres, mysql или mariadb.Если вы все же надумали установить Keycloak отдельно, более подробные инструкции вы найдете в официальной документации.
Установка kubernetes dashboard
helm install stable/kubernetes-dashboard --name dashboard -f values_dashboard.yaml
Заключение
Сегодня мы показали каким образом можно использовать KeyCloak для защиты веб-приложений. Большая часть статьи была отведена на имплементацию практического примера, в ходе которого мы затронули основные этапы конфигурации KeyCloak. Надеемся, что материал будет полезен и сэкономит время тем, кто захочет подключить KeyCloak в свои проекты или продолжить дальнейшее исследование этого продукта.
Вообще хочется отметить, что поверхностное использование продукта оставило в целом очень хорошие впечатления. Помимо описанного примера, мы достаточно быстро смогли настроить портал Liferay на использование KeyCloak по протоколу SAML 2.0 (в качестве источника данных по пользователям был использован Microsoft ADFS).
Наши исследования обязательно продолжатся и, возможно, мы еще поделимся интересными результатами по работе с KeyCloak.
Выводы
Подключение keycloak с помощью поставляемого адаптера, конечно, избавляет нас от написания большого количества кода и конфигураций. Но тогда наше приложение будет завязано на конкретную реализацию сервиса авторизации. Подключение же с использованием только возможностей фреймворка spring дает нам больше гибкости в настройке и больше выбора в реализациях.
Но вместе с тем заставляет нас писать больше кода и конфигурации, хотя, на мой взгляд, не настолько уж много. В любом случае, при выборе, как подключать сервис авторизации к своему приложению, мы должны исходить из множества параметров, главным из которых является здравый смысл.
Спасибо за внимание!
UPD: По созданию расширений в keycloak, можно почитать в статье у моего коллеги