Как стать автором
Обновить

Как я KeyCloak побеждал

Уровень сложностиСредний
Время на прочтение5 мин
Количество просмотров6.2K
Как я себя чувствовал
Как я себя чувствовал

Привет, Хабр! Меня зову Амир и я хотел бы сегодня поделиться с Вами своим опытом поднятия сервиса SSO на базе решения KeyCloak.

Вводные:

Требования со стороны бизнеса:

  • Для внутренних сервисов компании требуется единая точка входа с подключением пользователей из существующей Active Directory.

  • Требуется что бы пользователь мог иметь доступ к одному или нескольким сервисам (в каждом из сервисов имел одну или несколько ролей). Если доступ к тому или иному сервису отсутствует, сообщать ему об этом.

Требования взаимодействия с КС

  • KC должен работать по https.

  • На стороне фронта будет использоваться пакет от KC https://www.npmjs.com/package/keycloak-js.

  • Возможность отправлять события в Kafka

Требования развертки

  • KC должен быть развернут в Docker с помощью Docker compose

Версия KC: 25.0.2, так же проверял на версии 26.0.0 (тоже норм)

Ну вот с вводными разобрались, теперь приступим к реализации.

Build and deploy

Dockerfile

FROM keycloak/keycloak:25.0.2
COPY keycloak-kafka-1.1.5.jar /opt/keycloak/providers/

ENTRYPOINT ["/opt/keycloak/bin/kc.sh"]

Для подключения Kafka к CK используем keycloak-kafka-1.1.5

Dokcer-compose.yaml

version: "3.9"

services:
  keycloak:
    image: my_docker_hub/keycloak:latest
    volumes:
      - ./themes:/opt/keycloak/themes
      - ./cert/cert.jks:/etc/x509/https/truststore.jks
    container_name: keycloak
    ports:
      - "8443:8443"
    env_file: ./.env
    command: start
    depends_on:
      keycloak-postgres:
        condition: service_healthy
    healthcheck:
      test:
        [
          "CMD-SHELL",
          "exec 3<>/dev/tcp/127.0.0.1/9000;echo -e 'GET /health/ready HTTP/1.1\r\nhost: http://localhost\r\nConnection: close\r\n\r\n' >&3;if [ $? -eq 0 ]; then echo 'Healthcheck Successful';exit 0;else echo 'Healthcheck Failed';exit 1;fi;",
        ]
      start_period: 10s
      interval: 30s
      retries: 3
      timeout: 5s

  keycloak-postgres:
    container_name: keycloak-postgres
    image: postgres
    volumes:
      - ./db/data:/var/lib/postgresql/data
    env_file: ./.env
    healthcheck:
      test: pg_isready -d postgres
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 5s

.env

KC_FEATURES: preview
KC_HEALTH_ENABLED: true
KC_METRICS_ENABLED: true

KC_HOSTNAME: host_keycloak
KC_HTTPS_PORT: 8443
KC_HTTPS_KEY_STORE_PASSWORD=STORE_PASSWORD
KC_HTTPS_KEY_STORE_FILE=/etc/x509/https/truststore.jks
KC_PROXY_HEADERS: xforwarded

KEYCLOAK_ADMIN: admin 
KEYCLOAK_ADMIN_PASSWORD: admin

KAFKA_TOPIC: user.event.user
KAFKA_ADMIN_TOPIC: user.event.admin
KAFKA_CLIENT_ID: keycloak
KAFKA_BOOTSTRAP_SERVERS: BOOTSTRAP_SERVERS
KAFKA_EVENTS: LOGIN,LOGOUT

KC_DB: postgres
KC_DB_URL: jdbc:postgresql://keycloak-postgres:5432/keycloak
KC_DB_USERNAME: keycloak
KC_DB_PASSWORD: keycloak
KC_DB_SCHEMA: public

POSTGRES_DB: keycloak
PGUSER: keycloak
POSTGRES_USER : keycloak
POSTGRES_PASSWORD : keycloak
PGPASSWORD: password

Процесс сборки и развертывания опускаю, т.к. там ничего интересного нет.

Настраиваем HTTPS (В моем случае у меня есть root cert)

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

  1. Копируем root сертификат в /opt/keycloak/cert

  2. Выполняем команды

    keytool -importkeystore -srckeystore cert.pfx -srcstoretype pkcs12 -destkeystore ./cert.jks -deststoretype pkcs12
    cd /opt/keycloak/cert

    Указываем пароль от сертификата cert.pfx и назначаем пароль для keystore

  1. В docker-compose прокидываем cert.jks

volumes:
      - ./cert/cert.jks:/etc/x509/https/truststore.jks
  1. В файле .env

    Прописываем следующие переменные

