4gophers

Hydra

Перевод статьи "Hydra: Run your own Identity and Access Management service in <5 Minutes".

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

Hydra появилась на свет благодаря острой необходимости в масштабируемом 12factor приложении для OAuth сервисе корпоративного уровня и, при этом, без всяких сумасшедших фич и тонны сторонних зависимостей. Кроме того, мы добавили политики, управление аккаунтами/клиентами и еще кое-какие фичи. Мы решили не реализовывать поддержку 5 различных баз данных, что позволило сократить дерево зависимостей и теперь Hydra работает только с PostgreSQL и другими SQL'ными базами данных.

Краткое описание основных возможностей Hydra:

Hydra написан мной (GitHub/LinkedIn) как часть бизнес приложения, которое пока в разработке. Сейчас Hydra поддерживает Thomas Aidan Curran.

Почему Hydra это бекенд?

Hydra не предоставляет HTML страницы для логина, разлогирования или авторизации. Вместо это, если необходимо выполнить некоторое действие, то Hydra редиректит на заранее определенный URL, к примеру http://sign-up-app.yourservice.com/sign-up или http://sign-in-app.yourservice.com/sign-in. К тому же, пользователь может использовать другой OAuth2 провайдер, например Dropbox или Google.

Мне нужны действия, пацанчик!

Отлично, мне тоже! :) Давайте попробуем как установить Hydra и получить токен для клиентского приложения, его еще называют OAuth2 Client Grant (секция "Application Access"). Если вы не знаете как работать в консоли или у вас возникли вопросы, можете проконсультироваться на GitHub Issue Tracker.

Обратите внимание: в будущем, Hydra будет работать в Docker контейнере. Но сейчас, чтобы ее запустить, вам необходим Vagrant, VirtualBox и Git.

$ git clone https://github.com/ory-am/hydra.git
$ cd hydra
$ vagrant up

В рамках этого туториала я рассчитываю, что у вас не меняется рабочая директория. Я так же рассчитываю что вы работаете на Linux/Unix системе и у вас есть доступ к командной строке. Вы можете запускать vagrant ssh чтобы попасть в виртуальную машину. После этого вы можете выполнять все команды(например curl) без проблем. Только убедитесь что внутри виртуальной машине и избегайте всяких exit вызовов.

Для начала научимся запускать Hydra. Vagrant роутит 9000(HTTPS для Hydra) и 9001(для Postgres) на ваш локальный хост. Перейдите по URL https://localhost:9000/alive чтобы убедится, что Hydra запушен и все работает. Возможно, вам придется добавить исключение для HTTP сертификата, так-как он самоподписанный, и после это вы должны увидеть {"status":"alive"}, что означает что сервис Hydra запушен и работает.

Мы можем настроить тестовое клиентское приложение(id = app, secret = secret) с правами суперпользователя. Для этого в Vagrant нужно запустить команды:

$ hydra-host client create -i app -s secret -r http://localhost:3000/
authenticate/callback --as-superuser.

Инструмент hydra-host предоставляет набор возможностей для управления вашим инстансом Hydra. Вся информация по использованию есть в документации. Внутри Vagrant вы всегда можете спокойно добраться до hydra-host.

$ vagrant ssh
$ hydra-host help

Иногда, при загрузке у Vagrant возникают проблемы с сетью. Если вы не наблюдаете никаких других ошибок, например 404 в браузере, попробуйте выполнить vagrant destroy -f && vagrant up. После этого, в течении нескольких минут, Hydra поднимется и все должно заработать.

Получаем OAuth2 Token Client

Наконец то мы запустили Hydra, давайте теперь будем делать немного токен-магии. Я рассчитываю, что у вас установлен curl. Если это не так, то посмотрите как его установить. Наша цель заключается в "обмене"" учетных данных клиента на токен доступа.

Мы запускаем curl с параметром --insecure, так как у нас самоподписанный TLS сертификат. В вашем случае токен будет отличаться, но сам ответ сервера будет примерно таким же.

curl --insecure -X POST --user app:secret "https://localhost:9000/oauth2/
token?grant_type=client_credentials"

