Pull to refresh

Как я делал IAM на готовых решениях

Reading time4 min
Views7.5K

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

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

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


Что

Что не так было с проектом и почему мы вообще решили от него избавляться. Вот четыре всадника апокалипсиса в этом проекте.

  • Высокая стоимость поддержки. Мы как инженеры всегда стараемся держать минимальный time-to-market, при этом, чтобы стоимость поддержки была минимальной. Но тут что-то пошло не так из-за недостатка технического контроля

  • Плохая инженерная культура. Тесты не писались, куча костылей и сомнительный способ выбора технологий под меняющиеся требования. Изначально стек был выбран в виде Django+Celery с кучей примочек для асинхронности (сюда бы Go залетел бы со своей асинхронной моделью, но проект не того уровня, к сожалению)

  • Очень много велосипедов и самописных решений. Я не знаю, как в 2022м году можно стартуя новый проект делать что-то своё, не пользуясь опенсорс решениями или готовыми сервисами, типа Ory, Auth0, Firebase и подобными.

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

Как

Мы делали проект не для рынка РФ и поэтому мы могли выбрать любой хостинг, какой посчитаем нужным. Решили остановиться на AWS (ведь все сейчас сидят на нём, верно?) Разработку начали с создания документа, где описали все проблемы текстом, варианты решений и заложили сразу несколько принципов

  1. Инженерная культура должна оставаться на хорошем уровне. Мы пишем тесты, идем к самодокументирующемуся коду, документируем решения прямо в коде комментариями (это иногда спасает кучу нервов), делаем код максимально читаемым и понимаемым для человека, чтобы было понятно ПОЧЕМУ, а не как

  2. Стараемся использовать как можно больше от AWS и мы были готовы к вендор локу. Можно запустить базу внутри k8s кластера, но зачем? Денег не сэкономишь, а нужен будет еще админ для всего этого

  3. ISO 27001 compliant наложил ряд ограничений по задокументированной информации и мы решили документацию для разработчиков хранить в папочке doc в корне репозитория. Практика показала, что так просто удобнее. Мы пробовали gitbook, confluence и еще много разных форматов, но всё-таки на gitlab вместе с его поддержкой в Markdown и sequentional diagrams и всего остального вышло хорошо

  4. Modern application framework дал нам хорошие вопросы для установки границ

    1. What are your business priorities?

    2. What is the worst possible scenario?

    3. What are your immovable constraints?

    4. What data is this solution storing/processing?

    5. What skills does your team have?

    6. What is the timeline for the project?

Что выбирали и как

Выбирали мы между Okta, Auth0, Keycloak и Ory после небольшого ресерча. Требования мы выставили следующие:

  1. Простота в интеграции, хорошее API и хорошая документация. Чистый код, желательно нашего стека (Python+Go)

  2. Возможность управлять учетными данными самим и желательно, чтобы было self-hosted решение, но если будет достаточно безопасно, то можно и рассмотреть вариант интеграции

  3. Достаточно легковесно и нересурсоемко

  4. Подходы API First, Documentation first в разработке у команды, идеально если OpenSource

Auth0 - Из плюсов у них есть Single Sign On, у них API First подход, можно интегрировать с чем угодно достаточно просто, используя их API, но ценник кусался за $23 доллара за месяц за 10к MAU и при этом не получится

Keycloack - из плюсов - это достаточно хорошее enterprise level решение для IAM, оно опенсорсное, но мы не взяли его потому что были проблемы с infinispan и он написан на Java. Для банков с поддержкой вполне себе пойдет, но для нас не очень

Okta - хороший продукт для энтерпрайза, но для маленького проекта это было не очень удобно для нас, потому что не было self-hosted решений и это наложило бы на нас сразу небольшой vendor-lock.

Ory (Ory Kratos для идентификации и Ory Keto для пермишнов) мне полюбились, у них хорошая инженерная культура, мейнтейнеры рядом с сообществом, достаточно активная поддержка в гитхабе и слаке, иногда бывают комьюнити митапы и код написан на Go, при чём достаточно хорошо, после беглого ревью кода.

Интеграция в проект

Мы делали проект на Flask и интеграция Ory Kratos и Ory Keto сводится к тому, что мы добавляем их в docker-compose.yml , пользуемся документацией Ory Keto и Ory Kratos, пишем немного кода на Python и получается примерно следующий код (простой для примера)

"""Public section, including homepage and signup."""
import requests

from flask import Blueprint, render_template, session, redirect, request, abort
from config import settings

blueprint = Blueprint("public", __name__, static_folder="../static")


HTTP_STATUS_FORBIDDEN = 403


@blueprint.route("/", methods=["GET", "POST"])
def home():
    """Home page."""

    if 'ory_kratos_session' not in request.cookies:
        return redirect(settings.KRATOS_UI_URL)


    response = requests.get(
        f"{settings.KRATOS_EXTERNAL_API_URL}/sessions/whoami",
        cookies=request.cookies
    )
    active = response.json().get('active')
    if not active:
        abort(HTTP_STATUS_FORBIDDEN)

    email = response.json().get('identity', {}).get('traits', {}).get('email').replace('@', '')

    # Check permissions

    response = requests.get(
        f"{settings.KETO_API_READ_URL}/check",
        params={
            "namespace": "app",
            "object": "homepage",
            "relation": "read",
            "subject_id": email,
        }
    )
    if not response.json().get("allowed"):
        abort(HTTP_STATUS_FORBIDDEN)

    return render_template("public/home.html")

И всё работает. Весь проект можно посмотреть у меня на гитхабе.

А что используете вы, чтобы идентифицировать пользователей?

Tags:
Hubs:
Total votes 11: ↑8 and ↓3+7
Comments24

Articles