CI/CD Kubernetes платформа Gitorion. Единый вход Single Sign-On (SSO) во все сервисы платформы при помощи Keycloak
Привет всем! В предыдущей статье мы подробно рассмотрели реализацию непрерывной доставки 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
В селекторе выбора областей вверху слева нажмите кнопку «Create realm».
Список областей в Keycloak
В минимуме достаточно задать только имя области «Realm name» и нажать кнопку Create.
Создайте область Realm в Keycloak
Подключаем клиент (адаптер) приложения к Keycloak
Адаптерами оснащены из коробки 4 сервиса из 5ти — Gitea/Forgejo, Jenkins, Grafana и pgAdmin. Ниже мы рассмотрим для примера подключение адаптера Gitea/Forgejo к Keycloak. Адаптеры остальных сервисов хорошо документированы и подключаются аналогично. phpMyAdmin не имеет под капотом адаптера Keycloak. Подключение приложений, не имеющих адаптера Keycloak или вообще не защищенных окном аутентификации, мы рассмотрим ниже.
Выберите созданную выше область, перейдите в меню Clients и нажмите кнопку «Create client».
Создайте клиент Gitea/Forgeo в Keycloak
Задайте «Client ID» — это уникальный идентификатор клиента (адаптера). Нажмите кнопку Next.
Параметры клиента (адаптера) Gitea/Forgejo
В следующем окне включите «Client authentication» и нажмите кнопку Next.
Включите аутентификацию в настройках клиента (адаптера) Keycloak
В «Root URL» задайте URL приложения, на который пользователь будет перенаправлен в случае удачной аутентификации в Keycloak. Нажмите кнопку Save.
Задайте URL приложения для обратного перенаправления из Keycloak
После того, как будет создан клиент, перейдите на вкладу Credentials и скопируйте «Client Secret», который потребуется ниже при настройке Gitea/Forgejo. Данным ключом клиент Gitea/Forgejo будет подтверждать свою подлинность при подключении к Keycloak.
Ключ доступа клиента (адаптера) Gitea/Forgejo к Keycloak
Переходим к настройке Gitea/Forgejo. Залогиньтесь в Gitea/Forgejo пользователем с административными правами и перейдите в панель управления.
Меню администратора в Gitea/Forgejo
Перейдите в «Identity & Access» подменю Аутентификация и нажмите кнопку «Добавить новый источник».
Добавьте новый источник аутентификации в Gitea/Forgejo
В поле «ID клиента (ключ)» задайте значение, которое ввели выше при настройке клиента в Keycloak в поле «Client ID». В поле «Клиентский ключ» задайте значение «Client Secret», скопированное выше из вкладки Credentials настроек клиента в Keycloak. В поле «URL иконки» задайте пиктограмму, которая будет отображаться на окне аутентификации Gitea/Forgejo напротив кнопки «Войти с помощью keycloak».
Параметры источника аутентификации Keycloak в Gitea/Forgejo
«Open ID Connect URL для автоматизации входа» возьмите в меню настроек «Realm Settings», скопировав ссылку «Open ID Endpoint Configuration» в Keycloak.
Ссылка на параметры Endpoint Keycloak
Нажмите кнопку «Добавить новый источник» в Gitea/Forgejo и будет создан новый источник аутентификации, привязанный к Keycloak. На странице аутентификации Gitea/Forgejo появится кнопка «Войти с помощью keycloak».
Кнопка входа через Keycloak на интерфейсе Gitea/Forgejo
Пользователь нажимает на кнопку «Войти с помощью keycloak», и его перенаправляет на окно аутентификации Keycloak.
Окно аутентификации в Keycloak
Пользователь вводит логин и пароль, и в случае удачной аутентификации Keycloak перенаправляет пользователя в его личный кабинет в Gitea/Forgejo.
Личный кабинет пользователя в Gitea/Forgejo
А в Keycloak появится сессия пользователя.
Активная сессия пользователя в Keycloak
База данных логинов и групп пользователей
Keycloak может как самостоятельно хранить в собственной базе данных логины, пароли и прочую информацию о пользователях, так и выступать в качестве провайдера к внешним источникам аутентификации, вроде Microsoft Active Directory, LDAP и т.д. В нашем случае внешний источник аутентификации не потребовался, и мы создали логины и группы пользователей прямо в Keycloak.
Зайдите в консоль администратора Keycloak, выберите область и перейдите в меню Groups. В нашем случае мы разделили всех пользователей на две группы:
admins — пользователи с административными правами во всех сервисах (для тимлидов);
devs — пользователи с правами только на чтение (для разработчиков).
Группы пользователей в Keycloak
Нажмите кнопку «Create group» и введите имя группы.
Создайте группу пользователей в Keycloak
Нажмите кнопку Create. Повторите те же действия и создайте группу devs.
Далее, для примера создадим пользователя owneruser и добавим его в группу admins. А также пользователя user1 и добавим его в группу devs. Перейдите в меню Users и нажмите кнопку «Add users».
Пользователи в Keycloak
Введите данные пользователя. Обязательно задайте e-mail (Grafana использует e-mail пользователя вместо логина для аутентификации).
Параметры пользователя
Нажмите кнопку «Join Groups», добавьте пользователя в группу admins и нажмите кнопку Join.
Добавьте пользователя в группу
Нажмите кнопку Create, чтобы создать пользователя. Аналогично создайте пользователя user1 и добавьте его в группу devs.
Авторизация на базе ролей Roles
Авторизация и разграничение полномочий потребовалась в Jenkins и Grafana. Тимлидам нужно было предоставить полный доступ, чтобы они могли создавать пайплайны в Jenkins и дашборды в Grafana. А разработчикам дать права только на чтение, чтобы они могли наблюдать за своими пайплайнами и мониторить нагрузку по дашбордам в Grafana.
Залогиньтесь в консоль администратора Keycloak, перейдите в меню «Realm roles».
Список ролей Roles области
Нажмите кнопку «Create role». В следующем окне задайте имя роли и нажмите кнопку Save.
Параметры создаваемой роли
Перейдите в меню Groups, выберите группу admins и перейдите на вкладку «Role mapping».
Вкладка ролей группы
Нажмите кнопку «Assign role», выберите роль admins и нажмите кнопку Assign.
Ассоциируйте роль с группой
Аналогично ассоциируйте группу devs с ролью devs. Теперь в случае удачной авторизации Keycloak среди прочей информации о пользователе передаст еще и роль пользователя в сервис, из которого пришел аутентифицироваться пользователь. И сервис сможет выполнить авторизацию пользователя и назначить полномочия в соответствии с его ролью. Информацию о клиенте Keycloak передает в Scopes, список которых можно посмотреть в меню «Client scopes».
Список Scopes области
Среди прочих Scopes по умолчанию есть roles.
Настройте разграничение полномочий на основе ролей в Jenkins. Перейдите в настройках Jenkins в раздел «Manage and Assign Roles» и выберите подпункт «Maname Roles». В появившемся окне в поле «Role to add» введите имя роли admins и нажмите кнопку Add. Аналогично добавьте роль devs. В матрице доступа установите для роли admins галочку Administer в столбце Полные, а для роли devs галочки Read в столбцах Полные и Задача.
Настройка ролей в Jenkins
Залогиньтесь в Jenkins пользователем owneruser из группы admins, и вам будет доступен пункт добавления пайплайнов »+ Создать Item» и пункт настроек «Настроить Jenkins».
Личный кабинет пользователя с административными правами в Jenkins
Залогиньтесь пользователем user1, и пункты создания пайплайнов и настроек Jenkins исчезнут. Останется возможность только посмотреть информацию о пайплайнах.
Личный кабинет пользователя с правами только на чтение в Jenkins
Для 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
У пользователя user1 из группы devs права только на чтение, а пункт Administration в меню отсутствует.
Личный кабинет пользователя с правами только на чтение в Grafana
Аутентификация приложений без адаптера
Некоторые приложения не имеют под капотом адаптера 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
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
FallbackResource index.php
DirectoryIndex index.php
AuthType openid-connect
Require valid-user
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
При переходе на Web-интерфейс phpMyAdmin пользователь перенаправляется на окно аутентификации Keycloak. В случае удачной аутентификации Keycloak перенаправит пользователя в личный кабинет phpMyAdmin.
Личный кабинет пользователя в phpMyAdmin
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 области
Задайте имя кастомного Flow и нажмите кнопку Dupliacate.
Продублируйте browser Flow
Перейдите в Flow browser-custom и добавьте Sub-flow с именем RBAC в browser-custom forms.
Добавьте Sub-flow RBAC проверки доступа на базе ролей Roles
В Sub-flow RBAC добавьте Condition user-role.
Добавьте Condition user role
Нажмите на шестеренку справа от Condition user-role, задайте Alias и свяжите с ролью devs.
Свяжите Condition user role с ролью devs
Добавьте Step Deny Access
Step Deny Access
Нажмите шестеренку справа от Step Deny Access, задайте Alias и текст сообщения, которое будет выдано пользователю из группы devs при попытке доступа к базам данных в контурах staging и production.
Сообщение об ошибке
Подключите кастомный Flow browser-custom к клиентам (адаптерам) phpMyAdmin и pgAdmin в контурах staging и production. Перейдите в настройках клиента на вкладку Advanced, спуститесь в самый низ и в поле Browser Flow задайте browser-custom.
Задайте кастомный Browser Flow
При попытке доступа пользователя с ролью devs к Web-интерфейсу базы данных в контурах staging или production появится предупреждение.
Сообщение об ошибке доступа
Заключение
В данной статье мы осветили свой опыт реализации единого входа Single Sign-On (SSO) во все сервисы платформы Gitorion при помощи Keycloak. Будем рады обратной связи, замечаниям и конструктивной критике. Спасибо.