{
    "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiIiLCJleHAiO
    jE0NTAzNTc4MDksImlhdCI6MTQ1MDM1NDIwOSwiaXNzIjoiIiwiamlkIjoiMmYyYjk2MGQtZjE3
    OC00MzE5LWI4OGEtODc3YzM1Y2U5NTFkIiwibmJmIjoxNDUwMzU0MjA5LCJzdWIiOiJhcHAifQ.
    cLSY3G0Ngz62hJmanADZ3LUfblB5nOZOWr7bAflE9T0pZBp-
    Qv1sTkwRCQfqv870cpHdFvN9xL_AReMmNo_o9sLmXfNZDL5WJzDhhsLximxPMD-
    rO0DjnvY5663l0fvhFMlaGREsHGWDzPN-wZLczRjlFr1JXPv80qMeCm9d343hGMu26WWZ8bfdgA
    bae8ecmSO_oP7I8U0tWn22FzVJjSRuaShKxlWyQY2K_0-VoHDQDZMTEIXxYGNPA0MmCOEK1DDAi
    UeKTbguMSLMCjXTkbxd2rMwHday1oHDH8aBkyL0CGmmfVfl20hfRYqJ0x7_0sTd__-
    inASEjozSvYkVOw",
    "expires_in": 3600,
    "token_type": "Bearer"
}

На текущий момент мы все еще обсуждаем, какие токены нужно использовать в Hydra. Работать только с самодостаточными JWT токенами или поддерживать различные типы? Если у вас есть идеи на этот счет, обязательно отпишитесь в дискуссии.

Получаем OAuth2 Token Password

Это было быстро, верно? Давайте теперь добавим обычную учетную запись для пользователя. Вы можете использовать что угодно в качестве имени пользователя(имя, email, случайный идентификатор), но оно должно быть уникальным. И обязательно запомните полученный ID, он пригодится нам в будущем.

$ vagrant ssh
$ hydra-host account create foo@bar.com --password secret

Created account as "e152f029-424f-4d4d-9d69-643225113ee5".

$ exit

Аутентифицируем пользователя с использованием паролей в OAuth2 .

curl --insecure --data "grant_type=password&username=foo@bar.
com&password=secret" --user app:secret "https://localhost:9000/oauth2/token"

{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiIiLCJleHAiOjE
  0NTAzNzAwODAsImlhdCI6MTQ1MDM2NjQ4MCwiaXNzIjoiIiwiamlkIjoiYTdjZDFmYWQtZTg5MS00
  ZDJmLWIwZmEtMzE2Zjg3MTI5ZGIyIiwibmJmIjoxNDUwMzY2NDgwLCJzdWIiOiJiY2U5M2QzMy05Y
  jVhLTQ5MzMtOTQ3Mi1jYWRhMDE4ZGFmNjAifQ.
  dqUHiAJ0uoUYtV4hqhgVqYqA6PSy1cmNZQruyTpmRaCBh2RHzkijFj4F-
  T8xTbrFBnysTQG3LxxeXkDNq6PZBsZ4WzvUXSy1R18MayT5FWkgAi-ROQ2lHn9Isw1IgN3XWO-
  YOaQt9rO0gG4w_hRQ-DprMMKcUkNVC1zK_pdUpaB7cEurYF3sd7krPQjIhucPVhJqDjkAIZGG54kd
  28_uLqKi3eTaDrViwGLbYzmLenfTb79Hxjfd8qFd_KBQW-f1maLy0BwQNP1pVu2I_P7CBjIwEm898
  wTPye42CFUfVzyvB6ob4sAZM60YVwzxN_zaw_SO1160HbDI4oO-HwwPig",
  "expires_in": 3600,
  "refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.
  eyJpZCI6IjBlNmJmNTBlLTU1N2EtNGJiYy1iZDk1LTg3ZDJkOTNkOGQ5YSJ9.JFVgu7Tf1BZJLrMb
  gKi0wyBKXZuHB63yKbv6_UP8TUkUgH8e9S5Gi9MhlPOnU0KyiEkh8p5Z0CMN2HQeIeYj-
  0p3POFxoSkY6NPZeWKsnPXzDjlJJmXWYrqgI-N-
  BD26MmoGXLjHt_DY3hxBX_EzHHuqVk9q-2pUAfwc0BHjSidF5EZ852I5e3J0WHbiw4KnogNRKNN-l
  siIIEBSjkBxyyH85Dx4JdQZsAJVBKiXXzizWIQeQABAIutvIs5ok3T4xD8WYEiSuiHdKbPKe9bjNG
  X2OqW1X-eDts4RE0eHWatNQ-IafwMvi-7A0f5PSf26pSGPQ5TyvpA5qbnYAIXrMw",
  "token_type": "Bearer"
}

Процесс авторизации в OAuth2

