Подключение и конфигурирование#

Настройка интеграции с Platform V IAM с использованием проксирования#

При интеграции с использованием прокси-сервера, клиентская часть аутентификации по Open ID Connect(Relay Party) выполняется на стороне IAM Proxy (AUTH) продукта Platform V IAM (IAM).

Для осуществления проксирования на конкретное приложение/backend, необходимо на новом или уже существующем прокси сервиса аутентификации добавить "ответвление". Ответвление добавляется в компоненте PACMAN (CFGA), администраторами сервисов платформы.

При добавлении ответвления, необходимо указать базовый контекст (по базовому контексту определяется на какой backend будет осуществляться проксирование), необходимость вставки в URL базового контекста, адреса и порты серверов приложений и прочее.

После применения изменений в компоненте PACMAN (CFGA) (кнопка с зеленым треугольником в круге), в течение ~5 секунд, изменения должны попасть на прокси-сервера.

При первом переходе из браузера на прокси, пользователю необходимо пройти аутентификацию. Проксирование на сервера приложений, возможно после успешной аутентификации.

После проксирования ко всем запросам в backend добавляются HTTP-заголовки:

iv-user - логин пользователя (обратная совместимость с webseal);

iv-groups - группы пользователя (обратная совместимость с webseal);

Auth-Svc-User - логин пользователя;

Authorization - аутентификационный токен OIDC (id_token) в формате Authorization: Bearer id_token.

Сервер приложений (СП) использует для авторизации Объединенный cервис авторизации (ОСА). При вызове методов Объединенного cервиса авторизации (ОСА) (при получении ticket), необходимо на СП взять из HTTP-заголовка, полученный от прокси jwt-token (id_token или access_token) и использовать его.

Объединенный cервис авторизации (ОСА) умеет самостоятельно валидировать токен от Platform V IAM, на основе которого будет определен пользователь приложения при выдаче авторизационного решения. Также, Объединенный cервис авторизации (ОСА), имеет методы возврата различной информации о пользователе, такой как логин, ФИО, табельный номер, код подразделения и прочее.

В тестовых целях, на стендах тестирования, текущий id_token/access_token/refresh_token доступен для просмотра на endpoint /jwt/ proxy (доступен только на стендах тестирования).

Пример url - https://mycompany-auth-svc-proxy-dev2.mycompany.ru/jwt/

Настройка интеграции с сервисом аутентификации без использования проксирования#

Информация, о настройке интеграции с сервисом аутентификации без использования проксирования, описана в документации компонента Platform V (IAM) KeyCloak.SE.

Использование в Spring Boot#

Для установки Keycloak Spring Boot starter достаточно добавить зависимость: pom.xml

<dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-spring-boot-starter</artifactId>
</dependency>

Добавить использование зависимостей из Keycloak Adapter BOM в pom.xml:

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.keycloak.bom</groupId>
      <artifactId>keycloak-adapter-bom</artifactId>
      <version>4.8.3.Final</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

Spring Boot Adapter Configuration#

В файле application.properties указать настройки адаптера. Актуальные настройки описаны в документации компонента Keycloak.SE (KCSE).

application.properties

keycloak.realm = PlatformAuth
keycloak.auth-server-url = https://platform-devb:8443/auth
keycloak.ssl-required = external
keycloak.resource = demoapp
keycloak.credentials.secret = 11111111-1111-1111-1111-111111111111
keycloak.use-resource-role-mappings = true

Spring Boot Adapter установит login-method в KEYCLOAK и настроит security-constraints при запуске.
Пример конфигурации security-constraints :
application.properties

keycloak.securityConstraints[0].authRoles[0] = admin
keycloak.securityConstraints[0].authRoles[1] = user
keycloak.securityConstraints[0].securityCollections[0].name = insecure stuff
keycloak.securityConstraints[0].securityCollections[0].patterns[0] = /insecure
keycloak.securityConstraints[1].authRoles[0] = admin
keycloak.securityConstraints[1].securityCollections[0].name = admin stuff
keycloak.securityConstraints[1].securityCollections[0].patterns[0] = /admin

Spring Security Adapter#

Установка адаптера#

Для установки адаптера Spring Security достаточно добавить зависимость: pom.xml

<dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-spring-security-adapter</artifactId>
    <version>4.8.3.Final</version>
</dependency>

Конфигурация Spring Security в java#

Реализуем WebSecurityConfigurer (KeycloakWebSecurityConfigurerAdapter). При этом будет использоваться конфигурация из файла keycloak.json, который нужно создать в ресурсном каталоге:

@KeycloakConfiguration
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
    /**
     * Registers the KeycloakAuthenticationProvider with the authentication manager.
     */
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(keycloakAuthenticationProvider());
    }

    /**
     * Defines the session authentication strategy.
     */
    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http
                .authorizeRequests()
                .antMatchers("/customers*").hasRole("USER")
                .antMatchers("/admin*").hasRole("ADMIN")
                .anyRequest().permitAll();
    }
}

При необходимости можно организовать маппинг ролей, org.keycloak.adapters.springsecurity.authentication. KeycloakAuthenticationProvider имеет поддержку маппинга через org.springframework.security.core.authority.mapping. GrantedAuthoritiesMapper.

Конфигурация через XML#

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:security="http://www.springframework.org/schema/security"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/security
       http://www.springframework.org/schema/security/spring-security.xsd">

    <context:component-scan base-package="org.keycloak.adapters.springsecurity" />

    <security:authentication-manager alias="authenticationManager">
        <security:authentication-provider ref="keycloakAuthenticationProvider" />
    </security:authentication-manager>

    <bean id="adapterDeploymentContext" class="org.keycloak.adapters.springsecurity.AdapterDeploymentContextFactoryBean">
        <constructor-arg value="/WEB-INF/keycloak.json" />
    </bean>

    <bean id="keycloakAuthenticationEntryPoint" class="org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationEntryPoint" />
    <bean id="keycloakAuthenticationProvider" class="org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider" />
    <bean id="keycloakPreAuthActionsFilter" class="org.keycloak.adapters.springsecurity.filter.KeycloakPreAuthActionsFilter" />
    <bean id="keycloakAuthenticationProcessingFilter" class="org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter">
        <constructor-arg name="authenticationManager" ref="authenticationManager" />
    </bean>

    <bean id="keycloakLogoutHandler" class="org.keycloak.adapters.springsecurity.authentication.KeycloakLogoutHandler">
        <constructor-arg ref="adapterDeploymentContext" />
    </bean>

    <bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
        <constructor-arg name="logoutSuccessUrl" value="/" />
        <constructor-arg name="handlers">
            <list>
                <ref bean="keycloakLogoutHandler" />
                <bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" />
            </list>
        </constructor-arg>
        <property name="logoutRequestMatcher">
            <bean class="org.springframework.security.web.util.matcher.AntPathRequestMatcher">
                <constructor-arg name="pattern" value="/sso/logout**" />
                <constructor-arg name="httpMethod" value="GET" />
            </bean>
        </property>
    </bean>

    <security:http auto-config="false" entry-point-ref="keycloakAuthenticationEntryPoint">
        <security:custom-filter ref="keycloakPreAuthActionsFilter" before="LOGOUT_FILTER" />
        <security:custom-filter ref="keycloakAuthenticationProcessingFilter" before="FORM_LOGIN_FILTER" />
        <security:intercept-url pattern="/customers**" access="ROLE_USER" />
        <security:intercept-url pattern="/admin**" access="ROLE_ADMIN" />
        <security:custom-filter ref="logoutFilter" position="LOGOUT_FILTER" />
    </security:http>

</beans>

Настройка logout пользователя#