KC_HOSTNAME: host_keycloak
KC_HTTPS_PORT: 8443
KC_HTTPS_KEY_STORE_PASSWORD=STORE_PASSWORD
KC_HTTPS_KEY_STORE_FILE=/etc/x509/https/truststore.jks
KC_PROXY_HEADERS: xforwarded

После развертывания через Docker compose, можем открывать KC.

Поздравляю! До этого момента я добирался долго )

https://host_name:8443/

Стартовая страница KC
Стартовая страница KC

Заходим под admin / admin

Подключаем AD

Создаем свой Realm
Идем в User federation и создаем Ldap providers

Далее действовал по описанию в статье

https://habr.com/ru/companies/swordfish_security/articles/533264/

Ребятам и Swordfish Security огромное спасибо за статью

По обычаю, принимаясь за задачу по KC хотел уже потратить день другой на блогах и форумах в поисках решения/гайда, как тут уже все написано :-)

Подключаем Kafka

Заходим в Realm settings, переходим в Events, в селекте выбираем kafka

Теперь все события произведенные в админе будут отправляться в топик указанный в .env admin, а пользовательские события login/logout в топик user

Настройка Client

Создаем Client

Оставляем все настройки как есть
Оставляем все настройки как есть

После создания в настройках обязательно указываем разрешенные URL откуда можем обращаться и т.д.

Создаем client scope audience, что бы добавить аудиенцию клиента в токен.

add client scope
add client scope
create client scope
create client scope

Переходим на вкладку Mappers, жмём на Configure a new mapper

Audience
Audience
add mapper
add mapper

Name - указываете как удобно
Included Client Audience - выбираем нашего клиента

Сохраняем.

Добавляем наш scope нашему клиенту

Переходим в меню Clients - вкладка Client scopes - Add client scope

Add client scope
Add client scope

Выбираем наш scope и добавляем с признаком Default

Создание роли доступа для нашего client

Realm roles
Realm roles

Меню - Realm roles - Create role

Можно заполнить подобным образом
Можно заполнить подобным образом

Создание группы пользователей для доступа в наш client

Groups
Groups

Меню - Groups - Create group

Название я предпочитаю задавать для групп доступа = client id

Role mapping
Role mapping

Связываем роль доступа с нашей группой на вкладке Role mapping

Assign roles to my-client account
Assign roles to my-client account

Создание Flow для аутентификации через браузер для нашего клиента

Меню - authentication - Flows - дублируем browser

Далее формируем следующую структуру

Странно, но не думал что ситуация которую мне нужно было разрешить, настолько редкая и по этой тепе в интернете было совсем мало инфы (нуууу очень мало). Решение нашел на stackoverflow, которое было представлено в виде скрина, которое в итоге было модифицировано :-)

Flow - Required: {  
    name: Login: <Название клиента> 
}
Step - Alternative: Cookie
Step - Alternative: Identity Provider Redirector config 
Flow - Alternative: {  
    name: gated browser form: <Название клиента> 
}

Step - Required: Username Form
Flow - Conditional: {  
    name: gated browser form - Conditional OTP Form config: <Название клиента> 
}
Condition - Required: Condition - user configured
Step - Required: OTP Form
Flow: {  
    name: RBA - Conditiona: <Название клиента> 
}
Condition - Required: Condition - user role
{
    Alias: user role <Название клиента>,
    User role: Выбираем роль созданную для нашего Client,
    Negate output: On
}
Step - Required: Deny access  
{
    Alias: Deny access config <Название клиента>,
    Error message: Доступ в приложение <Название приложения> запрещен
}
Condition - user role config
Condition - user role config
Deny access config
Deny access config
Должно получиться так
Должно получиться так

Далее, указываем наш созданный Flow как основной для Client

advanced
advanced

Если сейчас мы попробуем пройти авторизацию в KC через адаптер keycloak-js получим следующий результат

У Вас нет доступа в my-client
У Вас нет доступа в my-client

Добавление пользователя в группу доступа к my-client

Меню - members - Add member - выбираем пользователя из локальной базы или импортированного из AD

Далее пробуем пройти авторизацию через адаптер keycloak-js, получим результат "Успешная авторизация" с получением токена и редиректа на страницу указанную в настройках Client.

Итог

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

Спасибо за прочтение.

Теги:
Хабы:
+11
Комментарии6

Публикации

Истории

Работа

DevOps инженер
36 вакансий

Ближайшие события

28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
2 – 18 декабря
Yandex DataLens Festival 2024
МоскваОнлайн
11 – 13 декабря
Международная конференция по AI/ML «AI Journey»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань