Привет всем! В предыдущей статье мы подробно рассмотрели реализацию непрерывной доставки CD в платформе Gitorion на базе Jenkins. В данной статье мы подробно рассмотрим тонкости внедрения системы единого входа Single Sign-On (SSO) во все сервисы платформы Gitorion при помощи Keycloak.
Обоснование необходимости внедрения
Платформа Gitorion состоит из сервисов, реализующих CI/CD, мониторинг и управление базами данных:
Gitea/Forgejo - легковесный хостинг кода и система контроля версий на базе Git;
Jenkins - непрерывная доставка CD;
Grafana - мониторинг и визуализация метрик Prometheus;
phpMyAdmin - управление базой данных MySQL;
pgAdmin - управление базой данных PostgreSQL.
Итого, базовый набор из 5-ти сервисов, каждый из которых имеет собственную систему аутентификации и базу данных пользователей. При небольшом числе участников проекта можно создавать логины пользователей в каждом из пяти сервисов отдельно. При большом количестве пользователей, задача станет невыполнимой. Требуется единая система аутентификации и база данных пользователей. Приняли решение реализовать единый вход Single Sign-On (SSO) при помощи Keycloak.
Принцип работы Keycloak
Большинство современных приложений оснащены клиентами (адаптерами) Keycloak, позволяющими подключаться к серверу Keycloak. На странице входа в приложение располагается кнопка "Подключиться с помощью Keycloak". Пользователь нажимает на эту кнопку, адаптер подключается к Keycloak и перенаправляет пользователя на окно аутентификации Keycloak. Пользователь вводит свой логин и пароль. Keycloak проверяет логин и пароль пользователя и перенаправляет уже аутентифицированного пользователя обратно в личный кабинет сервиса, из которого пришел пользователь.
Область (Realm)
Адаптеры разных сервисов (в нашем случае все пять сервисов платформы Gitorion) объединяются в одну область (Realm). Залогинившись в Web-интерфейсе любого из сервисов, подключенного в область Realm, пользователь получает от Keycloak токен, который записывается в Cookie браузера. В Web-интерфейсы всех остальных сервисов, чьи адаптеры подключены в ту же область, пользователь ходит токеном, без необходимости повторно вводить логин и пароль. Так и реализуется механизм бесшовной аутентификации и единого входа SSO.
Залогиньтесь в консоль администратора Keycloak Administration Console.
![Главное окно Keycloak Главное окно Keycloak](https://habrastorage.org/getpro/habr/upload_files/81c/071/303/81c071303f6338149e7a7f19f2756c14.png)
В селекторе выбора областей вверху слева нажмите кнопку "Create realm".
![Список областей в Keycloak Список областей в Keycloak](https://habrastorage.org/getpro/habr/upload_files/8de/373/69f/8de37369f94a12343f60df4225e01fd7.png)
В минимуме достаточно задать только имя области "Realm name" и нажать кнопку Create.
![Создайте область Realm в Keycloak Создайте область Realm в Keycloak](https://habrastorage.org/getpro/habr/upload_files/b60/353/8ea/b603538eaee6016d3e099087be6d4197.png)
Подключаем клиент (адаптер) приложения к Keycloak
Адаптерами оснащены из коробки 4 сервиса из 5ти - Gitea/Forgejo, Jenkins, Grafana и pgAdmin. Ниже мы рассмотрим для примера подключение адаптера Gitea/Forgejo к Keycloak. Адаптеры остальных сервисов хорошо документированы и подключаются аналогично. phpMyAdmin не имеет под капотом адаптера Keycloak. Подключение приложений, не имеющих адаптера Keycloak или вообще не защищенных окном аутентификации, мы рассмотрим ниже.
Выберите созданную выше область, перейдите в меню Clients и нажмите кнопку "Create client".
![Создайте клиент Gitea/Forgeo в Keycloak Создайте клиент Gitea/Forgeo в Keycloak](https://habrastorage.org/getpro/habr/upload_files/da7/f07/873/da7f07873884af9383834b24a2407879.png)
Задайте "Client ID" – это уникальный идентификатор клиента (адаптера). Нажмите кнопку Next.
![Параметры клиента (адаптера) Gitea/Forgejo Параметры клиента (адаптера) Gitea/Forgejo](https://habrastorage.org/getpro/habr/upload_files/562/138/767/5621387673aa66b39949ec329a643a23.png)
В следующем окне включите "Client authentication" и нажмите кнопку Next.
![Включите аутентификацию в настройках клиента (адаптера) Keycloak Включите аутентификацию в настройках клиента (адаптера) Keycloak](https://habrastorage.org/getpro/habr/upload_files/a1b/b83/c0d/a1bb83c0d9c2ec1d1176171fe2b6a0e5.png)
В "Root URL" задайте URL приложения, на который пользователь будет перенаправлен в случае удачной аутентификации в Keycloak. Нажмите кнопку Save.
![Задайте URL приложения для обратного перенаправления из Keycloak Задайте URL приложения для обратного перенаправления из Keycloak](https://habrastorage.org/getpro/habr/upload_files/830/16b/6cb/83016b6cbe8ca0dc22511dd5fee47cfb.png)
После того, как будет создан клиент, перейдите на вкладу Credentials и скопируйте "Client Secret", который потребуется ниже при настройке Gitea/Forgejo. Данным ключом клиент Gitea/Forgejo будет подтверждать свою подлинность при подключении к Keycloak.
![Ключ доступа клиента (адаптера) Gitea/Forgejo к Keycloak Ключ доступа клиента (адаптера) Gitea/Forgejo к Keycloak](https://habrastorage.org/getpro/habr/upload_files/a18/302/f8d/a18302f8d20d49f81a02365cc200492d.png)
Переходим к настройке Gitea/Forgejo. Залогиньтесь в Gitea/Forgejo пользователем с административными правами и перейдите в панель управления.
![Меню администратора в Gitea/Forgejo Меню администратора в Gitea/Forgejo](https://habrastorage.org/getpro/habr/upload_files/c7e/b6b/67b/c7eb6b67b23e5ca7fd77d446fa63b0b2.png)
Перейдите в "Identity & Access" подменю Аутентификация и нажмите кнопку "Добавить новый источник".
![Добавьте новый источник аутентификации в Gitea/Forgejo Добавьте новый источник аутентификации в Gitea/Forgejo](https://habrastorage.org/getpro/habr/upload_files/4f2/6e7/b03/4f26e7b03720ba44357987939900e2ec.png)
В поле "ID клиента (ключ)" задайте значение, которое ввели выше при настройке клиента в Keycloak в поле "Client ID". В поле "Клиентский ключ" задайте значение "Client Secret", скопированное выше из вкладки Credentials настроек клиента в Keycloak. В поле "URL иконки" задайте пиктограмму, которая будет отображаться на окне аутентификации Gitea/Forgejo напротив кнопки "Войти с помощью keycloak".
![Параметры источника аутентификации Keycloak в Gitea/Forgejo Параметры источника аутентификации Keycloak в Gitea/Forgejo](https://habrastorage.org/getpro/habr/upload_files/5ef/c6a/6d5/5efc6a6d566f6ea4e2599366b705c403.png)
"Open ID Connect URL для автоматизации входа" возьмите в меню настроек "Realm Settings", скопировав ссылку "Open ID Endpoint Configuration" в Keycloak.
![Ссылка на параметры Endpoint Keycloak Ссылка на параметры Endpoint Keycloak](https://habrastorage.org/getpro/habr/upload_files/8cb/3bd/d72/8cb3bdd7242a301e3c6726308c3bd201.png)
Нажмите кнопку "Добавить новый источник" в Gitea/Forgejo и будет создан новый источник аутентификации, привязанный к Keycloak. На странице аутентификации Gitea/Forgejo появится кнопка "Войти с помощью keycloak".
![Кнопка входа через Keycloak на интерфейсе Gitea/Forgejo Кнопка входа через Keycloak на интерфейсе Gitea/Forgejo](https://habrastorage.org/getpro/habr/upload_files/927/3ed/6f8/9273ed6f8101c761ca9d09951687333d.png)
Пользователь нажимает на кнопку "Войти с помощью keycloak", и его перенаправляет на окно аутентификации Keycloak.
![Окно аутентификации в Keycloak Окно аутентификации в Keycloak](https://habrastorage.org/getpro/habr/upload_files/4a5/87f/763/4a587f7630255d5a342a67e4884ba945.png)
Пользователь вводит логин и пароль, и в случае удачной аутентификации Keycloak перенаправляет пользователя в его личный кабинет в Gitea/Forgejo.
![Личный кабинет пользователя в Gitea/Forgejo Личный кабинет пользователя в Gitea/Forgejo](https://habrastorage.org/getpro/habr/upload_files/9e8/891/013/9e889101305f9051609b2ffe4e43bba0.png)
А в Keycloak появится сессия пользователя.
![Активная сессия пользователя в Keycloak Активная сессия пользователя в Keycloak](https://habrastorage.org/getpro/habr/upload_files/8a1/753/6c3/8a17536c3d86ceaefac698ba3f0d263e.png)
База данных логинов и групп пользователей
Keycloak может как самостоятельно хранить в собственной базе данных логины, пароли и прочую информацию о пользователях, так и выступать в качестве провайдера к внешним источникам аутентификации, вроде Microsoft Active Directory, LDAP и т.д. В нашем случае внешний источник аутентификации не потребовался, и мы создали логины и группы пользователей прямо в Keycloak.
Зайдите в консоль администратора Keycloak, выберите область и перейдите в меню Groups. В нашем случае мы разделили всех пользователей на две группы:
admins - пользователи с административными правами во всех сервисах (для тимлидов);
devs - пользователи с правами только на чтение (для разработчиков).
![Группы пользователей в Keycloak Группы пользователей в Keycloak](https://habrastorage.org/getpro/habr/upload_files/c92/949/680/c9294968067fddffeb1ac2a90c1c343b.png)
Нажмите кнопку "Create group" и введите имя группы.
![Создайте группу пользователей в Keycloak Создайте группу пользователей в Keycloak](https://habrastorage.org/getpro/habr/upload_files/c43/14e/a98/c4314ea98e2157d92ca43ce0df46c02e.png)
Нажмите кнопку Create. Повторите те же действия и создайте группу devs.
Далее, для примера создадим пользователя owneruser и добавим его в группу admins. А также пользователя user1 и добавим его в группу devs. Перейдите в меню Users и нажмите кнопку "Add users".
![Пользователи в Keycloak Пользователи в Keycloak](https://habrastorage.org/getpro/habr/upload_files/b0f/663/67a/b0f66367a121462a409c33e4c082310a.png)
Введите данные пользователя. Обязательно задайте e-mail (Grafana использует e-mail пользователя вместо логина для аутентификации).
![Параметры пользователя Параметры пользователя](https://habrastorage.org/getpro/habr/upload_files/b13/d97/189/b13d971899f6ff04a61684e88377e061.png)
Нажмите кнопку "Join Groups", добавьте пользователя в группу admins и нажмите кнопку Join.
![Добавьте пользователя в группу Добавьте пользователя в группу](https://habrastorage.org/getpro/habr/upload_files/3d9/5c3/1ad/3d95c31ad3b9db74384589f125a83fd1.png)
Нажмите кнопку Create, чтобы создать пользователя. Аналогично создайте пользователя user1 и добавьте его в группу devs.
Авторизация на базе ролей Roles
Авторизация и разграничение полномочий потребовалась в Jenkins и Grafana. Тимлидам нужно было предоставить полный доступ, чтобы они могли создавать пайплайны в Jenkins и дашборды в Grafana. А разработчикам дать права только на чтение, чтобы они могли наблюдать за своими пайплайнами и мониторить нагрузку по дашбордам в Grafana.
Залогиньтесь в консоль администратора Keycloak, перейдите в меню "Realm roles".
![Список ролей Roles области Список ролей Roles области](https://habrastorage.org/getpro/habr/upload_files/a52/370/114/a52370114b96f6d0d4210bf8b053baa7.png)
Нажмите кнопку "Create role". В следующем окне задайте имя роли и нажмите кнопку Save.
![Параметры создаваемой роли Параметры создаваемой роли](https://habrastorage.org/getpro/habr/upload_files/37c/d7b/d5e/37cd7bd5e47572fb207246e48e8aa84f.png)
Перейдите в меню Groups, выберите группу admins и перейдите на вкладку "Role mapping".
![Вкладка ролей группы Вкладка ролей группы](https://habrastorage.org/getpro/habr/upload_files/26d/173/ff8/26d173ff883e6eb2d5f66d11606705b2.png)
Нажмите кнопку "Assign role", выберите роль admins и нажмите кнопку Assign.
![Ассоциируйте роль с группой Ассоциируйте роль с группой](https://habrastorage.org/getpro/habr/upload_files/a79/a57/724/a79a57724d66a1fc2d10def11340e732.png)
Аналогично ассоциируйте группу devs с ролью devs. Теперь в случае удачной авторизации Keycloak среди прочей информации о пользователе передаст еще и роль пользователя в сервис, из которого пришел аутентифицироваться пользователь. И сервис сможет выполнить авторизацию пользователя и назначить полномочия в соответствии с его ролью. Информацию о клиенте Keycloak передает в Scopes, список которых можно посмотреть в меню "Client scopes".
![Список Scopes области Список Scopes области](https://habrastorage.org/getpro/habr/upload_files/67d/2e6/c1d/67d2e6c1d14de856aabf328f12188c5a.png)
Среди прочих Scopes по умолчанию есть roles.
Настройте разграничение полномочий на основе ролей в Jenkins. Перейдите в настройках Jenkins в раздел "Manage and Assign Roles" и выберите подпункт "Maname Roles". В появившемся окне в поле "Role to add" введите имя роли admins и нажмите кнопку Add. Аналогично добавьте роль devs. В матрице доступа установите для роли admins галочку Administer в столбце Полные, а для роли devs галочки Read в столбцах Полные и Задача.
![Настройка ролей в Jenkins Настройка ролей в Jenkins](https://habrastorage.org/getpro/habr/upload_files/c86/46d/c03/c8646dc03e48d7e0fe70fe69579bf663.png)
Залогиньтесь в Jenkins пользователем owneruser из группы admins, и вам будет доступен пункт добавления пайплайнов "+ Создать Item" и пункт настроек "Настроить Jenkins".
![Личный кабинет пользователя с административными правами в Jenkins Личный кабинет пользователя с административными правами в Jenkins](https://habrastorage.org/getpro/habr/upload_files/3bd/bcd/e8f/3bdbcde8f322249918024fca45ef213b.png)
Залогиньтесь пользователем user1, и пункты создания пайплайнов и настроек Jenkins исчезнут. Останется возможность только посмотреть информацию о пайплайнах.
![Личный кабинет пользователя с правами только на чтение в Jenkins Личный кабинет пользователя с правами только на чтение в Jenkins](https://habrastorage.org/getpro/habr/upload_files/766/5a1/266/7665a126668462cd2e4112d1eead93f7.png)
Для Grafana нужно в файле конфигурации grafana.ini ассоциировать роли Keycloak со встроенными ролями Grafana.
role_attribute_path: contains(realm_access.roles[], 'admins') && 'Admin' || contains(realm_access.roles[], 'editor') && 'Editor' || 'Viewer'
Роль admins из Keycloak ассоциируйте с ролью Admin из Grafana. Все остальные роли ассоциируются с ролью Viewer в Grafana.
Залогиньтесь в Grafana через Keycloak пользователем owneruser из группы admins, и вы получите полный доступ к настройкам Grafana.
![Личный кабинет пользователя с административными правами в Grafana Личный кабинет пользователя с административными правами в Grafana](https://habrastorage.org/getpro/habr/upload_files/1b4/b18/ec2/1b4b18ec23d0683efa287b3e5aa3ac9e.png)
У пользователя user1 из группы devs права только на чтение, а пункт Administration в меню отсутствует.
![Личный кабинет пользователя с правами только на чтение в Grafana Личный кабинет пользователя с правами только на чтение в Grafana](https://habrastorage.org/getpro/habr/upload_files/be0/604/5a4/be06045a4e3954c0a2d23a3a898e052d.png)
Аутентификация приложений без адаптера
Некоторые приложения не имеют под капотом адаптера Keycloak, а порой и вообще не защищены окном аутентификации. Закрыть такие приложения логином и паролем и настроить для них аутентификацию через Keycloak можно с помощью модуля mod_auth_openidc в составе apache2. В этом случае с Keycloak взаимодействует сервер apache2 и реализует аутентификацию для Location, в котором и нужно расположить приложение, требующее аутентификации. Таким приложением в нашем случае оказался phpMyAdmin, который имеет собственную аутентификацию, но не имеет клиента Keycloak. Разработчики предлагают реализовать SSO самостоятельно, используя данный скрипт.
В конфигурационном файле config.inc.php для phpMyAdmin переключите стандартную аутентификацию с помощью логина и пароля на аутентификацию с помощью скрипта signon.php
$cfg['Servers'][$i]['auth_type'] = 'signon';
$cfg['Servers'][$i]['SignonSession'] = 'SignonSession';
$cfg['Servers'][$i]['SignonURL'] = 'examples/signon.php';
И настройте Location для phpMyAdmin в apache2
LoadModule auth_openidc_module modules/mod_auth_openidc.so
ServerName gitrion.ru
<VirtualHost *:80>
ServerName phpmyadmin
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html/
DirectoryIndex examples/signon.php
OIDCProviderMetadataURL https://auth.gitorion.ru/realms/gitrion/.well-known/openid-configuration
OIDCRedirectURI /redirect_uri
OIDCDefaultURL https://phpmyadmin.staging.gitorion.ru:443/
OIDCCryptoPassphrase 0123456789
OIDCClientID phpmyadmin
OIDCClientSecret 2jb0IUaoAfYUxPpAuJSwtcF2EEMEMdOb
OIDCRemoteUserClaim preferred_username
OIDCXForwardedHeaders X-Forwarded-Host
OIDCXForwardedHeaders X-Forwarded-Port
OIDCXForwardedHeaders X-Forwarded-Proto
<Location />
FallbackResource index.php
DirectoryIndex index.php
AuthType openid-connect
Require valid-user
</Location>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
При переходе на Web-интерфейс phpMyAdmin пользователь перенаправляется на окно аутентификации Keycloak. В случае удачной аутентификации Keycloak перенаправит пользователя в личный кабинет phpMyAdmin.
![Личный кабинет пользователя в phpMyAdmin Личный кабинет пользователя в phpMyAdmin](https://habrastorage.org/getpro/habr/upload_files/59f/d61/ea3/59fd61ea3bec7d9e0ef09bfd0e8ef91e.png)
Browser Flow и ограничение доступа в пределах области на базе ролей Roles
Адаптеры Web-интерфейсов Gitea/Forgejo, Jenkins, Grafana и Web-интерфейсов баз данных phpMyAdmin и pgAdmin для контуров development, staging и production находятся в одной области. Разработчик, залогинившись в любом из Web-интерфейсов платформы, получит доступ к Web-интерфейсам управления базами данных в контуре development, а так же в контурах staging и production. Что недопустимо! Закрыть доступ разработчикам к Web-интерфейсам баз данных в контурах staging и production удалось, добавив в Browser Flow для клиентов (адаптеров) phpMyAdmin и pgAdmin проверку доступа на базе ролей Roles. Browser Flow задает порядок проверки параметров доступа клиента. По умолчанию, сначала проверяется наличие токена в Cookei. Если пользователь ранее залогинился и у него есть токен, то пользователю предоставляется доступ к сервису. Если нет, то предлагается ввести логин и пароль. Мы создали кастомный Browser Flow на базе дефолтного, который после проверки токена в Cookie и логина с паролем, проверяет еще и роль Role пользователя и запрещает доступ пользователям с ролью devs к Web-интерфейсам баз данных в контурах staging и production.
Залогиньтесь в административную консоль Keycloak, выберите область, перейдите в меню Configure, подменю Authentication, нажмите пиктограмму с тремя точками справа от Flow с именем browser и нажмите кнопку Duplicate.
![Список Flows области Список Flows области](https://habrastorage.org/getpro/habr/upload_files/984/1e1/ff3/9841e1ff3ed13d3ec71c32220037ab59.png)
Задайте имя кастомного Flow и нажмите кнопку Dupliacate.
![Продублируйте browser Flow Продублируйте browser Flow](https://habrastorage.org/getpro/habr/upload_files/a52/0f4/9aa/a520f49aa1e00b8486fbbd3298f125e2.png)
Перейдите в Flow browser-custom и добавьте Sub-flow с именем RBAC в browser-custom forms.
![Добавьте Sub-flow RBAC проверки доступа на базе ролей Roles Добавьте Sub-flow RBAC проверки доступа на базе ролей Roles](https://habrastorage.org/getpro/habr/upload_files/79f/071/d8f/79f071d8f82f64c17d95ceb05ef3c01e.png)
В Sub-flow RBAC добавьте Condition user-role.
![Добавьте Condition user role Добавьте Condition user role](https://habrastorage.org/getpro/habr/upload_files/ec9/6db/f4b/ec96dbf4b430ad385f9f1f7ae301460d.png)
Нажмите на шестеренку справа от Condition user-role, задайте Alias и свяжите с ролью devs.
![Свяжите Condition user role с ролью devs Свяжите Condition user role с ролью devs](https://habrastorage.org/getpro/habr/upload_files/10f/571/ea8/10f571ea81921eeaf049cf1a153e4834.png)
Добавьте Step Deny Access
![Step Deny Access Step Deny Access](https://habrastorage.org/getpro/habr/upload_files/4fb/917/feb/4fb917feb6f62a19ec0ee3b63a72a0d5.png)
Нажмите шестеренку справа от Step Deny Access, задайте Alias и текст сообщения, которое будет выдано пользователю из группы devs при попытке доступа к базам данных в контурах staging и production.
![Сообщение об ошибке Сообщение об ошибке](https://habrastorage.org/getpro/habr/upload_files/c8a/3bc/9f7/c8a3bc9f7e86a28efa9d6e1cad5788d7.png)
Подключите кастомный Flow browser-custom к клиентам (адаптерам) phpMyAdmin и pgAdmin в контурах staging и production. Перейдите в настройках клиента на вкладку Advanced, спуститесь в самый низ и в поле Browser Flow задайте browser-custom.
![Задайте кастомный Browser Flow Задайте кастомный Browser Flow](https://habrastorage.org/getpro/habr/upload_files/b48/e04/2c9/b48e042c9b6a49f009c046080e480be2.png)
При попытке доступа пользователя с ролью devs к Web-интерфейсу базы данных в контурах staging или production появится предупреждение.
![Сообщение об ошибке доступа Сообщение об ошибке доступа](https://habrastorage.org/getpro/habr/upload_files/9b0/881/e1e/9b0881e1ec534ba9d84064cd8ea6ff24.png)
Заключение
В данной статье мы осветили свой опыт реализации единого входа Single Sign-On (SSO) во все сервисы платформы Gitorion при помощи Keycloak. Будем рады обратной связи, замечаниям и конструктивной критике. Спасибо.