Keycloak — больно не будет

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Привет! Меня зовут Алексей, я DevOps-инженер компании Nixys. «Как правильно и своевременно предоставлять и отнимать доступ у различных сотрудников?» — этот вопрос беспокоит всех. Особую важность эта задача приобретает, когда продукт начинает быстро расти — если ваш штат регулярно меняется, вопросы безопасности данных и работоспособности системы должны стоять на особом контроле.

В статье я хочу рассказать, как мы совершенствовали систему контроля доступа в рамках одного проекта, и показать, как реализовали единую точку авторизации через Keycloak.


Глава 1: Нельзя всё всегда контролировать, но иногда можно.

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

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

Не так давно к нам пришёл проект, который уже имел огромную инфраструктуру, поднятую давным-давно другими людьми. Нам же, как инженерам, надо было в ней разобраться и предложить улучшения. Кроме этого, была поставлена задача — усовершенствовать систему контроля доступа: в компании был большой штат, регулярно менялись сотрудники, поэтому потребность в удобной системе распределения привилегий доступа была понятна. Причем привилегии должны были делиться таким образом, что одно и тоже ПО, как пример Nexus, располагаемое в различных изолированных контурах, должно быть доступно определенным сотрудникам. По большей части, стоял вопрос безопасности не только внутри инфраструктуры, но и вопрос распределения зон ответственности за различные разрабатываемые продукты.

Глава 2: Внедрение.

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

К счастью, как говорилось ранее, инфраструктура уже жила без нас и в ней имелась такая замечательная вещь как FreeIPA. Для тех, кто не знает, что это такое, подскажу ниже:

FreeIPA — открытый проект для создания централизованной системы по управлению идентификацией пользователей

Для начала нам нужен аккаунт в FreeIPA, и тут возникает первый вопрос «Что именно нам нужно?» Первый вариант — это обычный пользователь в FreeIPA, которого можно создать либо через консоль, либо через web интерфейс:

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

# ldapmodify -x -W
dn: uid=keycloak,cn=users,cn=compat,dc=test,dc=ru
changetype: add
objectclass: account
objectclass: simplesecurityobject
uid: keycloak
userPassword: supersecurepassword
passwordExpirationTime: 20380119031407Z
nsIdleTimeout: 0

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

Многие продукты с web - интерфейсом на данный момент имеют весьма удобную и красивую интеграцию с ldap. Идеальным и наглядным примером в нашем стеке является Grafana, которая содержит предпосылки для подобной настройки из коробки в ldap.toml:

[[servers]]
host = "10.10.10.3"
port = 636
use_ssl = true
ssl_skip_verify = true
# Search user bind dn
bind_dn = "uid=keycloak-service,cn=users,cn=compat,dc=test,dc=ru"
bind_password = 'supersecurepassword'
search_base_dns = ["cn=users,cn=compat,dc=test,dc=ru"]
search_filter = "(uid=%s)"
group_search_base_dns = ["cn=groups,cn=compat,dc=test,dc=ru"]
group_search_filter_user_attribute = "uid"
group_search_filter = "(&(objectClass=posixGroup)(memberUid=%s))"

[servers.attributes]
member_of = "memberOf"
email =  "mail"
name = "givenName"
surname = "sn"
username = "uid"

[[servers.group_mappings]]
group_dn = "cn=allow_rw_grafana,cn=groups,cn=compat,dc=test,dc=ru"
org_role = "Admin"
grafana_admin = true
org_id = 1

[[servers.group_mappings]]
group_dn = "cn=allow_ro_grafana,cn=groups,cn=compat,dc=test,dc=ru"
org_role = "Viewer"
grafana_admin = false
org_id = 1

Данная же конфигурация говорит нам о следующих моментах.

Для аутентификации пользователей используется FreeIPA через SSL порт 636, для аутентификации необходим пользователь с паролем и доступом до групп allow_rw_grafana, allow_ro_grafana.

В рамках первой нашей группы (allow_rw_grafana) всем пользователям, прошедшим аутентификацию по имени УЗ, будет предоставлена роль Admin, во второй группе будет предоставлена роль Viewer.

— Закрывает ли это наши потребности?

— Да

— Является ли данный процесс удобным для администрирования?

— Да

Всё описанное выше можно найти в документации или написать на основе примеров описанных в рамках ldap.toml конфигурации, но идеальное ли это для нас решение или нет?