Теперь посмотрим как работает процесс авторизации в OAuth2. Для этого на нужен ID аккаунта из примера выше. Так как Hydra это только бекенд часть, вам нужно будет использовать пример приложения для логина/разлогирования. Вам стоит посмотреть код в приложениях hydra-signin и hydra-signup.

$ vagrant ssh
$ ACCOUNT_ID=<account_id_from_above> hydra-signin & exit

Вам нужно заменить <...> своими данными, например:

$ vagrant ssh
$ ACCOUNT_ID=e152f029-424f-4d4d-9d69-643225113ee5 hydra-signin & exit

Теперь в браузере перейдите на по ссылке: https://localhost:9000/oauth2/auth?response_type=code&client_id=app&redirect_uri=http://localhost:3000/authenticate/callback&state=foo.

Sign in page

Кликните по ссылке "Press this link to sign in" для перехода на следующую страницу.

Sign in callback page

Указанные пути для логниа/разлогирования настраиваются через переменные окружения SIGNUP_URL и SIGNUP_URL. Ясно, что текущие настройки это просто заглушки. Как использовать переменные окружения хорошо описано в документации.

Для следующего шага мы будем использовать curl и полученный ранее код.

curl --insecure --data "grant_type=authorization_code&code=<code_crom_above>" -
-user app:secret "https://localhost:9000/oauth2/token"

Замените <...> вашими значениями, например:

curl --insecure --data 
"grant_type=authorization_code&code=fEat4PS3TVeyWrwKgLxICg" --user app:secret 
"https://localhost:9000/oauth2/token"

{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiIiLCJleHAiOjE
  0NTAzNTkwNDMsImlhdCI6MTQ1MDM1NTQ0MywiaXNzIjoiIiwiamlkIjoiZmMzODg4NjktZWE2MC00
  YzE4LWI1NmMtM2I4YmYzOTJmMzU5IiwibmJmIjoxNDUwMzU1NDQzLCJzdWIiOiJhcHAifQ.foEvIJ
  X3hwuCJCQvIi6x31m3g1VQ0RAp6ouiiVFIs2mVM7GsD2O3aS8WxlKaxZ5P7VhbJpxTR2zg9GDSGRe
  -Acj26r1OVjY9QSoLIeMNg2VfA6AwpASmYhP8EOdlbyjFEK8hC14JXToWn-
  cT6UXE0IZxg0ANevzDSHlPnaLDemNBkxoQ1cQPIOxPOz7xZSSDZmw9rv-MNlPi6F-
  FNZOEig5iEyl5vzDgExr5438Qkmc5OzlLYz-
  RoOroFtiyoqPXp0aYEms4zaowzB4m_DrQd0cIuAKjrtlUnbvId0rOnx-
  PBtF6yWZfSC7_hmWwtfrmho-XFWfaawjZswRWTAgaMg",
  "expires_in": 3600,
  "token_type": "Bearer"
}

Отлично. Вы научились работать с авторизацией. Теперь можно переходить к политикам.

Политики

Политики - это очень мощная штука. Мы смотрели в сторону AWS и старались адоптировать их архитектуру работы с политиками, реализовать подобную логику в Hydra. Более подробную документацию по политикам можно найти в этом репозитории на GitHub.

{
    // Это должен быть уникальный ID. По этому ID
    // в базе данных будет производится поиск.
    id: "68819e5a-738b-41ec-b03c-b58a1b19d043",

    // Описание для человеков. Это не обязательное поле.
    description: "описание для людей",

    // К кому применять эту политику?
    // Обратите внимание, что тут можно использовать 
    // регулярные выражение внутри < >.
    subjects: ["max", "peter", "<zac|ken>"],

    // Эта политика разрешает или запрещает?
    effect: "allow",

    // На какой ресурс действует эта политика?
    // И тут тоже можно использовать регулярные 
    // выражения внутри < >.
    resources: ["urn:something:resource_a", "urn:something:resource_b", "urn:something:foo:<.+>"],

    // На какие права доступа влияет эта политика.
    // И тут тоже используем регулярные выражения.
    permissions: ["<create|delete>", "get"],

    // При каких условия политики начинают работать.
    conditions: [
        // В этом примере только при условия срабатывания "SubjectIsOwner
        {
            "op": "SubjectIsOwner"
        }
    ]
}

Это пример того как выглядят политики. Как видно, есть ряд различных атрибутов:

Как вы помните, тестовое клиентское приложение (app) добавлено с правами супер-пользователя, а ваш тестовый пользователь без прав супер-пользователя. Давайте посмотрим, что это значит в реальной жизни.

Проверим, может ли тестовое приложение создать ресурс "fileA.png". Прежде всего, нам нужно получить токен для нашего клиента.

curl --insecure --data "grant_type=client_credentials&username=foo@bar.
com&password=secret" --user app:secret "https://localhost:9000/oauth2/token"

{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiIiLCJleHAiOjE
  0NTAzNTkwNDMsImlhdCI6MTQ1MDM1NTQ0MywiaXNzIjoiIiwiamlkIjoiZmMzODg4NjktZWE2MC00
  YzE4LWI1NmMtM2I4YmYzOTJmMzU5IiwibmJmIjoxNDUwMzU1NDQzLCJzdWIiOiJhcHAifQ.foEvIJ
  X3hwuCJCQvIi6x31m3g1VQ0RAp6ouiiVFIs2mVM7GsD2O3aS8WxlKaxZ5P7VhbJpxTR2zg9GDSGRe
  -Acj26r1OVjY9QSoLIeMNg2VfA6AwpASmYhP8EOdlbyjFEK8hC14JXToWn-
  cT6UXE0IZxg0ANevzDSHlPnaLDemNBkxoQ1cQPIOxPOz7xZSSDZmw9rv-MNlPi6F-
  FNZOEig5iEyl5vzDgExr5438Qkmc5OzlLYz-
  RoOroFtiyoqPXp0aYEms4zaowzB4m_DrQd0cIuAKjrtlUnbvId0rOnx-
  PBtF6yWZfSC7_hmWwtfrmho-XFWfaawjZswRWTAgaMg",
  "expires_in": 3600,
  "token_type": "Bearer"
}

В Hydra нужно передать определенную информацию для получения доступа:

Для проверки, имеет ли клиент права на создание ресурса "filA.png", нужно воспользоваться curl запросом с указанием токена, полученного выше, в теле POST (–data "...token=...") с использованием учетных данных клиента (–user app:secret):

curl --insecure \
--data '{"resource": "filA.png", "permission": "create", "token": 
"<client_token_from_above>"}' \
--user app:secret \
"https://localhost:9000/guard/allowed"

Замените <...> на ваши значения. Должно получится примерно так:

curl --insecure \
--data '{"resource": "filA.png", "permission": "create", "token": 
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiIiLCJleHAiOjE0NTAzNjkzOTQsImlhd
CI6MTQ1MDM2NTc5NCwiaXNzIjoiIiwiamlkIjoiZWZkM2M2ODMtZTQ3Ny00ODQ4LThmZTYtZWU4NGI1
YzAzZTUxIiwibmJmIjoxNDUwMzY1Nzk0LCJzdWIiOiJhcHAifQ.Q4zaiLaQvbVr9Ex3Oe9Htk-zhNsY
2mtxXQgtzvnxbIbWcvF2TE_fKoVAgOGQiUiF263CNVCpKqQkMGtWcm_c1fa_2r4HYXZvOoccxHrz7fo
aSuLDfqcfKinlhLn_UvERT5jR9sYOA5Vw7ES1cq2WdrP17LXog9V40I0aZzmhqHXFdAv5vb4y5MdUKp
aJgR_PWLBE_c12nmCRrLceSgHzVAVEyxW0BkUAK4cypIH0cz-
lsSPsFZLUogQQi0oBON3FVEuXeNBxJb-Ecp3V3C5aKjrg2bs0OKeJt-
ZItrzfsQF4Gsgh2irpLfF4tMN6fNDosulNT5-HuGLJGfzJzT2RYQ"}' \
--user app:secret \
"https://localhost:9000/guard/allowed"

{"allowed": true}

Теперь мы можем проверить, имеет ли тестовый пользователь foo@bar.com возможность создать ресурс "fileA.png". Спойлер: нет, не имеет, так как он не суперпользователь и мы не добавляли никаких дополнительных настроек. Для начала, получите токен для клиента, потом токен для пользователя:

curl --insecure --data "grant_type=password&username=foo@bar.
com&password=secret" --user app:secret "https://localhost:9000/oauth2/token"

{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiIiLCJleHAiOjE
  0NTAzNzAwODAsImlhdCI6MTQ1MDM2NjQ4MCwiaXNzIjoiIiwiamlkIjoiYTdjZDFmYWQtZTg5MS00
  ZDJmLWIwZmEtMzE2Zjg3MTI5ZGIyIiwibmJmIjoxNDUwMzY2NDgwLCJzdWIiOiJiY2U5M2QzMy05Y
  jVhLTQ5MzMtOTQ3Mi1jYWRhMDE4ZGFmNjAifQ.
  dqUHiAJ0uoUYtV4hqhgVqYqA6PSy1cmNZQruyTpmRaCBh2RHzkijFj4F-
  T8xTbrFBnysTQG3LxxeXkDNq6PZBsZ4WzvUXSy1R18MayT5FWkgAi-ROQ2lHn9Isw1IgN3XWO-
  YOaQt9rO0gG4w_hRQ-DprMMKcUkNVC1zK_pdUpaB7cEurYF3sd7krPQjIhucPVhJqDjkAIZGG54kd
  28_uLqKi3eTaDrViwGLbYzmLenfTb79Hxjfd8qFd_KBQW-f1maLy0BwQNP1pVu2I_P7CBjIwEm898
  wTPye42CFUfVzyvB6ob4sAZM60YVwzxN_zaw_SO1160HbDI4oO-HwwPig",
  "expires_in": 3600,
  "refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.
  eyJpZCI6IjBlNmJmNTBlLTU1N2EtNGJiYy1iZDk1LTg3ZDJkOTNkOGQ5YSJ9.JFVgu7Tf1BZJLrMb
  gKi0wyBKXZuHB63yKbv6_UP8TUkUgH8e9S5Gi9MhlPOnU0KyiEkh8p5Z0CMN2HQeIeYj-
  0p3POFxoSkY6NPZeWKsnPXzDjlJJmXWYrqgI-N-
  BD26MmoGXLjHt_DY3hxBX_EzHHuqVk9q-2pUAfwc0BHjSidF5EZ852I5e3J0WHbiw4KnogNRKNN-l
  siIIEBSjkBxyyH85Dx4JdQZsAJVBKiXXzizWIQeQABAIutvIs5ok3T4xD8WYEiSuiHdKbPKe9bjNG
  X2OqW1X-eDts4RE0eHWatNQ-IafwMvi-7A0f5PSf26pSGPQ5TyvpA5qbnYAIXrMw",
  "token_type": "Bearer"
}

Команда, которую вам нужно выполнить, очень простая, но в этот раз нам нужно указать токен пользователя внутри JSON в теле запроса.

curl --insecure \
--data '{"resource": "filA.png", "permission": "create", "token": 
"<ACCOUNT_token_from_above>"}' \
--user app:secret \
"https://localhost:9000/guard/allowed"
curl --insecure \
--data '{"resource": "filA.png", "permission": "create", "token": 
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiIiLCJleHAiOjE0NTAzNzAwODAsImlhd
CI6MTQ1MDM2NjQ4MCwiaXNzIjoiIiwiamlkIjoiYTdjZDFmYWQtZTg5MS00ZDJmLWIwZmEtMzE2Zjg3
MTI5ZGIyIiwibmJmIjoxNDUwMzY2NDgwLCJzdWIiOiJiY2U5M2QzMy05YjVhLTQ5MzMtOTQ3Mi1jYWR
hMDE4ZGFmNjAifQ.dqUHiAJ0uoUYtV4hqhgVqYqA6PSy1cmNZQruyTpmRaCBh2RHzkijFj4F-
T8xTbrFBnysTQG3LxxeXkDNq6PZBsZ4WzvUXSy1R18MayT5FWkgAi-ROQ2lHn9Isw1IgN3XWO-
YOaQt9rO0gG4w_hRQ-DprMMKcUkNVC1zK_pdUpaB7cEurYF3sd7krPQjIhucPVhJqDjkAIZGG54kd28
_uLqKi3eTaDrViwGLbYzmLenfTb79Hxjfd8qFd_KBQW-f1maLy0BwQNP1pVu2I_P7CBjIwEm898wTPy
e42CFUfVzyvB6ob4sAZM60YVwzxN_zaw_SO1160HbDI4oO-HwwPig"}' \
--user app:secret \
"https://localhost:9000/guard/allowed"
{"allowed": false}

Ух! Тут было много копипасты команд в консоль, но у вас получилось. Вы попробовали основные возможности Hydra. Конечно, мы прошлись поверхностно только по базовым возможностям Hydra и есть еще много чего для более подробного исследования. Команда Ори надеется, что вам понравился этот туториал и вы будете рекомендовать Hydra друзьям. Пока что Hydra не так стабильна, как хотелось бы, но мы работаем над этим. Если вы нашли баг, обязательно сообщите нам об этом.

За лого я благодарен pathfinderlinden.

Jan 26, 2016
 
comments powered by Disqus