С целью предотвращения несанкционированного доступа к данным конечного пользователя в защищаемом приложении, существуют функции деактивации пользовательской сессии и инвалидации JWT-токена, доступные в IAM Proxy. Данные механизмы являются частью стандарта OpenID Connect, что позволяет использовать их практически с любым провайдером идентификации. При рассмотрении данного раздела следует учесть, что IAM Proxy не используется для защиты в межсервисных взаимодействиях, а предназначен для использования в сценариях работы пользователя с конечными сервисами. Например, с web UI. Для осуществления выхода пользователя (logout) из защищаемого приложения, необходимо на fronend (браузер, мобильное приложение или устройство), вызвать сервис завершения сессии пользователя в IAM Proxy, посредством: GET: /openid-connect-auth/logout. В результате вызова сервиса IAM Proxy вернет сообщение с перенаправлением (redirect), который должен быть корректно обработан в frontend (браузером, мобильным приложением или устройством), для перенаправления пользователя на веб-страницу сервиса завершении сессии пользователя провайдера идентификации. В ходе разработки приложения необходимо учитывать определенные особенности реализации механизма завершения сессии пользователя, накладываемые спецификацией OpenID Connect и текущей реализацией компонента IAM Proxy.

Обратите внимание:

  • URL страницы или сервиса завершения сессии пользователя (или logout), должен быть настраиваемым в вашем приложении, так как конкретные значения могут быть изменены, в зависимости от настроек IAM Proxy;

  • при проектировании приложения и встраивании страницы или сервиса завершения сессии пользователя, необходимо учесть, что используемый протокол OpenID и текущая реализация решения, предполагает работу с компонентом IAM Proxy конечных пользователей (например, физически существующих людей, использующих браузеры или мобильные устройства), а не межсервисные взаимодействия, для аутентификации которых используются другие средства. В связи с этим, необходимо избежать использования программных решений, которые позволяют имитировать или автоматизировать выполнение запросов к сервису завершения пользовательской сессии. Примером, такого нежелательного решения может являться использование AJAX, который позволяет вызывать сервис с помощью JS, но при этом непосредственно браузер не обрабатывает результат вызова, а такие вызовы браузером считаются небезопасными из-за возможности XSS, вследствие чего будут применены политики CORS, что в итоге может не позволить обработать перенаправления на IDP провайдера (потребуется разрешение CORS на IDP). Вызов сервиса завершения пользовательской сессии должен выполняться браузером - это позволит осуществить корректное перенаправление для инвалидации всех, связанных с текущей сессией, объектов.

*Identity provider - СУДИР

Опции logout пользователя#

Доступно переопределение заданных по умолчанию при развертывании опций revoke токенов и выхода на IDP через параметры URI при завершении сессии (logout).

  • Параметр revoke отвечает за включение/выключение revoke токенов при logout;

  • Параметр logout_idp отвечает за logout из IDP.

Пример: /openid-connect-auth/logout?revoke=true&logout_idp=false.

При таких параметрах будет выполнен revoke токенов и logout на IDP выполнен не будет, ограничившись лишь выходом из клиента/АС, соответствующего отозванным токенам.

Описание стандартных опций logout доступно для просмотра в демо-профиле развертывания (oidc_revoke_tokens_on_logout и oidc_disable_logout_in_idp).

Настройка дизайна стартовой страницы IAM Proxy#

IAM Proxy позволяет использовать свой шаблон дизайна стартовой страницы. Для этого необходимо заменить файл jinja2-шаблона стартовой html-страницы template.index_html.jinja2 на свой (предварительно потребуется создать свой файл, на основе стандартного). Пример измененной страницы можно скачать.

Для виртуальной машины (ВМ)#

Замена шаблона стартовой страницы на виртуальной машине с использованием профиля развертывания#

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

    • расположение шаблона стартовой страницы: files/proxy/nginx/rds-client/templates/template.index_html.jinja2;
      ВАЖНО! имя файла шаблона должно соответствовать оригинальному имени файла: template.index_html.jinja2.

    • ресурсы используемые стартовой страницей (опционально) размещаются по пути files/proxy/nginx/html/, например: files/proxy/nginx/html/smartnlp_logo.png.

  2. Применить изменения внесенные в профиль развертывания и запустить Jenkins Job.

Замена шаблона стартовой страницы на виртуальной машине вручную#

Данный способ применим только при использовании RDS Server, стартовая страница при этом генерируется средствами rds-client.

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

  1. Скопируйте на виртуальную машину персонализированный шаблон и перенесите в директорию назначения /opt/iamproxy/rds-client/templates/.

    • Персонализированный шаблон размещен по пути /opt/iamproxy/rds-client/templates/.

  2. Измените владельца и группу владельцев файла персонализированного шаблона на IAM Proxy.

    • Владелец и группа владельцев успешно изменена.

  3. Скопируйте файлы ресурсов (изображения, css и прочие, опционально), используемые персонализированным шаблоном, в директорию /opt/iamproxy/html/.

    • Файлы ресурсов скопированы в директорию /opt/iamproxy/html/.

  4. Измените владельца и группу владельцев на скопированных файлах на IAM Proxy.

    • Владелец и группа владельцев успешно изменена.

  5. Перезапустите службы rds-client (sudo systemctl restart rds-client).

    • Служба rds-client (sudo systemctl restart rds-client) перезапущена.

Для OpenShift#

Замена шаблона стартовой страницы в контейнере (в OpenShift)#

  1. Подготовка ConfigMap. Подготавливаем файл персонализированного шаблона (пример). Для подключения статических бинарных ресурсов (пример), используемых персонализированным шаблоном (опционально), необходимо выполнить конвертацию подключаемого бинарного файла в формат base64 (например командой base64 smartnlp_logo.png). Пример формата: iVBORw0KGgoAAAANSUhE....AAACXBIWXMAABYlAAAWJQFJUiTwAAAA. Полученный формат изображения используем на следующем шаге.

  2. Создание OSE ConfigMap. Используя OSE UI или шаблон OSE ConfigMap по умолчанию, приводим манифест ConfigMap к следующему виду:

       kind: ConfigMap
       apiVersion: v1
       metadata:
           name: iamproxy.custom.template
       data:
           template.index.html.jinja2: | 
               # Содержимое персонализированного шаблона (полученный на предыдущем шаге)
       binaryData:
           smartnlp_logo.png: |
               # бинарный файл изображения в формате base64 (полученный на предыдущем шаге)
  1. Подготовка OSE Deployment IAM Proxy. Подключаем созданный в пункте 2 ConfigMap к контейнеру IAM Proxy в качестве volume, для чего приводим Deployment IAM Proxy к следующему виду:

   ...
   spec:
   ...
     template:
   ...
       spec:
         volumes:
           - name: iamproxy-custom-template-volume
             configMap:
               name: iamproxy.custom.template
               defaultMode: 0400 #Права только на чтение
   ...
         containers:
   ...
             volumeMounts:
               - name: iamproxy-custom-template-volume
                 mountPath: /opt/iamproxy/rds-client/templates/template.index.html.jinja2
                 subPath: template.index.html.jinja2
               - name: iamproxy-custom-template-volume
                 mountPath: /opt/iamproxy/html/smartnlp_logo.png
                 subPath: smartnlp_logo.png
   ...

Использование CORS с IAM Proxy#

Для использования ресурсов IAM Proxy при CORS-запросах, необходимо на ответвлении включить опцию rds-enable-cors.location.conf. Так же, чтобы обеспечить работу cookies (аутентификации) при CORS, необходимо чтобы вызывающая страница и источник размещались на одном родительском домене. Родительский домен необходимо задать как домен для сессионных cookies IAM Proxy (параметр iamproxy.k8s.session_domain при использовании DOT), и для сервиса задать уникальное имя сессионной cookie (параметр iamproxy.k8s.session_name при использовании DOT).

При CORS-запросах предполагается, что аутентификация ранее была сделана пользователем на источнике ресурсов (или для нее не требуется действий от пользователя), и запросы через IAM Proxy будут проходить успешно. Если действующей сессии/аутентификации нет, то будет получен ответ с кодом 401 или перенаправление на аутентификацию в провайдере.

При использовании CORS, также необходимо обеспечить отправку cookies в CORS-запросах. Для вызовов из JS, это XMLHttpRequest с withCredentials="true" или fetch с credentials='include', или аналогичные опции для используемого фреймворка. Для тегов img, audio, link, script, video это атрибут crossorigin="use-credentials".

При необходимости аутентификации в iframe, где потребуются действия от пользователя, от провайдера может потребоваться отдавать заголовок Content-Security-Policy: frame-ancestors 'self' https://example.org, чтобы разрешить отображение контента от провайдера в UI.