Ответ на этот вопрос у каждого свой, так как у каждого решения имеются как плюсы так и минусы, если смотреть с точки зрения системного архитектора, то упрощение системы и сетевого взаимодействия всегда радует глаз. С другой стороны, если мы хотим добиться единого входа в систему (SSO), то этот вариант выглядит принципиально неудачно в нашем случае. Ведь зачем авторизовываться в каждой вебке индивидуально?

Вопрос с Grafana можно считать закрытым, но как быть с теми продуктами, функционал которых не имеет из коробки подобных интеграций c FreeIPA или в них в принципе отсутствует авторизация?

Глава 3: Свой среди чужих, чужой среди своих.

Здесь на помощь приходят нестандартные решения. В нашем случае этим решением является Keycloak.

Keycloak — продукт с открытым кодом для реализации single sign-on с возможностью управления доступом, нацелен на современные применения и сервисы.

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

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

Основные задачи, которые перед нами стоят:

  • Создание процессов авторизации, если они отсутствуют.

  • Гибкость управления при выдаче и отзыве доступов.

  • Распределение по ролям авторизованных пользователей.

  • Использование LDAP в виде Freeipa, как источника информации о пользователях.

Глава 3.1: С чего лучше всего всегда начинать? — с начала.

С чего же лучше всего начать? Разворачиваем наш Keycloak версии 21.1.2 через docker-compose и образ от Bitnami:

version: "3"

services:
  keycloak:
    image: bitnami/keycloak:21.1.2
    container_name: 'keycloak'
    depends_on:
      - postgres
    volumes:
      - ./volumes/cacerts:/opt/bitnami/keycloak/certs-spi/cacerts
      - ./volumes/keycloak:/opt/bitnami/keycloak
    env_file:
      - .env
    ports:
      - "8080:8443"
    restart: always
    networks:
      lan:
        ipv4_address: 172.26.0.11
  postgres:
    image: postgres:13.2
    container_name: 'postgres-keycloak'
    restart: always
    volumes:
      - ./volumes/postgresql/data:/var/lib/postgresql/data
    env_file:
      - .env
    networks:
      lan:
        ipv4_address: 172.26.0.12
networks:
  lan:
    driver: bridge
    ipam:
      config:
        - subnet: 172.26.0.0/24
    driver_opts:
      com.docker.network.bridge.name: "br-${COMPOSE_PROJECT_NAME}"

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

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

  1. Ручное создание в веб интерфейсе.

  2. User Federation - синхронизация пользователей с серверов LDAP и Active Directory.

Глава 3.2: Автоматизируем и синхронизируем.

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

  • Указываем имя нашей федерации.

  • URL для коннекта к ldap, по протоколу ldaps, для шифрования соединения по SSL и повышению безопасности нашего трафика.

  • Указываем использовать ли центр доверенных сертификатов или нет.

Моменты, описанные выше, являются очень важными по ряду причин.Если ваша FreeIPA имеет обыкновенный общедоступный сертификат, то проблем с SSL сертификатом не возникнет, но как быть если ваш сертификат является самоподписанным? Ответ весьма простой, но не явно описанный в документации.

На текущий момент Keycloak не умеет производить включение ldaps с самоподписанным сертификатом — без ручного добавления его в центр хранения ключей и сертификатов keystore.

В виде volume данный файл хранится следующим образом на нашем сервере:

    volumes:
      - ./volumes/cacerts:/opt/bitnami/keycloak/certs-spi/cacerts

Заходим в контейнер и идем в каталог, где у нас есть права на редактирование и создание файлов.

docker exec -ti keycloak bash
cd /opt/bitnami/keycloak/

Выкачиваем новый сертификат в файл и импортируем его в текущий центр хранения Keystore:

openssl s_client -connect freeipa-replica.test.ru:636 2>/dev/null | openssl x509 > ca.crt
keytool -trustcacerts -keystore "/opt/bitnami/keycloak/cacerts" -importcert -alias freeipa-replica.test.ru -file /opt/bitnami/keycloak/ca.crt

На всплывающее подтверждение пишем "Yes". После чего наш новый Keystore готов, и нам необходимо лишь перепрокидывать его в контейнер, чтобы в дальнейшем он применялся на уровне keycloak. Полдела сделано, наш сертификат теперь является доверенным, и можно произвести тестирование коннекта с помощью “Test connection”.

Следующим шагом является описание параметров подключения к нашей FreeIPA.

Указываем ранее созданного пользователя, Common Name и Domain Component, которые можно посмотреть в самом LDAP и пароль для аутентификации, после чего проверяем успешность нашего подключения.

Осталось задать последние параметры синхронизации пользователей LDAP с Keycloak для извлечения всех наших uid юзеров из группы пользователей allow_users.

Здесь в настройках нас интересуют следующие поля:

  • Edit mode — режим работы с LDAP, в нашем случае чтение.

  • Users Distinguished Name — где описывается содержимое атрибутов в дереве

  • Username LDAP attribute — атрибут пользователя, который отображается как имя пользователя.

  • Relative Distinguished Names LDAP attribute — относительные уникальные имена из которых состоит DN.

  • UUID LDAP attribute — имя уникального идентификатора объекта.

  • User object classes — класс объекта.

  • User LDAP filter — фильтр из дерева объектов, в указанном пространстве пользователей.

  • Search scope — глубина поиска.

Все параметры для поиска объектов LDAP можно посмотреть в рамках нашей Freeipa при выводе полного списка пользователей со всеми их метаданными и нам будет достаточно лишь сделать настройку периода синхронизации пользователей из Freeipa.

Если все параметры заданы успешно, то первичная синхронизация произойдет самостоятельно после сохранения настроек и Keycloak уведомит вас о добавлении новых пользователей. Всех пользователей можно также посмотреть в Users, но только при поиске конкретного имени или же при поиске по регулярному выражению “*”.

Глава 3.3: Настройка Realm OpenID Connect для Prometheus.

Когда мы имеем в нашей системе уже всю информацию о ряде пользователей, можно приступать к работам непосредственно с Сlients внутри Realm. Создадим новый клиент с любым Name и Client ID на ваш выбор:

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

И смотрим, что у нас установлены настройки по умолчанию.

  • Authorization — включение или отключение детальной авторизации в Client. Данная настройка необходима для добавление дополнительных параметров настройки авторизации между приложением и Keycloak, где решения об авторизации могут приниматься на основе различных механизмов управления доступом.

  • Authentication flowStandard flow и Direct access grants включают поддержку аутентификации OpenID Connect c Authorization token и предоставляют Client-у доступ до имен и паролей пользователя в Keycloak для последующей выдачи токена авторизации.

  • Root URL — корневой URL в нашем случае обозначен переменной, и в результате это будет домен нашего Keycloak.

  • Home URL — Добавочная часть к основной части для перенаправления внешнего пользователя на нужный Realm.

  • Valid redirect URIs — допустимые URL после авторизации.

  • Valid post logout redirect URIs — допустимые URL после выхода из авторизованной УЗ.

P.S.

В рамках демонстрационной настройки были написан “*”, что означает, что доступные все URL в обоих случаях, но данный вариант решения не является безопасным и в prod- среде рекомендуется уточнять данные значения.

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

Здесь на сцену выходит уже известный обратный прокси сервер oauth2-proxy, который в взаимодействии с провайдером идентификации производит авторизацию пользователя. Подключить его можно следующим образом, добавив в наш ранее описанный docker-compose новый контейнер, который будет настроен на работу с нашим Realm.

  oauth2:                                                                                                                                                                                                          
    image: bitnami/oauth2-proxy:7.4.0                                                                    
    container_name: 'oauth2-proxy-prometheus'                                                                                                                                                                      
    env_file:                                                                                            
      - .env                                                                                                                                                                                                       
    command:                                                                                             
      - '--http-address=0.0.0.0:4180'                                                                                                                                                                              
      - '--provider=keycloak-oidc'                                                                                                                                                                                 
      - '--client-id=test’                                                                                                                                                                                 
      - '--client-secret=e8hhbO7KmCrBUCTzZNFLHYQpmOAumUdl'                                                                                                                                                         
      - '--redirect-url=https://prometheus-dashboard.test.ru'                                                                                                                                                 
      - '--upstream=http://10.10.10.2:9090'                                                                                                                                                                       
      - '--oidc-issuer-url=https://keycloak-dashboard.test.ru/realms/prometheus'                                                                                                                              
      - '--insecure-oidc-skip-issuer-verification=false'                                                                                                                                                           
      - '--email-domain=*'                                                                                                                                                                                         
      - '--cookie-secret=yx_eP3yIaFuj0FqeWVvjT3ycF0Lh1SuENwSPbs5TYB4='                                                                                                                                             
      - '--pass-access-token=true'                                                                                                                                                                                 
      - '--insecure-oidc-allow-unverified-email=true'                                                                                                                                                              
      - '--cookie-secure=true'                                                                                                                                                                                     
      - '--set-authorization-header=true'                                                                                                                                                                          
      - '--pass-authorization-header=true'                                                                                                                                                                         
      - '--reverse-proxy=true'                                                                                                                                                                                     
      - '--skip-provider-button=true'                                                                                                                                                                              
      - '--set-xauthrequest=true'                                                                                                                                                                                  
    ports:                                                                                                                                                                                                         
      - "4180:4180"                                                                                                                                                                                                
    restart: always                                                                                                                                                                                                
    networks:                                                                                                                                                                                                      
      lan:                                                                                                                                                                                                         
        ipv4_address: 172.26.0.10

И здесь есть также пара интересных настроек, на которые хочется с акцентировать внимание.

  • client-id — ранее мы задавали его во время создания Realm, и в нашем случае он “test”

  • redirect-urlURL перенаправления после успешной авторизации

  • upstream — адрес проксирования запросов на наш Prometheus.

  • oidc-issuer-urlURL нашего Realm куда идёт запрос на идентификацию

  • cookie-secret — кука для шифрования токена

  • client-secret — secret для шифрования трафика между значениями

В целом понятно всё, кроме как «Откуда взять client secret?» Ранее при создании Client было выбрано использование параметра Client authentication. Смысл его весьма прост и заключается он в том, что при взаимодействии через внешнюю или внутреннюю сеть между Keycloak и Oauth2-proxy, будет создан шифрованный коннект для передачи токена аутентификации. Извлечь secret можно из настроек самого клиента:

Чтобы обеспечить дополнительную безопасность в приведенном выше сценарии, поставщики OAuth предоставляют утверждение aud, или «Аудитория», в токене носителя, когда пользователь входит в систему. Утверждение aud — это значение, указывающее, для какого приложения предназначен токен носителя. А значит заключительной частью настроек Client является добавление данного сценария  в Client Scopes.

Путь

В итоге, что у нас получается? Всё сетевое взаимодействие запроса от момента первичного запроса площадки можно рассмотреть на следующей схеме:

  1. Первый, неавторизованный запрос пользователя отправляется на Nginx после чего он проксируется в oauth2-proxy и отправляется в Realm Keycloak.

  2. Далее в случае успешной авторизации запрос отправляется вновь на Nginx и проксируется на oauth2-proxy для проверки токена авторизации пользователя.

  3. После этого, в случае успеха, проксирование идёт в желаемый Prometheus dashboard.

Все шаги для авторизации уже у нас преднастроены, и осталось лишь добавить корректную конфигурацию Nginx в наш виртуальный хост для правильного проксирования всех запросов площадки prometheus-dashboard на oauth2-proxy. Остаётся лишь один вопрос, получилось ли у нас закрыть все необходимые потребности описанные нами ранее?

  • Создание процессов авторизации, если они отсутствуют.  - Выполнено, так как исходно из коробки сам Prometheus не имеет авторизацию.

  • Гибкость управления при выдаче и отзыве доступов. - Выполнено

  • Распределение по ролям авторизованных пользователей. - Не выполнено, так как отсутствуют роли в рамках самого Prometheus.

  • Использование LDAP в виде Freeipa, как источника информации о пользователях. - Выполнено.

Три пункта из четырёх — это уже отличный результат в поставленных для нас условиях и так как настройка для Prometheus завершена, по аналогии её можно повторить для Alertmanager.

Но ранее уже стоял вопрос про вариативность и удобство решений, смоделируем ситуацию, что количество сервисов, которое мы хотим подключить в Keycloak, больше 5 штук.

— Как тогда правильно на уровне федерации извлекать пользователей?

Ведь описанный выше вариант является более частным случаем. В нашем случае 5 настроенных клиентов на различное ПО не может контролироваться одной группой пользователей с точки зрения КБ и тогда нам понадобиться сделать следующее. Если одна группа пользователей нас не устраивает, то можно извлечь все группы пользователей и фильтровать разрешенных пользователей не на уровне Keycloak, а на уровне oauth2-proxy.

Для этого необходимо лишь внести пару правок в нашу ранее настроенную федерацию, удаляем фильтры, так как они нам больше не нужны:

И производим добавление нового маппера типа group-ldap-mapper.

Имя выбирается произвольно, настройки DN для извлечения групп как и ранее можем посмотреть в LDAP.

  • Group Name LDAP Attribute — параметр, где описано имя группы объекта по умолчанию cn, но в частных случаях он может отличаться.

  • Group Object Classes — класс объекта групп.

  • Preserve Group Inheritance — включение или выключение распространения группового наследования. То есть в случае, если у вас группа пользователей состоит из других групп пользователей, то вся эта иерархия наследования также перенесётся в Keycloak. Но тут есть важный момент: если ваша структура распределения привилегий имеет рекурсию или же включение нескольких родительских и дочерних групп, то синхронизация будет падать с ошибкой Keycloak. В нашем случае необходимо лишь знать в каких группах состоят пользователи, а не их иерархию, именно поэтому можно произвести выключение данного параметра.

  • Ignore Missing Groups — игнорирование отсутствующих групп.

  • Membership LDAP Attribute — имя атрибута LDAP для сопоставления пользователей с группами.

  • Membership Attribute Type — тип атрибута сопоставления.

Все остальные параметры уже были описаны ранее и логичны по своему описанию за исключением User Groups Retrieve Strategy, на который стоит обратить внимание:

Ведь он отвечает за способ извлечения групп пользователей:

  • LOAD_GROUPS_BY_MEMBER_ATTRIBUTE — группы пользователя будут получены путем отправки запроса LDAP для получения всех групп, где состоит наш пользователь.

  • GET_GROUPS_FROM_USER_MEMBEROF_ATTRIBUTE — извлечение всех групп будет происходить из артибута каждого пользователя.

После синхронизации user federation все данные о пользователях отобразятся в groups и необходимо будет лишь передать их после успешной авторизации пользователя и для этого добавляем ранее описанный ресурс mapper на уровне Clients.

После чего авторизованный запрос пользователя, получаемый oauth2-proxy должен пройти проверку по группе пользователя и в этом может помочь настройка следующего фильтрации групп allowed_groups.

  oauth2:                                                                                                                                                                                                          
    image: bitnami/oauth2-proxy:7.4.0                                                                    
    container_name: 'oauth2-proxy-prometheus'                                                                                                                                                                      
    env_file:                                                                                            
      - .env                                                                                                                                                                                                       
    command:                                                                                             
      - '--http-address=0.0.0.0:4180'                                                                                                                                                                              
      - '--provider=keycloak-oidc'                                                                                                                                                                                 
      - '--client-id=test’                                                                                                                                                                                 
      - '--client-secret=e8hhbO7KmCrBUCTzZNFLHYQpmOAumUdl'                                                                                                                                                         
      - '--redirect-url=https://prometheus-dashboard.test.ru'                                                                                                                                                 
      - '--upstream=http://10.10.10.2:9090'                                                                                                                                                                       
      - '--oidc-issuer-url=https://keycloak-dashboard.test.ru/realms/prometheus'                                                                                                                              
      - '--insecure-oidc-skip-issuer-verification=false'                                                                                                                                                           
      - '--email-domain=*'                                                                                                                                                                                         
      - '--cookie-secret=yx_eP3yIaFuj0FqeWVvjT3ycF0Lh1SuENwSPbs5TYB4='                                                                                                                                             
      - '--pass-access-token=true'                                                                                                                                                                                 
      - '--insecure-oidc-allow-unverified-email=true'                                                                                                                                                              
      - '--cookie-secure=true'                                                                                                                                                                                     
      - '--set-authorization-header=true'                                                                                                                                                                          
      - '--pass-authorization-header=true'                                                                                                                                                                         
      - '--reverse-proxy=true'                                                                                                                                                                                     
      - '--skip-provider-button=true'                                                                                                                                                                              
      - '--set-xauthrequest=true'                            
      - '--allowed_groups=admins,allow_users'                                                                                                                                                         
    ports:                                                                                                                                                                                                         
      - "4180:4180"                                                                                                                                                                                                
    restart: always                                                                                                                                                                                                
    networks:                                                                                                                                                                                                      
      lan:                                                                                                                                                                                                         
        ipv4_address: 172.26.0.10

По итогам описанных выше работ, мы разобрались как можно произвести настройку с OpenID Connect, но нужен ли он нам всегда? Практически в любом ПО можно отключить панель авторизации для пользователей и создать свою панель для аутентификации, но иногда приложение предоставляет настройку авторизации через SAML и данный вопрос можно также закрыть на уровне Keycloak, не усложняя схему сетевого взаимодействия.

Глава 3.4: Настройка Client SAML Keycloak для Ceph.

