Уявіть собі таку картину: ви створили справжній шедевр серед веб-додатків. Він стильний, адаптивний, і ваша мама вже розповіла про нього всім своїм друзям.
Але є проблема — хто ж підкрадається до вас? Кіт сусідів? Ваш колишній? Боти, що планують продати ваші дані користувачів?
Ось тут і з’являється Keycloak — вартовий, якого заслуговує ваш веб-додаток.
Keycloak не просто стоїть на порозі і перевіряє документи — це справжній охоронець, готовий відсіяти небажаних осіб, видаючи VIP-перепустки лише вашим легітимним користувачам.
З кількома налаштуваннями він перетворює ваш додаток на фортецю з управлінням ідентичністю, одноразовим входом (SSO) та іншими крутих акронімами, які можна тільки уявити.
У цьому посібнику ми покажемо, як інтегрувати Keycloak у ваш додаток, крок за кроком, без потреби в магістерському дипломі з безпеки.
Вважайте це IKEA для налаштувань автентифікації — мінімум інструментів, досить чіткі інструкції та декілька моментів, які змусить вас задуматися.
Наприкінці ваш додаток отримає більше стилю, ніж нічний клуб з оксамитовими мотузками. Почнемо!
Крок 1: Налаштування простого веб-додатку
Отже, час рукаватися і приступати до справи. Перш ніж Keycloak зможе розпочати свою магію, нам потрібен веб-додаток, який буде готовий зустріти його з розкритими обіймами (і стабільною базою коду). Вважайте це налаштуванням сцени, щоб Keycloak міг увійти і забрати шоу.
Збираємо інгредієнти
Ми робимо це просто — легкий Flask додаток, достатній для демонстрації потужності Keycloak. Спочатку налаштуємо залежності (requirements.txt):
Flask==3.1.0
Authlib==1.3.2
python-keycloak==5.1.1
Чому саме ці? Flask — наш вибір для швидких і чистих веб-додатків, а Authlib і python-keycloak допоможуть нам працювати з Keycloak як професіонали.
Привіт, Flask!
Наступний крок — створити простий app.py для запуску додатку:
from flask import Flask
app = Flask(__name__)
@app.get("/")
def display_index():
return "Hello World!"
Це ваш веб-додаток у вигляді “Привіт, це я”. Нічого складного, але, що правда, кожен шедевр починається з чистого полотна.
Запуск
Тепер час побачити, як наше творіння робить свої перші кроки. Виконайте ці команди:
python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install -r requirements.txt
python3 -m flask run
Якщо все пройде без проблем, ви побачите свій додаток за адресою http://127.0.0.1:5000. Відкрийте його у браузері, і ось він: скромне “Hello World!” чекає, поки стане чимось набагато крутішим.
Крок 2: Dockerize ваш додаток
Отже, ваш додаток працює локально, і ви відчуваєте себе справжнім кодером-чарівником.
Але давайте будемо чесними — який сенс, якщо він не може вирватися з вашого ноутбука і жити своїм найкращим життям у хмарі (або хоча б на сервері)?
Час помістити цей додаток у Docker контейнер і зробити його портативним, масштабованим і просто крутим.
Напишемо Dockerfile
Dockerfile — це як рецепт для створення контейнера для додатка. Ось який він буде для нашого Flask-додатку:
FROM python:3.13-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
EXPOSE 5000
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
CMD ["flask", "run"]
Розберемо це:
— Базовий образ: Ми використовуємо python:3.13-slim — легкий, швидкий і з усією добротою Python.
— Робочий каталог: /app — саме тут відбуватиметься вся магія всередині контейнера.
— Залежності: Ми копіюємо requirements.txt і встановлюємо все через pip.
— Код: app.py поміщається в контейнер, як зірковий актор на сцену.
— Відкриття порту: Flask за замовчуванням використовує порт 5000, тому ми робимо його доступним для зовнішнього світу.
— Запуск додатку: Остання команда запускає шоу.
Додаємо Docker Compose
Навіщо зупинятися лише на контейнері, коли можна оркеструвати його, як симфонію?
Docker Compose допоможе зробити життя легшим, особливо якщо з часом ви додасте базу даних чи інші сервіси.
Ось простий docker-compose.yml:
version: '3.9'
services:
app:
build:
context: ./
ports:
- "80:5000"
Це робить дві важливі речі:
— Створення образу: Вказує на ваш Dockerfile і відповідає за створення додатку.
— Маппінг портів: Мапить порт 5000 всередині контейнера на порт 80 на вашій машині, щоб ваш додаток був доступний за адресою http://localhost/.
Запускаємо додаток у Docker
Якщо ваш Dockerfile і docker-compose.yml готові, вам залишиться лише виконати одну команду:
docker compose up -d
Ця команда будує образ, запускає контейнер і відключає його (ось чому параметр -d).
Щоб перевірити, чи він працює, перейдіть за адресою http://localhost/ у браузері, і ось воно!
Ваш додаток тепер працює всередині Docker контейнера.
“Hello World!” поки що скромне, але тепер воно має паспорт для подорожей туди, де дозволено Docker.
Крок 3: Захистіть свій (марний) додаток за допомогою Keycloak
Вітаємо, ви побудували ідеальну майданчик для того, щоб Keycloak показав свої вміння.
У цьому розділі ми познайомимося з охоронцем, налаштуємо перевірки ID та переконаємося, що ніхто не проникне через двері.
Приготуйтеся — буде цікаво!
Спочатку ми розглянемо, як налаштувати Keycloak у Docker та як створити новий realm і клієнта, а після цього поговоримо, як повідомити нашому простому веб-додатку, що він повинен насправді використовувати Keycloak-сервіс, який ми налаштували. Це буде зроблено через OIDC (OpenID Connect) робочий процес.
Запуск Keycloak в Docker
Налаштування Keycloak у Docker таке ж просте, як додати наступну конфігурацію у ваш docker-compose.yml і запустити docker compose up -d.
services:
keycloak:
image: quay.io/keycloak/keycloak:24.0
container_name: keycloak
environment:
KC_HOSTNAME: localhost
KC_HOSTNAME_PORT: 7080
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: Start123
KC_HOSTNAME_STRICT_BACKCHANNEL: false
KC_HEALTH_ENABLED: "true"
KC_LOG_LEVEL: info
healthcheck:
test: ["CMD", "curl", "-f", "http://keycloak:7080/health/ready"]
interval: 15s
timeout: 2s
retries: 15
command: ["start-dev", "--http-port", "7080", "--https-port", "7443"]
ports:
- "7080:7080"
networks:
-
Ми використовуємо Docker образ quay.io/keycloak/keycloak:24.0, і наш контейнер буде називатися "keycloak". Також потрібно налаштувати деякі змінні середовища. Вони досить прості. Важливо, щоб KCHOSTNAMESTRICTBACKCHANNEL_ було встановлено в false, оскільки інакше Keycloak відмовлявся б від REST викликів, які надходять з бекенду нашого веб-додатку. Healthcheck є необов'язковим, але дуже корисним, оскільки Docker може показувати не тільки, чи працює наш контейнер, але й чи працює служба всередині контейнера правильно (здоровий).
Команда наказує Keycloak запускатися в режимі розробки. Це дуже важливо для нас, тому що в нерозробницькому режимі Keycloak наполягатиме на використанні HTTPS, для чого потрібен сертифікат і буде багато інших проблем. Тому ми використовуємо його в режимі розробки і ігноруємо всі попередження.
Але не використовуйте це в продакшн середовищі!
Крім того, наш контейнер Keycloak має бути підключений до тієї ж мережі, до якої підключено наш простий веб-додаток (у нашому випадку стандартна мережа Docker). Нам все ж потрібно відкрити порт 7080 для загального доступу, оскільки саме там працює консоль адміністратора Keycloak.
Тепер ми вводимо docker compose up -d і отримуємо працюючий екземпляр Keycloak.
Налаштування Keycloak
У цьому розділі ми налаштуємо Keycloak через консоль адміністратора, щоб він міг бути використаний нашим простим веб-додатком.
Спочатку відкрийте http://localhost:7080 та увійдіть за допомогою імені користувача та пароля, які ми налаштували у docker-compose.yml.
Створіть новий realm і дайте йому ім'я. Переконайтесь, що в цьому імені немає символів, несумісних з URL (наприклад, пробілів), інакше це викличе проблеми пізніше.
Після натискання "Create", перейдіть до налаштувань realm вашого нового realm і увімкніть "User registration" на вкладці "Login". Якщо ми увімкнемо це, ми зможемо пропустити створення тестових користувачів, що могло б бути незручно при експорті realm пізніше.
Далі перейдіть до "Clients" і натисніть "Create client". Дайте йому ID клієнта.
Увімкніть автентифікацію клієнта.
Введіть URL вашого додатку. У нашому випадку це http://localhost/. Ми можемо пропустити порт, оскільки це порт за замовчуванням 80. Якщо ми використовували якийсь порт розробки, наприклад 5000, ми повинні вказати його тут явно.
Після натискання "Create", перейдіть до вкладки "Credentials" вашого новоствореного клієнта і скопіюйте секрет клієнта для подальшого використання.
Keycloak тепер повністю налаштований!
Експорт конфігурації Keycloak
Тепер, коли ми налаштували наш сервіс Keycloak, уявіть, що вам потрібно робити це кожного разу при розгортанні сервісу. На щастя, є спосіб цього уникнути.
Спочатку потрібно експортувати нашу конфігурацію у файл json. Для цього перейдіть до налаштувань realm вашого realm і натисніть "Partial export" у випадаючому меню в правому верхньому кутку.
Виберіть "Include clients" і експортуйте.
Як ви можете побачити, немає можливості експортувати користувачів. Ось чому ми раніше увімкнули реєстрацію користувачів, щоб не створювати нових тестових користувачів кожного разу, коли розгортаємо Keycloak. (Можна вручну додати користувачів до експортованого файлу realm, але ви не хочете це робити. Повірте мені.)
Після експорту відкрийте файл .json і знайдіть рядок “secret”: “”. Вставте секрет, який ми скопіювали раніше, замість “**********”. Keycloak не експортує секрети клієнтів з міркувань безпеки, але для нашого випадку нам це потрібно в експортованому файлі realm.
Тепер ми можемо налаштувати наш docker-compose.yml для автоматичного імпорту realm при запуску Keycloak.
Ми робимо це наступним чином:
services:
keycloak:
image: quay.io/keycloak/keycloak:24.0
container_name: keycloak
environment:
KC_HOSTNAME: localhost
KC_HOSTNAME_STRICT_BACKCHANNEL: false
KC_HOSTNAME_PORT: 7080
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: Start123
KC_HEALTH_ENABLED: "true"
KC_LOG_LEVEL: info
healthcheck:
test: ["CMD", "curl", "-f", "http://keycloak:7080/health/ready"]
interval: 15s
timeout: 2s
retries: 15
command: ["start-dev", "--http-port", "7080", "--https-port", "7443", "--import-realm"]
ports:
- "7080:7080"
networks:
- reddyt
volumes:
- ${PWD}/keycloak:/opt/keycloak/data/import
Як ви можете побачити, ми додали прапорець --import-realm до опції команди. Це вказує Keycloak імпортувати файли realm з /opt/keycloak/data/import всередині контейнера при першому запуску. Ми забезпечили, що експортований файл realm знаходиться на місці, додавши том з нашої локальної папки keycloak до місця в контейнері.
Все, що вам потрібно зробити, це помістити експортований файл realm у папку keycloak.
Налаштування простого веб-додатку
Ми налаштували наш сервіс Keycloak, однак, якщо ми відвідаємо http://localhost/ у браузері, ми все одно можемо увійти в наш простий веб-додаток без будь-яких запитів на авторизацію. Це тому, що додаток ще нічого не знає про Keycloak. Ми змінимо це в цій секції.
Пакети
Перш за все, потрібно встановити необхідні пакети Python. Вам знадобляться:
— Authlib (ми використовуємо версію 1.3.2)
— python-keycloak (ми використовуємо версію 5.1.1)
Додайте їх у свій requirements.txt, виконайте команду pip install -r requirements.txt, і все буде готово.
Реєстрація клієнта OAuth
Тепер потрібно налаштувати наш додаток. Для цього додайте наступний код у app.py:
from authlib.integrations.flask_client import OAuth
from flask import Flask
app = Flask(__name__)
app.secret_key = "" # Не робіть цього в продакшн-версії
oauth = OAuth(app)
keycloak = oauth.register(
name="keycloak",
client_id="simple-web-app", # Використовуйте змінні середовища для цих значень в продакшн
client_secret="",
server_metadata_url="http://keycloak:7080/realms/simple-web-application-realm/.well-known/openid-configuration",
client_kwargs={"scope": "openid profile email"},
)
Що це робить? Ми дали нашому Flask додатку секретний ключ, щоб він міг керувати сесіями за нас. Також ми налаштували OAuth-клієнта для нашого Flask додатку. Для цього клієнта ми реєструємо наш сервіс Keycloak з id клієнта та секретом, які ми встановили раніше, а також URL метаданих сервера (або кінцеву точку .well-known), яку можна знайти в налаштуваннях realm на сторінці в Keycloak. Окремі параметри, які ми встановили в client_kwargs, визначені стандартом OIDC.
Для продакшн-версії обов’язково використовуйте змінні середовища для всіх параметрів конфігурації, особливо для секретів, але НІКОЛИ не вписуйте їх прямо в вихідний код. Ми зробили це тут, бо вважаємо, що сервіс середовища ускладнив би справу і відволікав би від основної мети налаштування Keycloak.
Налаштування маршрутів
Тепер наш додаток знає про сервіс Keycloak, але ще не використовує його для автентифікації. Тому потрібно налаштувати кілька маршрутів:
import secrets
from authlib.oauth2 import OAuth2Error
from flask import session, url_for, redirect
@app.route("/login")
def login():
nonce = secrets.token_urlsafe(16)
session['nonce'] = nonce
redirect_uri = url_for('callback', _external=True)
return keycloak.authorize_redirect(redirect_uri, nonce=nonce)
@app.route("/callback")
def callback():
try:
token = keycloak.authorize_access_token()
session['token'] = token
user_info = keycloak.parse_id_token(token, session.pop("nonce", None))
session['user'] = user_info
return redirect("/")
except OAuth2Error as error:
return f"Authentication failed: {error.description}"
Ми налаштували маршрут /login, який перенаправляє користувача на екран входу Keycloak, коли він відвідує його, а також маршрут /callback, на який Keycloak перенаправляє користувача після завершення входу. Після автентифікації ми зберігаємо інформацію про користувача в їхній сесії, оскільки можемо використовувати її далі.
Тепер користувачі можуть авторизуватися. Однак спробуйте відвідати http://localhost/ без авторизації. Ви зможете відвідати додаток без авторизації. Це має бути іншим. Додаток має автоматично перенаправляти будь-якого неавторизованого користувача на сторінку входу.
Щоб це зробити, додаємо наступну перевірку до app.py:
from flask import request, session, url_for, redirect
@app.before_request
def check_authentication():
if request.endpoint in ["login", "callback"]:
return
if "user" not in session:
return redirect(url_for("login"))
Тепер ми перевіряємо, чи містить сесія інформацію про користувача для кожного запиту, який ми отримуємо.
Ми виключаємо маршрути /login та /callback, оскільки ці маршрути повинні бути доступні для неавторизованих користувачів. Для всіх інших маршрутів ми перенаправляємо неавторизованих користувачів на сторінку входу.
Вихід з системи
Тепер всі користувачі повинні авторизуватися, щоб використовувати наш додаток. Однак вони не можуть вийти з системи. Ми виправимо це в цій секції.
Ми вже налаштували правильну URL-адресу для виходу в Keycloak (це єдине, що не налаштовується автоматично). Отже, все, що нам потрібно зробити, це сказати нашому додатку, як вийти з системи. Ми додаємо кнопку для виходу та маршрут /logout до нашого app.py:
from flask import session, url_for, redirect
@app.get("/")
def display_index():
return """
Hello World!
Logout
"""
@app.route('/logout')
def logout():
id_token = session['token']['id_token']
if not id_token:
return redirect(url_for('login'))
keycloak_logout_url = "http://localhost:7080/realms/simple-web-application-realm/protocol/openid-connect/logout"
post_logout_redirect_uri = url_for('login', _external=True)
session.clear()
return redirect(
f"{keycloak_logout_url}?id_token_hint={id_token}&post_logout_redirect_uri={post_logout_redirect_uri}")
Кнопка виходу перенаправляє на кінцеву точку /logout. Ця точка виходу перевіряє, чи є у користувача id token, відкликає токен на Keycloak, якщо він є, і перенаправляє користувача на сторінку входу. Відкликання токена здійснюється шляхом перенаправлення користувача на кінцеву точку відкликання токена в Keycloak з токеном та URI перенаправлення після виходу, що в свою чергу використовується Keycloak для перенаправлення користувача після того, як токен буде відкликано. Також ми очищаємо сесію Flask.
— Тепер ви успішно налаштували ваш простий веб-додаток з Keycloak.
Вітаємо!
Перекладено з: Securing a Flask App with Keycloak in Docker