Этот способ в разы проще, в отличие от описанного выше, и для него не нужны дополнительные ПО помимо самого Keycloak. Причина в том, что SAML уже сам по себе является стандартом обмена данными аутентификации и авторизации между участниками. В нашем случае между провайдером идентификации (Keycloak) и поставщиком сервиса (Ceph).

Так как ранее мы уже настроили User Federation в Realm, можем переиспользовать текущую группу пользователей для авторизации в Ceph и поэтому сразу приступаем к шагу создания Client.

И здесь есть 2 ключевых момента, на которые нам надо обратить внимание. Первый момент — это изменение типа клиента на SAML и второй момент — Client ID. Он должен быть сформированной ссылкой на ваши метаданные Ceph.

Метаданные SAML - это XML-файл, содержащий информацию, необходимую для безопасного взаимодействия между провайдером идентификации и безопасности.

Данный URL всегда стандартен в Ceph и для проверки его корректности, достаточно перейти по нему, после чего вас должна ждать следующая информация:

curl https://ceph-dashboard.test.ru/auth/saml2/metadata
<?xml version="1.0"?>
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"

                     cacheDuration="PT604800S"
                     entityID="https://ceph-dashboard.test.ru/auth/saml2/metadata">
    <md:SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
        <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
                                Location="https://ceph-dashboard.test.ru/auth/saml2/logout" />
        <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</md:NameIDFormat>
        <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
                                     Location="https://ceph-dashboard.test.ru/auth/saml2"
                                     index="1" />
        <md:AttributeConsumingService index="1">
            <md:ServiceName xml:lang="en">Ceph Dashboard</md:ServiceName>
            <md:ServiceDescription xml:lang="en">Ceph Dashboard Service</md:ServiceDescription>
            <md:RequestedAttribute Name="username" isRequired="true" />
        </md:AttributeConsumingService>
    </md:SPSSODescriptor>

После успешного создания редактируем настройки по умолчанию нашего текущего клиента.

  1. Производим отключение Client signature required.

    Данная настройка нужна для повышения безопасности при отправке и получении SAML-запросов. В случае, если вы хотите выполнить данный пункт, после включения будет сгенерирован сертификат и приватный ключ, который также необходимо будет импортировать в Ceph.

  2. Добавляем Mapper категории Hardcoded attribute.

Делать то, что написано — это хорошо, но зачем? 

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

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

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

Другим вариантом, конечно же, может являться задание данного атрибута на уровне User Federation и этот вариант также приемлем и выбор, по большей части, зависит от вашей задачи.

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

ceph dashboard sso setup saml2 https://ceph-dashboard.test.ru https://keycloak-dashboard.test.ru/realms/${REALM_NAME}/protocol/saml/descriptor username

Чтобы убедиться, что все SAML-данные корректно импортировались из Keycloak в Ceph, можно использовать следующую команду:

ceph dashboard sso show saml2

Также рекомендую проверить, что SSO действительно включено и можно тестировать процесс авторизации.

ceph dashboard sso status

Но что получается в результате?

  • Создание процессов авторизации, если они отсутствуют. - Выполнено.

  • Гибкость управления при выдаче и отзыве доступов. - Выполнено.

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

  • Использование LDAP в виде Freeipa, как источника информации о пользователях. - Выполнено.

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

Глава 4: А получилось ли?

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

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

Если есть вопросы — с радостью отвечу в комментариях. Также приглашаю вас подписаться на наш блог Хабр, TG-канал DevOps FM, интернет-издание VC и познакомиться с YouTube — мы всегда рады новым друзьям!

Источник: https://habr.com/ru/companies/nixys/articles/752994/


Интересные статьи

Интересные статьи

Для того, чтобы с помощью цифр получить информацию о происходящих на футбольном поле событиях, предлагаю оценить как влияет тот или иной показатель на результат матча, а потом выяснить по каким из пок...
В ближайшем будущем вы можете отправиться, например, в аэропорт, на стадион, в ресторан, открыть приложение или предъявить карточку, после чего беспрепятственно попасть в...
Toyota — мировой лидер автомобилестроения, один из самых дорогих автомобильных брендов и синоним слова «качество». Toyota известна своей сложной производственной системой, благодаря которой она с...
Под катом можно увидеть, как прошла первая встреча сообщества, а пока — важный анонс для тех, кто живет в Нижнем или будет там 21 декабря (это суббота). Приходи на новые посиделки: Дмитри...
Здесь не будет очередной статьи, описывающей возможности межсетевого экрана уровня приложений. Таких уже полно. Сегодня мы будем объяснять подводные камни при работе с этим решением, чтобы вы з...